/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include "crophandler.h" #undef THREAD_PRIORITY_NORMAL #include #include "guiutils.h" #include "cropwindow.h" #include "../rtengine/dcrop.h" #include "../rtengine/refreshmap.h" using namespace rtengine; CropHandler::CropHandler () : zoom(10), ww(0), wh(0), cx(0), cy(0), cw(0), ch(0), cropX(0), cropY(0), cropW(0), cropH(0), enabled(false), cropimg(NULL), cropimgtrue(NULL), ipc(NULL), crop(NULL), listener(NULL), isLowUpdatePriority(false) { chi = new CropHandlerIdleHelper; chi->destroyed = false; chi->pending = 0; chi->cropHandler = this; } CropHandler::~CropHandler () { if (ipc) ipc->delSizeListener (this); setEnabled (false); if (crop) { //crop->destroy (); delete crop; // will do the same than destroy, plus delete the object crop = NULL; } cimg.lock (); if (chi->pending) chi->destroyed = true; else delete chi; cimg.unlock (); } void CropHandler::setEditSubscriber (EditSubscriber* newSubscriber) { (static_cast(crop))->setEditSubscriber(newSubscriber); } void CropHandler::newImage (StagedImageProcessor* ipc_, bool isDetailWindow) { ipc = ipc_; cx = 0; cy = 0; if (!ipc) return; EditDataProvider *editDataProvider = NULL; CropWindow *cropWin = listener ? static_cast(listener) : NULL; if (cropWin) editDataProvider = cropWin->getImageArea(); crop = ipc->createCrop (editDataProvider, isDetailWindow); ipc->setSizeListener (this); crop->setListener (enabled ? this : NULL); initial = true; } void CropHandler::sizeChanged (int x, int y, int ow, int oh) { // the ipc notifies it to keep track size changes like rotation compDim (); // this should be put into an idle source!!! /* if (listener) listener->cropWindowChanged (); */ } double CropHandler::getFitCropZoom () { double z1 = (double) wh / cropParams.h; double z2 = (double) ww / cropParams.w; return z1getFullHeight (); double z2 = (double) ww / ipc->getFullWidth (); return z1=0) x = centerx; if (centery>=0) y = centery; // maybe demosaic etc. if we cross the border to >100% bool needsFullRefresh = (z>=1000 && zoom<1000); zoom = z; if (zoom>=1000) { cw = ww * 1000 / zoom; ch = wh * 1000 / zoom; } else { cw = ww * zoom; ch = wh * zoom; } cx = x - cw / 2; cy = y - ch / 2; compDim (); if (enabled) { if (needsFullRefresh) ipc->startProcessing(M_HIGHQUAL); else update (); } } void CropHandler::setWSize (int w, int h) { ww = w; wh = h; if (zoom>=1000) { cw = ww * 1000 / zoom; ch = wh * 1000 / zoom; } else { cw = ww * zoom; ch = wh * zoom; } compDim (); if (enabled) update (); } void CropHandler::getWSize (int& w, int &h) { w = ww; h = wh; } void CropHandler::setPosition (int x, int y, bool update_) { cx = x; cy = y; compDim (); if (enabled && update_) update (); } void CropHandler::getPosition (int& x, int& y) { x = cropX; y = cropY; } int createpixbufs (void* data) { GThreadLock lock; CropHandlerIdleHelper* chi = static_cast(data); if (chi->destroyed) { if (chi->pending == 1) delete chi; else chi->pending--; return 0; } CropHandler* ch = chi->cropHandler; ch->cimg.lock (); ch->cropPixbuf.clear (); if (!ch->enabled) { delete [] ch->cropimg; ch->cropimg = NULL; delete [] ch->cropimgtrue; ch->cropimgtrue = NULL; ch->cimg.unlock (); return 0; } if (ch->cropimg) { if (ch->cix==ch->cropX && ch->ciy==ch->cropY && ch->ciw==ch->cropW && ch->cih==ch->cropH && ch->cis==(ch->zoom>=1000?1:ch->zoom)) { // calculate final image size int czoom = ch->zoom<1000 ? 1000 : ch->zoom; int imw = ch->cropimg_width * czoom / 1000; int imh = ch->cropimg_height * czoom / 1000; if (imw>ch->ww) imw = ch->ww; if (imh>ch->wh) imh = ch->wh; Glib::RefPtr tmpPixbuf = Gdk::Pixbuf::create_from_data (ch->cropimg, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2*ch->cropimg_height, 3*ch->cropimg_width); ch->cropPixbuf = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); tmpPixbuf->scale (ch->cropPixbuf, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST); tmpPixbuf.clear (); Glib::RefPtr tmpPixbuftrue = Gdk::Pixbuf::create_from_data (ch->cropimgtrue, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2*ch->cropimg_height, 3*ch->cropimg_width); ch->cropPixbuftrue = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); tmpPixbuftrue->scale (ch->cropPixbuftrue, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST); tmpPixbuftrue.clear (); } delete [] ch->cropimg; ch->cropimg = NULL; delete [] ch->cropimgtrue; ch->cropimgtrue = NULL; } ch->cimg.unlock (); if (ch->listener) { ch->listener->cropImageUpdated (); if (ch->initial) { ch->listener->initialImageArrived (); ch->initial = false; } } chi->pending--; return 0; } void CropHandler::setDetailedCrop (IImage8* im, IImage8* imtrue, rtengine::procparams::ColorManagementParams cmp, rtengine::procparams::CropParams cp, int ax, int ay, int aw, int ah, int askip) { if (!enabled) return; cimg.lock (); cropParams = cp; colorParams = cmp; cropPixbuf.clear (); if (cropimg) delete [] cropimg; cropimg = NULL; if (cropimgtrue) delete [] cropimgtrue; cropimgtrue = NULL; if (ax==cropX && ay==cropY && aw==cropW && ah==cropH && askip==(zoom>=1000?1:zoom)) { cropimg_width = im->getWidth (); cropimg_height = im->getHeight (); cropimg = new unsigned char [3*cropimg_width*cropimg_height]; cropimgtrue = new unsigned char [3*cropimg_width*cropimg_height]; memcpy (cropimg, im->getData(), 3*cropimg_width*cropimg_height); memcpy (cropimgtrue, imtrue->getData(), 3*cropimg_width*cropimg_height); cix = ax; ciy = ay; ciw = aw; cih = ah; cis = askip; chi->pending++; g_idle_add (createpixbufs, chi); } cimg.unlock (); } bool CropHandler::getWindow (int& cwx, int& cwy, int& cww, int& cwh, int& cskip) { cwx = cropX; cwy = cropY; cww = cropW; cwh = cropH; // hack: if called before first size allocation the size will be 0 if (cww<10) cww = 10; if (cwh<32) cwh = 32; cskip = zoom>=1000 ? 1 : zoom; return true; } void CropHandler::update () { if (crop) { // crop->setWindow (cropX, cropY, cropW, cropH, zoom>=1000 ? 1 : zoom); --> we use the "getWindow" hook instead of setting the size before crop->setListener (this); cropPixbuf.clear (); // To save threads, try to mark "needUpdate" without a thread first if (crop->tryUpdate()) { if (isLowUpdatePriority) Glib::Thread::create(sigc::mem_fun(*crop, &DetailedCrop::fullUpdate), 0, false, true, Glib::THREAD_PRIORITY_LOW); else Glib::Thread::create(sigc::mem_fun(*crop, &DetailedCrop::fullUpdate), false ); } } } void CropHandler::setEnabled (bool e) { enabled = e; if (!enabled) { if (crop) crop->setListener (NULL); cimg.lock (); delete [] cropimg; cropimg = NULL; delete [] cropimgtrue; cropimgtrue = NULL; cropPixbuf.clear (); cimg.unlock (); } else update (); } bool CropHandler::getEnabled () { return enabled; } void CropHandler::getSize (int& w, int& h) { w = cropW; h = cropH; } void CropHandler::getFullImageSize (int& w, int& h) { if (ipc) { w = ipc->getFullWidth (); h = ipc->getFullHeight (); } else { w=h=0; } } void CropHandler::compDim () { cropX = cx; cropY = cy; cropW = cw; cropH = ch; cutRectToImgBounds (cropX, cropY, cropW, cropH); } void CropHandler::cutRectToImgBounds (int& x, int& y, int& w, int& h) { if (ipc) { if (w > ipc->getFullWidth()) w = ipc->getFullWidth(); if (h > ipc->getFullHeight()) h = ipc->getFullHeight(); if (x < 0) x = 0; if (y < 0) y = 0; if (x + w >= ipc->getFullWidth()) x = ipc->getFullWidth() - w; if (y + h >= ipc->getFullHeight()) y = ipc->getFullHeight() - h; } }