diff options
Diffstat (limited to 'src/utilities/imageeditor/canvas')
19 files changed, 4945 insertions, 0 deletions
diff --git a/src/utilities/imageeditor/canvas/Makefile.am b/src/utilities/imageeditor/canvas/Makefile.am new file mode 100644 index 00000000..740f854e --- /dev/null +++ b/src/utilities/imageeditor/canvas/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgcanvas.la + +libdimgcanvas_la_SOURCES = dimginterface.cpp colorcorrectiondlg.cpp \ + canvas.cpp undocache.cpp \ + undoaction.cpp undomanager.cpp \ + imagepluginloader.cpp imageplugin.cpp + +libdimgcanvas_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TIFF) + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/utilities/splashscreen \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/rawimport \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/widgets/common \ + $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) + +digikaminclude_HEADERS = imageplugin.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/utilities/imageeditor/canvas/canvas.cpp b/src/utilities/imageeditor/canvas/canvas.cpp new file mode 100644 index 00000000..89758c3d --- /dev/null +++ b/src/utilities/imageeditor/canvas/canvas.cpp @@ -0,0 +1,1421 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-09 + * Description : image editor canvas management class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdio> +#include <cmath> + +// TQt includes. + +#include <tqtooltip.h> +#include <tqfile.h> +#include <tqstring.h> +#include <tqevent.h> +#include <tqpoint.h> +#include <tqpainter.h> +#include <tqpen.h> +#include <tqpixmap.h> +#include <tqstyle.h> +#include <tqapplication.h> +#include <tqcursor.h> +#include <tqimage.h> +#include <tqregion.h> +#include <tqtimer.h> +#include <tqcache.h> +#include <tqcolor.h> +#include <tqdragobject.h> +#include <tqclipboard.h> +#include <tqtoolbutton.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <kdatetbl.h> +#include <tdeglobalsettings.h> + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagepaniconwidget.h" +#include "dimginterface.h" +#include "iccsettingscontainer.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "loadingcacheinterface.h" +#include "canvas.h" +#include "canvas.moc" + +namespace Digikam +{ + +class CanvasPrivate +{ + +public: + + CanvasPrivate() : + tileSize(128), minZoom(0.1), maxZoom(12.0), zoomMultiplier(1.2) + { + rubber = 0; + pressedMoved = false; + pressedMoving = false; + ltActive = false; + rtActive = false; + lbActive = false; + rbActive = false; + midButtonPressed = false; + midButtonX = 0; + midButtonY = 0; + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + parent = 0; + im = 0; + rubber = 0; + autoZoom = false; + fullScreen = false; + zoom = 1.0; + tileTmpPix = new TQPixmap(tileSize, tileSize); + + tileCache.setMaxCost((10*1024*1024)/(tileSize*tileSize*4)); + tileCache.setAutoDelete(true); + } + + bool autoZoom; + bool fullScreen; + bool pressedMoved; + bool pressedMoving; + bool ltActive; + bool rtActive; + bool lbActive; + bool rbActive; + bool midButtonPressed; + + const int tileSize; + int midButtonX; + int midButtonY; + + double zoom; + const double minZoom; + const double maxZoom; + const double zoomMultiplier; + + TQToolButton *cornerButton; + + TQRect *rubber; + TQRect pixmapRect; + + TQCache<TQPixmap> tileCache; + + TQPixmap* tileTmpPix; + TQPixmap qcheck; + + TQColor bgColor; + + TQWidget *parent; + + TDEPopupFrame *panIconPopup; + + DImgInterface *im; + + ImagePanIconWidget *panIconWidget; +}; + +Canvas::Canvas(TQWidget *parent) + : TQScrollView(parent) +{ + d = new CanvasPrivate; + d->im = new DImgInterface(); + d->parent = parent; + d->bgColor.setRgb(0, 0, 0); + + d->qcheck.resize(16, 16); + TQPainter p(&d->qcheck); + p.fillRect(0, 0, 8, 8, TQColor(144, 144, 144)); + p.fillRect(8, 8, 8, 8, TQColor(144, 144, 144)); + p.fillRect(0, 8, 8, 8, TQColor(100, 100, 100)); + p.fillRect(8, 0, 8, 8, TQColor(100, 100, 100)); + p.end(); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image to a region")); + setCornerWidget(d->cornerButton); + + viewport()->setBackgroundMode(TQt::NoBackground); + viewport()->setMouseTracking(false); + setFrameStyle( TQFrame::NoFrame ); + + // ------------------------------------------------------------ + + connect(this, TQ_SIGNAL(signalZoomChanged(double)), + this, TQ_SLOT(slotZoomChanged(double))); + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(d->im, TQ_SIGNAL(signalModified()), + this, TQ_SLOT(slotModified())); + + connect(d->im, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool)), + this, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingStarted(const TQString&)), + this, TQ_SIGNAL(signalLoadingStarted(const TQString&))); + + connect(d->im, TQ_SIGNAL(signalImageLoaded(const TQString&, bool)), + this, TQ_SLOT(slotImageLoaded(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalImageSaved(const TQString&, bool)), + this, TQ_SLOT(slotImageSaved(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalLoadingProgress(const TQString&, float))); + + connect(d->im, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalSavingProgress(const TQString&, float))); + + connect(this, TQ_SIGNAL(signalSelected(bool)), + this, TQ_SLOT(slotSelected())); +} + +Canvas::~Canvas() +{ + delete d->tileTmpPix; + delete d->im; + + if (d->rubber) + delete d->rubber; + + delete d; +} + +void Canvas::resetImage() +{ + reset(); + viewport()->setUpdatesEnabled(false); + d->im->resetImage(); +} + +void Canvas::reset() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + if (d->im->imageValid()) + emit signalSelected(false); + } + + d->tileCache.clear(); +} + +void Canvas::load(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + reset(); + + viewport()->setUpdatesEnabled(false); + + d->im->load( filename, IOFileSettings, d->parent ); + + emit signalPrepareToLoad(); +} + +void Canvas::slotImageLoaded(const TQString& filePath, bool success) +{ + d->zoom = 1.0; + d->im->zoom(d->zoom); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(true); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + + emit signalLoadingFinished(filePath, success); +} + +void Canvas::preload(const TQString& /*filename*/) +{ +// d->im->preload(filename); +} + +/* +These code part are unused and untested +void Canvas::save(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + d->im->save(filename, IOFileSettings); + emit signalSavingStarted(filename); +} + +void Canvas::saveAs(const TQString& filename,IOFileSettingsContainer *IOFileSettings, + const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, mimeType); + emit signalSavingStarted(filename); +} +*/ + +void Canvas::saveAs(const TQString& filename, IOFileSettingsContainer *IOFileSettings, + bool setExifOrientationTag, const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, setExifOrientationTag, mimeType); + emit signalSavingStarted(filename); +} + +void Canvas::slotImageSaved(const TQString& filePath, bool success) +{ + emit signalSavingFinished(filePath, success); +} + +void Canvas::switchToLastSaved(const TQString& newFilename) +{ + d->im->switchToLastSaved(newFilename); +} + +void Canvas::abortSaving() +{ + d->im->abortSaving(); +} + +void Canvas::setModified() +{ + d->im->setModified(); +} + +void Canvas::readMetadataFromFile(const TQString &file) +{ + d->im->readMetadataFromFile(file); +} + +void Canvas::clearUndoHistory() +{ + d->im->clearUndoManager(); +} + +void Canvas::setUndoHistoryOrigin() +{ + d->im->setUndoManagerOrigin(); +} + +void Canvas::updateUndoState() +{ + d->im->updateUndoState(); +} + +DImg Canvas::currentImage() +{ + return DImg(*d->im->getImg()); +} + +TQString Canvas::currentImageFileFormat() +{ + return d->im->getImageFormat(); +} + +TQString Canvas::currentImageFilePath() +{ + return d->im->getImageFilePath(); +} + +int Canvas::imageWidth() +{ + return d->im->origWidth(); +} + +int Canvas::imageHeight() +{ + return d->im->origHeight(); +} + +bool Canvas::isReadOnly() +{ + return d->im->isReadOnly(); +} + +TQRect Canvas::getSelectedArea() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + return ( TQRect(x, y, w, h) ); +} + +DImgInterface *Canvas::interface() const +{ + return d->im; +} + +void Canvas::makeDefaultEditingCanvas() +{ + DImgInterface::setDefaultInterface(d->im); +} + +double Canvas::calcAutoZoomFactor() +{ + if (!d->im->imageValid()) return d->zoom; + + double srcWidth = d->im->origWidth(); + double srcHeight = d->im->origHeight(); + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + return TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); +} + +void Canvas::updateAutoZoom() +{ + d->zoom = calcAutoZoomFactor(); + d->im->zoom(d->zoom); + emit signalZoomChanged(d->zoom); +} + +void Canvas::updateContentsSize(bool deleteRubber) +{ + viewport()->setUpdatesEnabled(false); + + if (deleteRubber && d->rubber) + { + delete d->rubber; + d->rubber = 0; + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + d->pressedMoved = false; + viewport()->unsetCursor(); + viewport()->setMouseTracking(false); + if (d->im->imageValid()) + emit signalSelected(false); + } + + int wZ = d->im->width(); + int hZ = d->im->height(); + + if (visibleWidth() > wZ || visibleHeight() > hZ) + { + // Center the image + int centerx = contentsRect().width()/2; + int centery = contentsRect().height()/2; + int xoffset = int(centerx - wZ/2); + int yoffset = int(centery - hZ/2); + xoffset = TQMAX(xoffset, 0); + yoffset = TQMAX(yoffset, 0); + + d->pixmapRect = TQRect(xoffset, yoffset, wZ, hZ); + } + else + { + d->pixmapRect = TQRect(0, 0, wZ, hZ); + } + + if (!deleteRubber && d->rubber) + { + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + xSel = (int)((xSel * d->tileSize) / floor(d->tileSize / d->zoom)); + ySel = (int)((ySel * d->tileSize) / floor(d->tileSize / d->zoom)); + wSel = (int)((wSel * d->tileSize) / floor(d->tileSize / d->zoom)); + hSel = (int)((hSel * d->tileSize) / floor(d->tileSize / d->zoom)); + d->rubber->setX(xSel); + d->rubber->setY(ySel); + d->rubber->setWidth(wSel); + d->rubber->setHeight(hSel); + d->rubber->moveBy(d->pixmapRect.x(), d->pixmapRect.y()); + } + + d->tileCache.clear(); + resizeContents(wZ, hZ); + viewport()->setUpdatesEnabled(true); +} + +void Canvas::resizeEvent(TQResizeEvent* e) +{ + if (!e) + return; + + TQScrollView::resizeEvent(e); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(false); + + // No need to repaint. its called + // automatically after resize + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with resize event. + slotZoomChanged(d->zoom); +} + +void Canvas::viewportPaintEvent(TQPaintEvent *e) +{ + TQRect er(e->rect()); + er = TQRect(TQMAX(er.x() - 1, 0), + TQMAX(er.y() - 1, 0), + TQMIN(er.width() + 2, contentsRect().width()), + TQMIN(er.height() + 2, contentsRect().height())); + + paintViewport(er, (d->zoom <= 1.0) ? true : false); +} + +void Canvas::paintViewport(const TQRect& er, bool antialias) +{ + TQRect o_cr(viewportToContents(er.topLeft()), viewportToContents(er.bottomRight())); + TQRect cr = o_cr; + + TQRegion clipRegion(er); + cr = d->pixmapRect.intersect(cr); + + if (!cr.isEmpty() && d->im->imageValid()) + { + clipRegion -= TQRect(contentsToViewport(cr.topLeft()), cr.size()); + + TQRect pr = TQRect(cr.x() - d->pixmapRect.x(), cr.y() - d->pixmapRect.y(), + cr.width(), cr.height()); + + int x1 = (int)floor((double)pr.x() / (double)d->tileSize) * d->tileSize; + int y1 = (int)floor((double)pr.y() / (double)d->tileSize) * d->tileSize; + int x2 = (int)ceilf((double)pr.right() / (double)d->tileSize) * d->tileSize; + int y2 = (int)ceilf((double)pr.bottom() / (double)d->tileSize) * d->tileSize; + + TQPixmap pix(d->tileSize, d->tileSize); + int sx, sy, sw, sh; + int step = (int)floor(d->tileSize / d->zoom); + + bool hasRubber = (d->rubber && d->pressedMoved && d->pressedMoving && d->rubber->intersects(pr)); + if (hasRubber) + { + // remove rubber + drawRubber(); + } + + for (int j = y1 ; j < y2 ; j += d->tileSize) + { + for (int i = x1 ; i < x2 ; i += d->tileSize) + { + TQString key = TQString("%1,%2").arg(i).arg(j); + TQPixmap *pix = d->tileCache.find(key); + + if (!pix) + { + if (antialias) + { + pix = new TQPixmap(d->tileSize, d->tileSize); + d->tileCache.insert(key, pix); + } + else + { + pix = d->tileTmpPix; + } + + if (d->im->hasAlpha()) + { + TQPainter p(pix); + p.drawTiledPixmap(0, 0, d->tileSize, d->tileSize, + d->qcheck, 0, 0); + p.end(); + } + else + { + pix->fill(d->bgColor); + } + + // NOTE : with implementations <= 0.9.1, the canvas doesn't work properly using high zoom level (> 500). + // The sx, sy, sw, sh values haven't be computed properly and "tile" artefacts been appears + // over the image. Look the example here: + // http://digikam3rdparty.free.fr/Screenshots/editorhighzoomartefact.png + // Note than these "tile" artifacts are not the real tiles of canvas. + // The new implementation below fix this problem to handle properly the areas to + // use from the source image to generate the canvas pixmap tiles. + + sx = (int)floor((double)i / d->tileSize) * step; + sy = (int)floor((double)j / d->tileSize) * step; + sw = step; + sh = step; + + if (d->rubber && d->pressedMoved && !d->pressedMoving) + { + TQRect rr(d->rubber->normalize()); + TQRect r(i, j, d->tileSize, d->tileSize); + + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + rr.x() - i - d->pixmapRect.x(), + rr.y() - j - d->pixmapRect.y(), + rr.width(), rr.height(), + antialias); + + rr.moveBy(-i -d->pixmapRect.x(), -j -d->pixmapRect.y()); + + TQPainter p(pix); + p.setPen(TQPen(TQColor(250, 250, 255), 1)); + p.drawRect(rr); + if (rr.width() >= 10 && rr.height() >= 10) + { + p.drawRect(rr.x(), rr.y(), 5, 5); + p.drawRect(rr.x(), rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y(), 5, 5); + } + p.end(); + } + else + { + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + antialias); + } + } + + TQRect r(i, j, d->tileSize, d->tileSize); + TQRect ir = pr.intersect(r); + TQPoint pt(contentsToViewport(TQPoint(ir.x() + d->pixmapRect.x(), + ir.y() + d->pixmapRect.y()))); + + bitBlt(viewport(), pt.x(), pt.y(), + pix, + ir.x()-r.x(), ir.y()-r.y(), + ir.width(), ir.height()); + } + } + + if (hasRubber) + { + // restore rubber + drawRubber(); + } + } + + TQPainter painter(viewport()); + painter.setClipRegion(clipRegion); + painter.fillRect(er, d->bgColor); + painter.end(); +} + +void Canvas::drawRubber() +{ + if (!d->rubber || !d->im->imageValid()) + return; + + TQPainter p(viewport()); + p.setRasterOp(TQt::NotROP ); + p.setPen(TQPen(TQt::color0, 1)); + p.setBrush(NoBrush); + + TQRect r(d->rubber->normalize()); + r = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), r.size()); + + TQPoint pnt(r.x(), r.y()); + + style().drawPrimitive(TQStyle::PE_FocusRect, &p, + TQRect(pnt.x(), pnt.y(), r.width(), r.height()), + colorGroup(), TQStyle::Style_Default, + TQStyleOption(colorGroup().base())); + p.end(); +} + +void Canvas::contentsMousePressEvent(TQMouseEvent *e) +{ + if (!e || e->button() == TQt::RightButton) + return; + + d->midButtonPressed = false; + + if (e->button() == TQt::LeftButton) + { + if (d->ltActive || d->rtActive || + d->lbActive || d->rbActive) + { + Q_ASSERT( d->rubber ); + if (!d->rubber) + return; + + // Set diagonally opposite corner as anchor + + TQRect r(d->rubber->normalize()); + + if (d->ltActive) + { + d->rubber->setTopLeft(r.bottomRight()); + d->rubber->setBottomRight(r.topLeft()); + } + else if (d->rtActive) + { + d->rubber->setTopLeft(r.bottomLeft()); + d->rubber->setBottomRight(r.topRight()); + } + else if (d->lbActive) + { + d->rubber->setTopLeft(r.topRight()); + d->rubber->setBottomRight(r.bottomLeft()); + } + else if (d->rbActive) + { + d->rubber->setTopLeft(r.topLeft()); + d->rubber->setBottomRight(r.bottomLeft()); + } + + viewport()->setMouseTracking(false); + d->pressedMoved = false; + d->pressedMoving = true; + + d->tileCache.clear(); + viewport()->repaint(false); + + return; + } + } + else if (e->button() == TQt::MidButton) + { + if (visibleWidth() < d->im->width() || + visibleHeight() < d->im->height()) + { + viewport()->setCursor(TQt::SizeAllCursor); + d->midButtonPressed = true; + d->midButtonX = e->x(); + d->midButtonY = e->y(); + } + return; + } + + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(e->x(), e->y(), 0, 0); + + if (d->pressedMoved) + { + d->tileCache.clear(); + viewport()->update(); + } + + d->pressedMoved = false; + d->pressedMoving = true; + + viewport()->setMouseTracking(false); +} + +void Canvas::contentsMouseMoveEvent(TQMouseEvent *e) +{ + if (!e) + return; + + if (e->state() & TQt::MidButton) + { + if (d->midButtonPressed) + { + scrollBy(d->midButtonX - e->x(), + d->midButtonY - e->y()); + } + } + else if (!viewport()->hasMouseTracking()) + { + if (!d->rubber) + return; + + if (e->state() != TQt::LeftButton && + !(d->ltActive || d->rtActive || + d->lbActive || d->rbActive)) + return; + + // Clear old rubber. + if (d->pressedMoved) + drawRubber(); + + // Move content if necessary. + blockSignals(true); + setUpdatesEnabled(false); + ensureVisible(e->x(), e->y(), 10, 10); + setUpdatesEnabled(true); + blockSignals(false); + + // draw the new rubber position. + int r, b; + r = (e->x() > d->pixmapRect.left()) ? e->x() : d->pixmapRect.left(); + r = (r < d->pixmapRect.right()) ? r : d->pixmapRect.right(); + b = (e->y() > d->pixmapRect.top()) ? e->y() : d->pixmapRect.top(); + b = (b < d->pixmapRect.bottom()) ? b : d->pixmapRect.bottom(); + d->rubber->setRight(r); + d->rubber->setBottom(b); + drawRubber(); + + d->pressedMoved = true; + d->pressedMoving = true; + + // To refresh editor status bar with current selection. + emit signalSelectionChanged(calcSeletedArea()); + } + else + { + if (!d->rubber) + return; + + TQRect r(d->rubber->normalize()); + + TQRect lt(r.x()-5, r.y()-5, 10, 10); + TQRect rt(r.x()+r.width()-5, r.y()-5, 10, 10); + TQRect lb(r.x()-5, r.y()+r.height()-5, 10, 10); + TQRect rb(r.x()+r.width()-5, r.y()+r.height()-5, 10, 10); + + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + + if (lt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->ltActive = true; + } + else if (rb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->rbActive = true; + } + else if (lb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->lbActive = true; + } + else if (rt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->rtActive = true; + } + else + viewport()->unsetCursor(); + } +} + +void Canvas::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + if (!e) + return; + + d->midButtonPressed = false; + + if (d->pressedMoving) + { + d->pressedMoving = false; + viewport()->update(); + } + + if (d->pressedMoved && d->rubber) + { + // Normalize rubber rectangle to always have the selection into the image + TQRect rec = d->rubber->normalize(); + + if (rec.left() < d->pixmapRect.left()) rec.setLeft(d->pixmapRect.left()); + if (rec.right() > d->pixmapRect.right()) rec.setRight(d->pixmapRect.right()); + if (rec.top() < d->pixmapRect.top()) rec.setTop(d->pixmapRect.top()); + if (rec.bottom() > d->pixmapRect.bottom()) rec.setBottom(d->pixmapRect.bottom()); + + d->rubber->setLeft(rec.left()); + d->rubber->setRight(rec.right()); + d->rubber->setTop(rec.top()); + d->rubber->setBottom(rec.bottom()); + + d->tileCache.clear(); + viewport()->setMouseTracking(true); + if (d->im->imageValid()) + emit signalSelected(true); + } + else + { + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + viewport()->setMouseTracking(false); + viewport()->unsetCursor(); + if (d->im->imageValid()) + emit signalSelected(false); + } + + if (e->button() != TQt::LeftButton) + { + viewport()->unsetCursor(); + } + + if (e->button() == TQt::RightButton) + { + emit signalRightButtonClicked(); + } +} + +void Canvas::contentsWheelEvent(TQWheelEvent *e) +{ + e->accept(); + + if (e->state() & TQt::ShiftButton) + { + if (e->delta() < 0) + emit signalShowNextImage(); + else if (e->delta() > 0) + emit signalShowPrevImage(); + return; + } + else if (e->state() & TQt::ControlButton) + { + if (e->delta() < 0) + slotDecreaseZoom(); + else if (e->delta() > 0) + slotIncreaseZoom(); + return; + } + + TQScrollView::contentsWheelEvent(e); +} + +bool Canvas::maxZoom() +{ + return ((d->zoom * d->zoomMultiplier) >= d->maxZoom); +} + +bool Canvas::minZoom() +{ + return ((d->zoom / d->zoomMultiplier) <= d->minZoom); +} + +bool Canvas::exifRotated() +{ + return d->im->exifRotated(); +} + +double Canvas::snapZoom(double zoom) +{ + // If the zoom value gets changed from d->zoom to zoom + // across 50%, 100% or fit-to-window, then return the + // the corresponding special value. Otherwise zoom is returned unchanged. + double fit = calcAutoZoomFactor(); + TQValueList<double> snapValues; + snapValues.append(0.5); + snapValues.append(1.0); + snapValues.append(fit); + + qHeapSort(snapValues); + TQValueList<double>::const_iterator it; + + if (d->zoom < zoom) + { + for(it = snapValues.constBegin(); it != snapValues.constEnd(); ++it) + { + double z = *it; + if ((d->zoom < z) && (zoom > z)) + { + zoom = z; + break; + } + } + } + else + { + // We need to go through the list in reverse order, + // however, tqCopyBackward does not seem to work here, so + // a simple for loop over integers is used instead. + for(int i=snapValues.size()-1; i>=0; i--) + { + double z = snapValues[i]; + if ((d->zoom > z) && (zoom < z)) + { + zoom = z; + break; + } + } + } + + return zoom; +} + +void Canvas::slotIncreaseZoom() +{ + if (maxZoom()) + return; + + double zoom = d->zoom * d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::slotDecreaseZoom() +{ + if (minZoom()) + return; + + double zoom = d->zoom / d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::setZoomFactorSnapped(double zoom) +{ + double fit = calcAutoZoomFactor(); + + if (fabs(zoom-fit) < 0.05) + { + // If 1.0 or 0.5 are even closer to zoom than fit, then choose these. + if (fabs(zoom-fit) > fabs(zoom-1.0) ) + { + zoom = 1.0; + } + else if (fabs(zoom-fit) > fabs(zoom-0.5) ) + { + zoom = 0.5; + } + else + { + zoom = fit; + } + } + else + { + if (fabs(zoom-1.0) < 0.05) + { + zoom = 1.0; + } + if (fabs(zoom-0.5) < 0.05) + { + zoom = 0.5; + } + } + setZoomFactor(zoom); +} + +double Canvas::zoomFactor() +{ + return d->zoom; +} + +void Canvas::setZoomFactor(double zoom) +{ + if (d->autoZoom) + { + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + } + + // Zoom using center of canvas and given zoom factor. + + double cpx = contentsX() + visibleWidth() / 2.0; + double cpy = contentsY() + visibleHeight() / 2.0; + + cpx = (cpx / d->tileSize) * floor(d->tileSize / d->zoom); + cpy = (cpy / d->tileSize) * floor(d->tileSize / d->zoom); + + d->zoom = zoom; + + d->im->zoom(d->zoom); + updateContentsSize(false); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); +} + +void Canvas::fitToSelect() +{ + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + + if (wSel && hSel ) + { + // If selected area, use center of selection + // and recompute zoom factor accordinly. + double cpx = xSel + wSel / 2.0; + double cpy = ySel + hSel / 2.0; + + double srcWidth = wSel; + double srcHeight = hSel; + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + + d->zoom = TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); + + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + d->im->zoom(d->zoom); + updateContentsSize(true); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + } +} + +bool Canvas::fitToWindow() +{ + return d->autoZoom; +} + +void Canvas::toggleFitToWindow() +{ + d->autoZoom = !d->autoZoom; + + if (d->autoZoom) + updateAutoZoom(); + else + { + d->zoom = 1.0; + emit signalZoomChanged(d->zoom); + } + + d->im->zoom(d->zoom); + updateContentsSize(false); + slotZoomChanged(d->zoom); + viewport()->update(); +} + +void Canvas::slotRotate90() +{ + d->im->rotate90(); +} + +void Canvas::slotRotate180() +{ + d->im->rotate180(); +} + +void Canvas::slotRotate270() +{ + d->im->rotate270(); +} + +void Canvas::slotFlipHoriz() +{ + d->im->flipHoriz(); +} + +void Canvas::slotFlipVert() +{ + d->im->flipVert(); +} + +void Canvas::slotCrop() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + d->im->crop(x, y, w, h); +} + +void Canvas::resizeImage(int w, int h) +{ + d->im->resize(w, h); +} + +void Canvas::setBackgroundColor(const TQColor& color) +{ + if (d->bgColor == color) + return; + + d->bgColor = color; + viewport()->update(); +} + +void Canvas::setICCSettings(ICCSettingsContainer *cmSettings) +{ + d->im->setICCSettings(cmSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExposureSettings(ExposureSettingsContainer *expoSettings) +{ + d->im->setExposureSettings(expoSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExifOrient(bool exifOrient) +{ + d->im->setExifOrient(exifOrient); + viewport()->update(); +} + +void Canvas::increaseGamma() +{ + d->im->changeGamma(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseGamma() +{ + d->im->changeGamma(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseBrightness() +{ + d->im->changeBrightness(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseBrightness() +{ + d->im->changeBrightness(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseContrast() +{ + d->im->changeContrast(5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseContrast() +{ + d->im->changeContrast(-5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::slotRestore() +{ + d->im->restore(); +} + +void Canvas::slotUndo(int steps) +{ + while(steps > 0) + { + d->im->undo(); + --steps; + } +} + +void Canvas::getUndoHistory(TQStringList &titles) +{ + d->im->getUndoHistory(titles); +} + +void Canvas::getRedoHistory(TQStringList &titles) +{ + d->im->getRedoHistory(titles); +} + +void Canvas::slotRedo(int steps) +{ + while(steps > 0) + { + d->im->redo(); + --steps; + } +} + +void Canvas::slotCopy() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + TQApplication::setOverrideCursor (TQt::waitCursor); + uchar* data = d->im->getImageSelection(); + DImg selDImg = DImg(w, h, d->im->sixteenBit(), d->im->hasAlpha(), data); + delete [] data; + + TQImage selImg = selDImg.copyTQImage(); + TQApplication::clipboard()->setData(new TQImageDrag(selImg), TQClipboard::Clipboard); + TQApplication::restoreOverrideCursor (); +} + +void Canvas::slotSelected() +{ + int x=0, y=0, w=0, h=0; + + if (d->rubber && d->pressedMoved) + { + TQRect sel = calcSeletedArea(); + x = sel.x(); + y = sel.y(); + w = sel.width(); + h = sel.height(); + } + + d->im->setSelectedArea(x, y, w, h); +} + +TQRect Canvas::calcSeletedArea() +{ + int x=0, y=0, w=0, h=0; + TQRect r(d->rubber->normalize()); + + if (r.isValid()) + { + r.moveBy(- d->pixmapRect.x(), - d->pixmapRect.y()); + + x = (int)(((double)r.x() / d->tileSize) * floor(d->tileSize / d->zoom)); + y = (int)(((double)r.y() / d->tileSize) * floor(d->tileSize / d->zoom)); + w = (int)(((double)r.width() / d->tileSize) * floor(d->tileSize / d->zoom)); + h = (int)(((double)r.height() / d->tileSize) * floor(d->tileSize / d->zoom)); + + x = TQMIN(imageWidth(), TQMAX(x, 0)); + y = TQMIN(imageHeight(), TQMAX(y, 0)); + w = TQMIN(imageWidth(), TQMAX(w, 0)); + h = TQMIN(imageHeight(), TQMAX(h, 0)); + + // Avoid empty selection by rubberband - at least mark one pixel + // At high zoom factors, the rubberband may operate at subpixel level! + if (w == 0) + w = 1; + if (h == 0) + h = 1; + } + + return TQRect(x, y, w, h); +} + +void Canvas::slotModified() +{ + if (d->autoZoom) + updateAutoZoom(); + d->im->zoom(d->zoom); + + updateContentsSize(true); + viewport()->update(); + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with new image size (if changed). + slotZoomChanged(d->zoom); + + emit signalChanged(); +} + +void Canvas::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + ImagePanIconWidget *pan = new ImagePanIconWidget(180, 120, d->panIconPopup); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / d->zoom), (int)(contentsY() / d->zoom), + (int)(visibleWidth() / d->zoom), (int)(visibleHeight() / d->zoom)); + pan->setRegionSelection(r); + pan->setMouseFocus(); + + connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), + this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); + + connect(pan, TQ_SIGNAL(signalHiden()), + this, TQ_SLOT(slotPanIconHiden())); + + TQPoint g = mapToGlobal(viewport()->pos()); + g.setX(g.x()+ viewport()->size().width()); + g.setY(g.y()+ viewport()->size().height()); + d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(), + g.y() - d->panIconPopup->height())); + + pan->setCursorToLocalRegionSelectionCenter(); +} + +void Canvas::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void Canvas::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*d->zoom), (int)(r.y()*d->zoom)); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void Canvas::slotZoomChanged(double /*zoom*/) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); +} + +void Canvas::slotSelectAll() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(d->pixmapRect); + d->pressedMoved = true; + d->tileCache.clear(); + viewport()->setMouseTracking(true); + viewport()->update(); + + if (d->im->imageValid()) + emit signalSelected(true); +} + +void Canvas::slotSelectNone() +{ + reset(); + viewport()->update(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/canvas.h b/src/utilities/imageeditor/canvas/canvas.h new file mode 100644 index 00000000..c772098d --- /dev/null +++ b/src/utilities/imageeditor/canvas/canvas.h @@ -0,0 +1,209 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-09 + * Description : image editor canvas management class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef CANVAS_H +#define CANVAS_H + +// TQt includes. + +#include <tqscrollview.h> +#include <tqrect.h> + +// Local includes. + +#include "digikam_export.h" +#include "dimg.h" + +class TQString; +class TQStringList; +class TQPixmap; +class TQPaintEvent; +class TQResizeEvent; +class TQWheelEvent; +class TQKeyEvent; +class TQColor; + +namespace Digikam +{ + +class CanvasPrivate; +class DImgInterface; +class ExposureSettingsContainer; +class ICCSettingsContainer; +class IOFileSettingsContainer; + +class DIGIKAM_EXPORT Canvas : public TQScrollView +{ + TQ_OBJECT + + +public: + + Canvas(TQWidget *parent=0); + ~Canvas(); + + void load(const TQString& filename, IOFileSettingsContainer *IOFileSettings); + void preload(const TQString& filename); + + void saveAs(const TQString& filename, IOFileSettingsContainer *IOFileSettings, + bool setExifOrientationTag, const TQString& mimeType=TQString()); + void resetImage(); + void switchToLastSaved(const TQString& newFilename); + void abortSaving(); + void setModified(); + void readMetadataFromFile(const TQString &file); + void clearUndoHistory(); + void setUndoHistoryOrigin(); + void updateUndoState(); + DImg currentImage(); + TQString currentImageFileFormat(); + TQString currentImageFilePath(); + + DImgInterface *interface() const; + void makeDefaultEditingCanvas(); + + double snapZoom(double z); + void setZoomFactorSnapped(double zoom); + + double zoomFactor(); + void setZoomFactor(double z); + bool fitToWindow(); + bool maxZoom(); + bool minZoom(); + bool exifRotated(); + int imageWidth(); + int imageHeight(); + TQRect getSelectedArea(); + + // If current image file format is only available in read only, + // typicially all RAW image file formats. + bool isReadOnly(); + + void resizeImage(int w, int h); + + void setBackgroundColor(const TQColor& color); + void setICCSettings(ICCSettingsContainer *cmSettings); + void setExposureSettings(ExposureSettingsContainer *expoSettings); + + void setExifOrient(bool exifOrient); + + void increaseGamma(); + void decreaseGamma(); + void increaseBrightness(); + void decreaseBrightness(); + void increaseContrast(); + void decreaseContrast(); + + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + + void toggleFitToWindow(); + void fitToSelect(); + +signals: + + void signalZoomChanged(double zoom); + void signalMaxZoom(); + void signalMinZoom(); + void signalChanged(); + void signalUndoStateChanged(bool, bool, bool); + void signalSelected(bool); + void signalRightButtonClicked(); + void signalShowNextImage(); + void signalShowPrevImage(); + void signalPrepareToLoad(); + void signalLoadingStarted(const TQString &filename); + void signalLoadingFinished(const TQString &filename, bool success); + void signalLoadingProgress(const TQString& filePath, float progress); + void signalSavingStarted(const TQString &filename); + void signalSavingFinished(const TQString &filename, bool success); + void signalSavingProgress(const TQString& filePath, float progress); + void signalSelectionChanged(const TQRect&); + void signalToggleOffFitToWindow(); + +public slots: + + void slotIncreaseZoom(); + void slotDecreaseZoom(); + + // image modifiers + void slotRotate90(); + void slotRotate180(); + void slotRotate270(); + + void slotFlipHoriz(); + void slotFlipVert(); + + void slotCrop(); + + void slotRestore(); + void slotUndo(int steps=1); + void slotRedo(int steps=1); + + void slotCopy(); + + void slotSelectAll(); + void slotSelectNone(); + +protected: + + void resizeEvent(TQResizeEvent* e); + void viewportPaintEvent(TQPaintEvent *e); + void contentsMousePressEvent(TQMouseEvent *e); + void contentsMouseMoveEvent(TQMouseEvent *e); + void contentsMouseReleaseEvent(TQMouseEvent *e); + void contentsWheelEvent(TQWheelEvent *e); + +private: + + TQRect calcSeletedArea(); + double calcAutoZoomFactor(); + void updateAutoZoom(); + void updateContentsSize(bool deleteRubber); + + void drawRubber(); + void paintViewport(const TQRect& er, bool antialias); + + void reset(); + +private slots: + + void slotSelected(); + void slotModified(); + void slotImageLoaded(const TQString& filePath, bool success); + void slotImageSaved(const TQString& filePath, bool success); + void slotCornerButtonPressed(); + void slotZoomChanged(double); + void slotPanIconSelectionMoved(const TQRect&, bool); + void slotPanIconHiden(); + +private: + + CanvasPrivate *d; +}; + +} // namespace Digikam + +#endif /* CANVAS_H */ + diff --git a/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp b/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp new file mode 100644 index 00000000..1403773f --- /dev/null +++ b/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp @@ -0,0 +1,186 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-15 + * Description : a dialog to see preview ICC color correction + * before to apply color profile. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqlayout.h> +#include <tqframe.h> +#include <tqstring.h> +#include <tqfileinfo.h> +#include <tqpushbutton.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeapplication.h> +#include <kseparator.h> + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "icctransform.h" +#include "iccprofileinfodlg.h" +#include "colorcorrectiondlg.h" +#include "colorcorrectiondlg.moc" + +namespace Digikam +{ + +ColorCorrectionDlg::ColorCorrectionDlg(TQWidget* parent, DImg *preview, + IccTransform *iccTrans, const TQString& file) + : KDialogBase(parent, "", true, TQString(), + Help|Ok|Apply|Cancel, Ok, true) +{ + m_iccTrans = iccTrans; + m_parent = parent; + setHelp("iccprofile.anchor", "digikam"); + setButtonText(Ok, i18n("Convert")); + setButtonTip(Ok, i18n("Apply the default color workspace profile to the image")); + setButtonText(Cancel, i18n("Do Nothing")); + setButtonTip(Cancel, i18n("Do not change the image")); + setButtonText(Apply, i18n("Assign")); + setButtonTip(Apply, i18n("Only embed the color workspace profile in the image, don't change the image")); + + TQFileInfo fi(file); + setCaption(fi.fileName()); + + TQWidget *page = new TQWidget(this); + TQGridLayout* grid = new TQGridLayout(page, 3, 2, 0, KDialog::spacingHint()); + + TQLabel *originalTitle = new TQLabel(i18n("Original Image:"), page); + TQLabel *previewOriginal = new TQLabel(page); + TQLabel *targetTitle = new TQLabel(i18n("Corrected Image:"), page); + TQLabel *previewTarget = new TQLabel(page); + TQLabel *logo = new TQLabel(page); + TQLabel *message = new TQLabel(page); + TQLabel *currentProfileTitle = new TQLabel(i18n("Current workspace color profile:"), page); + TQLabel *currentProfileDesc = new TQLabel(TQString("<b>%1</b>").arg(m_iccTrans->getOutpoutProfileDescriptor()), page); + TQPushButton *currentProfInfo = new TQPushButton(i18n("Info..."), page); + TQLabel *embeddedProfileTitle = new TQLabel(i18n("Embedded color profile:"), page); + TQLabel *embeddedProfileDesc = new TQLabel(TQString("<b>%1</b>").arg(m_iccTrans->getEmbeddedProfileDescriptor()), page); + TQPushButton *embeddedProfInfo = new TQPushButton(i18n("Info..."), page); + KSeparator *line = new KSeparator(TQt::Horizontal, page); + + if (m_iccTrans->embeddedProfile().isEmpty()) + { + message->setText(i18n("<p>This image has not been assigned a color profile.</p>" + "<p>Do you want to convert it to your workspace color profile?</p>")); + + line->hide(); + embeddedProfileTitle->hide(); + embeddedProfileDesc->hide(); + embeddedProfInfo->hide(); + } + else + { + message->setText(i18n("<p>This image has been assigned to a color profile that does not " + "match your default workspace color profile.</p>" + "<p>Do you want to convert it to your workspace color profile?</p>")); + } + + previewOriginal->setPixmap(preview->convertToPixmap()); + previewTarget->setPixmap(preview->convertToPixmap(m_iccTrans)); + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 128, TDEIcon::DefaultState, 0, true)); + + grid->addMultiCellWidget(originalTitle, 0, 0, 0, 0); + grid->addMultiCellWidget(previewOriginal, 1, 1, 0, 0); + grid->addMultiCellWidget(targetTitle, 2, 2, 0, 0); + grid->addMultiCellWidget(previewTarget, 3, 3, 0, 0); + + TQVBoxLayout *vlay = new TQVBoxLayout( KDialog::spacingHint() ); + vlay->addWidget(logo); + vlay->addWidget(message); + + vlay->addWidget(new KSeparator(TQt::Horizontal, page)); + vlay->addWidget(currentProfileTitle); + vlay->addWidget(currentProfileDesc); + + TQHBoxLayout *hlay1 = new TQHBoxLayout( KDialog::spacingHint() ); + hlay1->addWidget(currentProfInfo); + hlay1->addStretch(); + vlay->addLayout(hlay1); + + vlay->addWidget(line); + vlay->addWidget(embeddedProfileTitle); + vlay->addWidget(embeddedProfileDesc); + + TQHBoxLayout *hlay2 = new TQHBoxLayout( KDialog::spacingHint() ); + hlay2->addWidget(embeddedProfInfo); + hlay2->addStretch(); + vlay->addLayout(hlay2); + vlay->addStretch(); + + grid->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::Expanding), 0, 3, 1, 1); + grid->addMultiCellLayout(vlay, 0, 3, 2, 2); + + setMainWidget(page); + + // -------------------------------------------------------------------- + + connect(currentProfInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCurrentProfInfo()) ); + + connect(embeddedProfInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotEmbeddedProfInfo()) ); + + connect(this, TQ_SIGNAL(applyClicked()), + this, TQ_SLOT(slotApplyClicked())); +} + +ColorCorrectionDlg::~ColorCorrectionDlg() +{ +} + +void ColorCorrectionDlg::slotCurrentProfInfo() +{ + if (m_iccTrans->outputProfile().isEmpty()) + return; + + ICCProfileInfoDlg infoDlg(m_parent, TQString(), m_iccTrans->outputProfile()); + infoDlg.exec(); +} + +void ColorCorrectionDlg::slotEmbeddedProfInfo() +{ + if (m_iccTrans->embeddedProfile().isEmpty()) + return; + + ICCProfileInfoDlg infoDlg(m_parent, TQString(), m_iccTrans->embeddedProfile()); + infoDlg.exec(); +} + +void ColorCorrectionDlg::slotApplyClicked() +{ + DDebug() << "colorcorrectiondlg: Apply pressed" << endl; + done(-1); +} + +} // NameSpace Digikam + diff --git a/src/utilities/imageeditor/canvas/colorcorrectiondlg.h b/src/utilities/imageeditor/canvas/colorcorrectiondlg.h new file mode 100644 index 00000000..7cc4c19d --- /dev/null +++ b/src/utilities/imageeditor/canvas/colorcorrectiondlg.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-15 + * Description : a dialog to see preview ICC color correction + * before to apply color profile. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef COLORCORRECTIONDLG_H +#define COLORCORRECTIONDLG_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class IccTransform; +class DImg; + +class DIGIKAM_EXPORT ColorCorrectionDlg : public KDialogBase +{ + TQ_OBJECT + + +public: + + ColorCorrectionDlg(TQWidget *parent, DImg *preview, + IccTransform *iccTrans, const TQString& file); + ~ColorCorrectionDlg(); + +private slots: + + void slotCurrentProfInfo(); + void slotEmbeddedProfInfo(); + void slotApplyClicked(); + +private: + + TQWidget *m_parent; + + IccTransform *m_iccTrans; +}; + +} // Namespace Digikam + +#endif /* COLORCORRECTIONDLG_H */ diff --git a/src/utilities/imageeditor/canvas/dimginterface.cpp b/src/utilities/imageeditor/canvas/dimginterface.cpp new file mode 100644 index 00000000..8ce1c114 --- /dev/null +++ b/src/utilities/imageeditor/canvas/dimginterface.cpp @@ -0,0 +1,1270 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-15 + * Description : DImg interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#define OPACITY 0.7 +#define RCOL 0xAA +#define GCOL 0xAA +#define BCOL 0xAA + +// C++ includes. + +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <iostream> + +// TQt includes. + +#include <tqwidget.h> +#include <tqimage.h> +#include <tqpixmap.h> +#include <tqbitmap.h> +#include <tqcolor.h> +#include <tqfile.h> +#include <tqvariant.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdemessagebox.h> + +// Local includes. + +#include "ddebug.h" +#include "bcgmodifier.h" +#include "colorcorrectiondlg.h" +#include "undomanager.h" +#include "undoaction.h" +#include "iccsettingscontainer.h" +#include "icctransform.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "rawimport.h" +#include "editortooliface.h" +#include "sharedloadsavethread.h" +#include "dmetadata.h" +#include "dimginterface.h" +#include "dimginterface.moc" + +namespace Digikam +{ + +class UndoManager; + +class DImgInterfacePrivate +{ + +public: + + DImgInterfacePrivate() + { + parent = 0; + undoMan = 0; + cmSettings = 0; + expoSettings = 0; + iofileSettings = 0; + thread = 0; + width = 0; + height = 0; + origWidth = 0; + origHeight = 0; + selX = 0; + selY = 0; + selW = 0; + selH = 0; + zoom = 1.0; + exifOrient = false; + valid = false; + rotatedOrFlipped = false; + } + + bool valid; + bool rotatedOrFlipped; + bool exifOrient; + bool changedBCG; + + int width; + int height; + int origWidth; + int origHeight; + int selX; + int selY; + int selW; + int selH; + + float gamma; + float brightness; + float contrast; + + double zoom; + + // Used by ICC color profile dialog. + TQWidget *parent; + + TQString filename; + TQString savingFilename; + + DImg image; + + UndoManager *undoMan; + + BCGModifier cmod; + + ICCSettingsContainer *cmSettings; + + ExposureSettingsContainer *expoSettings; + + IOFileSettingsContainer *iofileSettings; + + SharedLoadSaveThread *thread; + + IccTransform monitorICCtrans; +}; + +DImgInterface* DImgInterface::m_defaultInterface = 0; + +DImgInterface* DImgInterface::defaultInterface() +{ + return m_defaultInterface; +} + +void DImgInterface::setDefaultInterface(DImgInterface *defaultInterface) +{ + m_defaultInterface = defaultInterface; +} + +DImgInterface::DImgInterface() + : TQObject() +{ + d = new DImgInterfacePrivate; + + d->undoMan = new UndoManager(this); + d->thread = new SharedLoadSaveThread; + + connect( d->thread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg&)), + this, TQ_SLOT(slotImageLoaded(const LoadingDescription &, const DImg&)) ); + + connect( d->thread, TQ_SIGNAL(signalImageSaved(const TQString&, bool)), + this, TQ_SLOT(slotImageSaved(const TQString&, bool)) ); + + connect( d->thread, TQ_SIGNAL(signalLoadingProgress(const LoadingDescription &, float)), + this, TQ_SLOT(slotLoadingProgress(const LoadingDescription &, float)) ); + + connect( d->thread, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SLOT(slotSavingProgress(const TQString&, float)) ); +} + +DImgInterface::~DImgInterface() +{ + delete d->undoMan; + delete d->thread; + delete d; + if (m_defaultInterface == this) + m_defaultInterface = 0; +} + +void DImgInterface::load(const TQString& filename, IOFileSettingsContainer *iofileSettings, + TQWidget *parent) +{ + // store here in case filename == d->fileName, and is then reset by resetValues + TQString newFileName = filename; + + resetValues(); + + d->filename = newFileName; + d->iofileSettings = iofileSettings; + d->parent = parent; + + if (d->iofileSettings->useRAWImport && DImg::fileFormat(d->filename) == DImg::RAW) + { + RawImport *rawImport = new RawImport(KURL(d->filename), this); + EditorToolIface::editorToolIface()->loadTool(rawImport); + + connect(rawImport, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotUseRawImportSettings())); + + connect(rawImport, TQ_SIGNAL(cancelClicked()), + this, TQ_SLOT(slotUseDefaultSettings())); + } + else + { + slotUseDefaultSettings(); + } +} + +void DImgInterface::slotUseRawImportSettings() +{ + RawImport *rawImport = dynamic_cast<RawImport*>(EditorToolIface::editorToolIface()->currentTool()); + + d->thread->load(LoadingDescription(d->filename, + rawImport->rawDecodingSettings()), + SharedLoadSaveThread::AccessModeReadWrite, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + emit signalLoadingStarted(d->filename); + + EditorToolIface::editorToolIface()->unLoadTool(); +} + +void DImgInterface::slotUseDefaultSettings() +{ + d->thread->load(LoadingDescription(d->filename, + d->iofileSettings->rawDecodingSettings), + SharedLoadSaveThread::AccessModeReadWrite, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + emit signalLoadingStarted(d->filename); + + EditorToolIface::editorToolIface()->unLoadTool(); +} + +void DImgInterface::resetImage() +{ + EditorToolIface::editorToolIface()->unLoadTool(); + resetValues(); + d->image.reset(); +} + +void DImgInterface::resetValues() +{ + d->valid = false; + d->filename = TQString(); + d->width = 0; + d->height = 0; + d->origWidth = 0; + d->origHeight = 0; + d->selX = 0; + d->selY = 0; + d->selW = 0; + d->selH = 0; + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + d->changedBCG = false; + d->iofileSettings = 0; + d->parent = 0; + + d->cmod.reset(); + d->undoMan->clear(); +} + +void DImgInterface::setICCSettings(ICCSettingsContainer *cmSettings) +{ + d->cmSettings = cmSettings; + d->monitorICCtrans.setProfiles(d->cmSettings->workspaceSetting, d->cmSettings->monitorSetting); +} + +void DImgInterface::setExposureSettings(ExposureSettingsContainer *expoSettings) +{ + d->expoSettings = expoSettings; +} + +void DImgInterface::slotImageLoaded(const LoadingDescription &loadingDescription, const DImg& img) +{ + const TQString &fileName = loadingDescription.filePath; + + if (fileName != d->filename) + return; + + bool valRet = false; + d->image = img; + + if (!d->image.isNull()) + { + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + d->valid = true; + d->width = d->origWidth; + d->height = d->origHeight; + valRet = true; + + // Raw files are already rotated properlly by dcraw. Only perform auto-rotation with JPEG/PNG/TIFF file. + // We don't have a feedback from dcraw about auto-rotated RAW file during decoding. Well set transformed + // flag as well. + + if (d->image.attribute("format").toString() == TQString("RAW")) + d->rotatedOrFlipped = true; + + if (d->exifOrient && + (d->image.attribute("format").toString() == TQString("JPEG") || + d->image.attribute("format").toString() == TQString("PNG") || + d->image.attribute("format").toString() == TQString("TIFF"))) + exifRotate(d->filename); + + if (d->cmSettings->enableCMSetting) + { + if (TQFile::exists(d->cmSettings->workspaceSetting)) + { + IccTransform trans; + TQByteArray fakeProfile; + + // First possibility: image has no embedded profile + if(d->image.getICCProfil().isNull()) + { + // Ask or apply? + if (d->cmSettings->askOrApplySetting) + { + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.setProfiles(TQFile::encodeName(d->cmSettings->inputSetting), + TQFile::encodeName(d->cmSettings->workspaceSetting)); + + // NOTE: If Input color profile do not exist, using built-in sRGB intead. + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, + TQFile::exists(d->cmSettings->inputSetting)); + + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + } + else + { + // To repaint image in canvas before to ask about to apply ICC profile. + emit signalImageLoaded(d->filename, valRet); + + DImg preview = d->image.smoothScale(240, 180, TQSize::ScaleMin); + trans.setProfiles(TQFile::encodeName(d->cmSettings->inputSetting), + TQFile::encodeName(d->cmSettings->workspaceSetting)); + ColorCorrectionDlg dlg(d->parent, &preview, &trans, fileName); + + switch (dlg.exec()) + { + case TQDialog::Accepted: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + + // NOTE: If Input color profile do not exist, using built-in sRGB intead. + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, + TQFile::exists(d->cmSettings->inputSetting)); + + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + break; + case -1: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + DDebug() << "dimginterface.cpp: Apply pressed" << endl; + break; + } + } + } + // Second possibility: image has an embedded profile + else + { + trans.getEmbeddedProfile( d->image ); + + // Ask or apply? + if (d->cmSettings->askOrApplySetting) + { + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.setProfiles(TQFile::encodeName(d->cmSettings->workspaceSetting)); + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, false); + if (d->parent) d->parent->unsetCursor(); + } + else + { + if (trans.getEmbeddedProfileDescriptor() + != trans.getProfileDescription( d->cmSettings->workspaceSetting )) + { + // Embedded profile and default workspace profile are different: ask to user! + + DDebug() << "Embedded profile: " << trans.getEmbeddedProfileDescriptor() << endl; + + // To repaint image in canvas before to ask about to apply ICC profile. + emit signalImageLoaded(d->filename, valRet); + + DImg preview = d->image.smoothScale(240, 180, TQSize::ScaleMin); + trans.setProfiles(TQFile::encodeName(d->cmSettings->workspaceSetting)); + ColorCorrectionDlg dlg(d->parent, &preview, &trans, fileName); + + switch (dlg.exec()) + { + case TQDialog::Accepted: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, false); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + break; + case -1: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + DDebug() << "dimginterface.cpp: Apply pressed" << endl; + break; + } + } + } + } + } + else + { + TQString message = i18n("Cannot find the ICC color-space profile file. " + "The ICC profiles path seems to be invalid. " + "No color transform will be applied. " + "Please check the \"Color Management\" " + "configuration in digiKam's setup to verify the ICC path."); + KMessageBox::information(d->parent, message); + } + } + } + else + { + valRet = false; + } + + emit signalImageLoaded(d->filename, valRet); + setModified(); +} + +void DImgInterface::slotLoadingProgress(const LoadingDescription &loadingDescription, float progress) +{ + if (loadingDescription.filePath == d->filename) + emit signalLoadingProgress(loadingDescription.filePath, progress); +} + +bool DImgInterface::exifRotated() +{ + return d->rotatedOrFlipped; +} + +void DImgInterface::exifRotate(const TQString& filename) +{ + // Rotate image based on EXIF rotate tag + + DMetadata metadata(filename); + DMetadata::ImageOrientation orientation = metadata.getImageOrientation(); + + if(orientation != DMetadata::ORIENTATION_NORMAL) + { + switch (orientation) + { + case DMetadata::ORIENTATION_NORMAL: + case DMetadata::ORIENTATION_UNSPECIFIED: + break; + + case DMetadata::ORIENTATION_HFLIP: + flipHoriz(false); + break; + + case DMetadata::ORIENTATION_ROT_180: + rotate180(false); + break; + + case DMetadata::ORIENTATION_VFLIP: + flipVert(false); + break; + + case DMetadata::ORIENTATION_ROT_90_HFLIP: + rotate90(false); + flipHoriz(false); + break; + + case DMetadata::ORIENTATION_ROT_90: + rotate90(false); + break; + + case DMetadata::ORIENTATION_ROT_90_VFLIP: + rotate90(false); + flipVert(false); + break; + + case DMetadata::ORIENTATION_ROT_270: + rotate270(false); + break; + } + + d->rotatedOrFlipped = true; + } +} + +void DImgInterface::setExifOrient(bool exifOrient) +{ + d->exifOrient = exifOrient; +} + +void DImgInterface::undo() +{ + if (!d->undoMan->anyMoreUndo()) + { + emit signalUndoStateChanged(false, d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); + return; + } + + d->undoMan->undo(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::redo() +{ + if (!d->undoMan->anyMoreRedo()) + { + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), false, !d->undoMan->isAtOrigin()); + return; + } + + d->undoMan->redo(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::restore() +{ + d->undoMan->clear(); + load(d->filename, d->iofileSettings); +} + +/* +This code is unused and untested +void DImgInterface::save(const TQString& file, IOFileSettingsContainer *iofileSettings) +{ + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->cmod.applyBCG(d->image); + + d->cmod.reset(); + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + + TQString currentMimeType(TQImageIO::imageFormat(d->filename)); + + d->needClearUndoManager = true; + + saveAction(file, iofileSettings, currentMimeType); +} +*/ + +void DImgInterface::saveAs(const TQString& fileName, IOFileSettingsContainer *iofileSettings, + bool setExifOrientationTag, const TQString& givenMimeType) +{ + // No need to toggle off undo, redo or save action during saving using + // signalUndoStateChanged(), this is will done by GUI implementation directly. + + if (d->changedBCG) + { + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->cmod.applyBCG(d->image); + } + + // Try hard to find a mimetype. + TQString mimeType = givenMimeType; + + // This is possibly empty + if (mimeType.isEmpty()) + mimeType = getImageFormat(); + + DDebug() << "Saving to :" << TQFile::encodeName(fileName).data() << " (" + << mimeType << ")" << endl; + + // JPEG file format. + if ( mimeType.upper() == TQString("JPG") || mimeType.upper() == TQString("JPEG") || + mimeType.upper() == TQString("JPE")) + { + d->image.setAttribute("quality", iofileSettings->JPEGCompression); + d->image.setAttribute("subsampling", iofileSettings->JPEGSubSampling); + } + + // PNG file format. + if ( mimeType.upper() == TQString("PNG") ) + d->image.setAttribute("quality", iofileSettings->PNGCompression); + + // TIFF file format. + if ( mimeType.upper() == TQString("TIFF") || mimeType.upper() == TQString("TIF") ) + d->image.setAttribute("compress", iofileSettings->TIFFCompression); + + // JPEG 2000 file format. + if ( mimeType.upper() == TQString("JP2") || mimeType.upper() == TQString("JPX") || + mimeType.upper() == TQString("JPC") || mimeType.upper() == TQString("PGX")) + { + if (iofileSettings->JPEG2000LossLess) + d->image.setAttribute("quality", 100); // LossLess compression + else + d->image.setAttribute("quality", iofileSettings->JPEG2000Compression); + } + + d->savingFilename = fileName; + + // Get image Exif/Iptc data. + DMetadata meta; + meta.setExif(d->image.getExif()); + meta.setIptc(d->image.getIptc()); + + // Update Iptc preview. + // NOTE: see B.K.O #130525. a JPEG segment is limited to 64K. If the IPTC byte array is + // bigger than 64K duing of image preview tag size, the target JPEG image will be + // broken. Note that IPTC image preview tag is limited to 256K!!! + // There is no limitation with TIFF and PNG about IPTC byte array size. + + TQImage preview = d->image.smoothScale(1280, 1024, TQSize::ScaleMin).copyTQImage(); + + if ( mimeType.upper() != TQString("JPG") && mimeType.upper() != TQString("JPEG") && + mimeType.upper() != TQString("JPE")) + { + // Non JPEG file, we update IPTC preview + meta.setImagePreview(preview); + } + else + { + // JPEG file, we remove IPTC preview. + meta.removeIptcTag("Iptc.Application2.Preview"); + meta.removeIptcTag("Iptc.Application2.PreviewFormat"); + meta.removeIptcTag("Iptc.Application2.PreviewVersion"); + } + + // Update Exif thumbnail. + TQImage thumb = preview.smoothScale(160, 120, TQImage::ScaleMin); + meta.setExifThumbnail(thumb); + + // Update Exif Image dimensions. + meta.setImageDimensions(d->image.size()); + + // Update Exif Document Name tag with the original file name. + meta.setExifTagString("Exif.Image.DocumentName", getImageFileName()); + + // Update Exif Orientation tag if necessary. + if( setExifOrientationTag ) + meta.setImageOrientation(DMetadata::ORIENTATION_NORMAL); + + // Store new Exif/Iptc data into image. + d->image.setExif(meta.getExif()); + d->image.setIptc(meta.getIptc()); + + d->thread->save(d->image, fileName, mimeType); +} + +void DImgInterface::slotImageSaved(const TQString& filePath, bool success) +{ + if (filePath != d->savingFilename) + return; + + if (!success) + DWarning() << "error saving image '" << TQFile::encodeName(filePath).data() << endl; + + emit signalImageSaved(filePath, success); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::slotSavingProgress(const TQString& filePath, float progress) +{ + if (filePath == d->savingFilename) + emit signalSavingProgress(filePath, progress); +} + +void DImgInterface::abortSaving() +{ + // failure will be reported by a signal + d->thread->stopSaving(d->savingFilename); +} + +void DImgInterface::switchToLastSaved(const TQString& newFilename) +{ + // Higher level wants to use the current DImg object to represent the file + // it has previously been saved to. + d->filename = newFilename; + + // Currently the only place where a DImg is connected to the file it originates from + // is the format attribute. + TQString savedformat = d->image.attribute("savedformat").toString(); + if (!savedformat.isEmpty()) + d->image.setAttribute("format", savedformat); +} + +void DImgInterface::setModified() +{ + emit signalModified(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::readMetadataFromFile(const TQString &file) +{ + DMetadata meta(file); + + //TODO: code is essentially the same as DImgLoader::readMetadata, + // put both in a common place. + if (!meta.getComments().isNull()) + d->image.setComments(meta.getComments()); + if (!meta.getExif().isNull()) + d->image.setExif(meta.getExif()); + if (!meta.getIptc().isNull()) + d->image.setIptc(meta.getIptc()); +} + +void DImgInterface::clearUndoManager() +{ + d->undoMan->clear(); + d->undoMan->setOrigin(); + emit signalUndoStateChanged(false, false, false); +} + +void DImgInterface::setUndoManagerOrigin() +{ + d->undoMan->setOrigin(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::updateUndoState() +{ + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +bool DImgInterface::imageValid() +{ + return !d->image.isNull(); +} + +int DImgInterface::width() +{ + return d->width; +} + +int DImgInterface::height() +{ + return d->height; +} + +int DImgInterface::origWidth() +{ + return d->origWidth; +} + +int DImgInterface::origHeight() +{ + return d->origHeight; +} + +int DImgInterface::bytesDepth() +{ + return d->image.bytesDepth(); +} + +bool DImgInterface::sixteenBit() +{ + return d->image.sixteenBit(); +} + +bool DImgInterface::hasAlpha() +{ + return d->image.hasAlpha(); +} + +bool DImgInterface::isReadOnly() +{ + if (d->image.isNull()) + return true; + else + return d->image.isReadOnly(); +} + +void DImgInterface::setSelectedArea(int x, int y, int w, int h) +{ + d->selX = x; + d->selY = y; + d->selW = w; + d->selH = h; +} + +void DImgInterface::getSelectedArea(int& x, int& y, int& w, int& h) +{ + x = d->selX; + y = d->selY; + w = d->selW; + h = d->selH; +} + +void DImgInterface::paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int /*antialias*/) +{ + if (d->image.isNull()) + return; + + DImg img = d->image.smoothScaleSection(sx, sy, sw, sh, dw, dh); + d->cmod.applyBCG(img); + img.convertDepth(32); + + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + { + TQPixmap pix(img.convertToPixmap(&d->monitorICCtrans)); + bitBlt(p, dx, dy, &pix, 0, 0); + } + else + { + TQPixmap pix(img.convertToPixmap()); + bitBlt(p, dx, dy, &pix, 0, 0); + } + + // Show the Over/Under exposure pixels indicators + + if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator) + { + TQImage pureColorMask = d->image.copy(sx, sy, sw, sh).pureColorMask(d->expoSettings); + TQPixmap pixMask(pureColorMask.scale(dw, dh)); + bitBlt(p, dx, dy, &pixMask, 0, 0); + } +} + +void DImgInterface::paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int mx, int my, int mw, int mh, + int /*antialias*/) +{ + if (d->image.isNull()) + return; + + DImg img = d->image.smoothScaleSection(sx, sy, sw, sh, dw, dh); + d->cmod.applyBCG(img); + img.convertDepth(32); + + uint* data = (uint*)img.bits(); + uchar r, g, b, a; + + for (int j=0; j < (int)img.height(); j++) + { + for (int i=0; i < (int)img.width(); i++) + { + if (i < (mx-dx) || i > (mx-dx+mw-1) || + j < (my-dy) || j > (my-dy+mh-1)) + { + a = (*data >> 24) & 0xff; + r = (*data >> 16) & 0xff; + g = (*data >> 8) & 0xff; + b = (*data ) & 0xff; + + r += (uchar)((RCOL - r) * OPACITY); + g += (uchar)((GCOL - g) * OPACITY); + b += (uchar)((BCOL - b) * OPACITY); + + *data = (a << 24) | (r << 16) | (g << 8) | b; + } + + data++; + } + } + + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + { + TQPixmap pix(img.convertToPixmap(&d->monitorICCtrans)); + bitBlt(p, dx, dy, &pix, 0, 0); + } + else + { + TQPixmap pix(img.convertToPixmap()); + bitBlt(p, dx, dy, &pix, 0, 0); + } + + // Show the Over/Under exposure pixels indicators + + if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator) + { + TQImage pureColorMask = d->image.copy(sx, sy, sw, sh).pureColorMask(d->expoSettings); + TQPixmap pixMask(pureColorMask.scale(dw, dh)); + bitBlt(p, dx, dy, &pixMask, 0, 0); + } +} + +void DImgInterface::zoom(double val) +{ + d->zoom = val; + d->width = (int)(d->origWidth * val); + d->height = (int)(d->origHeight * val); +} + +void DImgInterface::rotate90(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R90)); + } + + d->image.rotate(DImg::ROT90); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::rotate180(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R180)); + } + + d->image.rotate(DImg::ROT180); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::rotate270(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R270)); + } + + d->image.rotate(DImg::ROT270); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::flipHoriz(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionFlip(this, UndoActionFlip::Horizontal)); + } + + d->image.flip(DImg::HORIZONTAL); + + setModified(); +} + +void DImgInterface::flipVert(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionFlip(this, UndoActionFlip::Vertical)); + } + + d->image.flip(DImg::VERTICAL); + + setModified(); +} + +void DImgInterface::crop(int x, int y, int w, int h) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Crop")); + + d->image.crop(x, y, w, h); + + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::resize(int w, int h) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Resize")); + + d->image.resize(w, h); + + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::changeGamma(double gamma) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, gamma, d->brightness, + d->contrast)); + + d->gamma += gamma/10.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeBrightness(double brightness) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, d->gamma, brightness, + d->contrast)); + + d->brightness += brightness/100.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeContrast(double contrast) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, d->gamma, d->brightness, + contrast)); + + d->contrast += contrast/100.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeBCG(double gamma, double brightness, double contrast) +{ + d->gamma = gamma; + d->brightness = brightness; + d->contrast = contrast; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::setBCG(double brightness, double contrast, double gamma) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Brightness, Contrast, Gamma")); + + d->cmod.reset(); + d->cmod.setGamma(gamma); + d->cmod.setBrightness(brightness); + d->cmod.setContrast(contrast); + d->cmod.applyBCG(d->image); + + d->cmod.reset(); + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + d->changedBCG = false; + + setModified(); +} + +void DImgInterface::convertDepth(int depth) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Convert Color Depth")); + d->image.convertDepth(depth); + + setModified(); +} + +DImg* DImgInterface::getImg() +{ + if (!d->image.isNull()) + { + return &d->image; + } + else + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return 0; + } +} + +uchar* DImgInterface::getImage() +{ + if (!d->image.isNull()) + { + return d->image.bits(); + } + else + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return 0; + } +} + +void DImgInterface::putImage(const TQString &caller, uchar* data, int w, int h) +{ + putImage(caller, data, w, h, d->image.sixteenBit()); +} + +void DImgInterface::putImage(const TQString &caller, uchar* data, int w, int h, bool sixteenBit) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, caller)); + putImage(data, w, h, sixteenBit); +} + +void DImgInterface::putImage(uchar* data, int w, int h) +{ + putImage(data, w, h, d->image.sixteenBit()); +} + +void DImgInterface::putImage(uchar* data, int w, int h, bool sixteenBit) +{ + if (d->image.isNull()) + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return; + } + + if (!data) + { + DWarning() << k_funcinfo << "New image is NULL" << endl; + return; + } + + if (w == -1 && h == -1) + { + // New image size + w = d->origWidth; + h = d->origHeight; + } + else + { + // New image size == original size + d->origWidth = w; + d->origHeight = h; + } + + //DDebug() << k_funcinfo << data << " " << w << " " << h << endl; + d->image.putImageData(w, h, sixteenBit, d->image.hasAlpha(), data); + + setModified(); +} + +void DImgInterface::setEmbeddedICCToOriginalImage( TQString profilePath) +{ + if (d->image.isNull()) + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return; + } + + DDebug() << k_funcinfo << "Embedding profile: " << profilePath << endl; + d->image.getICCProfilFromFile( TQFile::encodeName(profilePath)); + setModified(); +} + +uchar* DImgInterface::getImageSelection() +{ + if (!d->selW || !d->selH) + return 0; + + if (!d->image.isNull()) + { + DImg im = d->image.copy(d->selX, d->selY, d->selW, d->selH); + return im.stripImageData(); + } + + return 0; +} + +void DImgInterface::putImageSelection(const TQString &caller, uchar* data) +{ + if (!data || d->image.isNull()) + return; + + d->undoMan->addAction(new UndoActionIrreversible(this, caller)); + + d->image.bitBltImage(data, 0, 0, d->selW, d->selH, d->selX, d->selY, d->selW, d->selH, d->image.bytesDepth()); + + setModified(); +} + +void DImgInterface::getUndoHistory(TQStringList &titles) +{ + d->undoMan->getUndoHistory(titles); +} + +void DImgInterface::getRedoHistory(TQStringList &titles) +{ + d->undoMan->getRedoHistory(titles); +} + +TQByteArray DImgInterface::getEmbeddedICC() +{ + return d->image.getICCProfil(); +} + +TQByteArray DImgInterface::getExif() +{ + return d->image.getExif(); +} + +TQByteArray DImgInterface::getIptc() +{ + return d->image.getIptc(); +} + +TQString DImgInterface::getImageFilePath() +{ + return d->filename; +} + +TQString DImgInterface::getImageFileName() +{ + return d->filename.section( '/', -1 ); +} + +TQString DImgInterface::getImageFormat() +{ + if (d->image.isNull()) + return TQString(); + + TQString mimeType = d->image.attribute("format").toString(); + // It is a bug in the loader if format attribute is not given + if (mimeType.isEmpty()) + { + DWarning() << "DImg object does not contain attribute \"format\"" << endl; + mimeType = TQImageIO::imageFormat(d->filename); + } + return mimeType; +} + +ICCSettingsContainer* DImgInterface::getICCSettings() +{ + return d->cmSettings; +} + +TQPixmap DImgInterface::convertToPixmap(DImg& img) +{ + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + return img.convertToPixmap(&d->monitorICCtrans); + + return img.convertToPixmap(); +} + +TQColor DImgInterface::underExposureColor() +{ + return d->expoSettings->underExposureColor; +} + +TQColor DImgInterface::overExposureColor() +{ + return d->expoSettings->overExposureColor; +} + +} // namespace Digikam + diff --git a/src/utilities/imageeditor/canvas/dimginterface.h b/src/utilities/imageeditor/canvas/dimginterface.h new file mode 100644 index 00000000..9a41eb05 --- /dev/null +++ b/src/utilities/imageeditor/canvas/dimginterface.h @@ -0,0 +1,200 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-15 + * Description : DImg interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef DIMGINTERFACE_H +#define DIMGINTERFACE_H + +// TQt includes. + +#include <tqobject.h> +#include <tqstring.h> + +// Local includes. + +#include "digikam_export.h" +#include "dimg.h" + +class TQWidget; +class TQPixmap; + +namespace Digikam +{ + +class ICCSettingsContainer; +class ExposureSettingsContainer; +class IOFileSettingsContainer; +class LoadingDescription; +class DImgInterfacePrivate; + +class DIGIKAM_EXPORT DImgInterface : public TQObject +{ + TQ_OBJECT + + +public: + + static DImgInterface* defaultInterface(); + static void setDefaultInterface(DImgInterface *defaultInterface); + + DImgInterface(); + ~DImgInterface(); + + void load(const TQString& filename, IOFileSettingsContainer *iofileSettings, TQWidget *parent=0); + + void setICCSettings(ICCSettingsContainer *cmSettings); + void setExposureSettings(ExposureSettingsContainer *expoSettings); + void setExifOrient(bool exifOrient); + + void undo(); + void redo(); + void restore(); + + void saveAs(const TQString& file, IOFileSettingsContainer *iofileSettings, + bool setExifOrientationTag, const TQString& mimeType=TQString()); + + void switchToLastSaved(const TQString& newFilename); + void abortSaving(); + void setModified(); + void readMetadataFromFile(const TQString &file); + void clearUndoManager(); + void setUndoManagerOrigin(); + void updateUndoState(); + void resetImage(); + + void zoom(double val); + + void paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int antialias); + void paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int mx, int my, int mw, int mh, + int antialias); + + bool imageValid(); + int width(); + int height(); + int origWidth(); + int origHeight(); + int bytesDepth(); + bool hasAlpha(); + bool sixteenBit(); + bool exifRotated(); + bool isReadOnly(); + + void setSelectedArea(int x, int y, int w, int h); + void getSelectedArea(int& x, int& y, int& w, int& h); + + void rotate90(bool saveUndo=true); + void rotate180(bool saveUndo=true); + void rotate270(bool saveUndo=true); + + void flipHoriz(bool saveUndo=true); + void flipVert(bool saveUndo=true); + + void crop(int x, int y, int w, int h); + + void resize(int w, int h); + + void changeGamma(double gamma); + void changeBrightness(double brightness); + void changeContrast(double contrast); + void changeBCG(double gamma, double brightness, double contrast); + + void setBCG(double brightness, double contrast, double gamma); + + void convertDepth(int depth); + + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + + DImg* getImg(); + uchar* getImage(); + + void putImage(uchar* data, int w, int h); + void putImage(uchar* data, int w, int h, bool sixteenBit); + void putImage(const TQString &caller, uchar* data, int w, int h); + void putImage(const TQString &caller, uchar* data, int w, int h, bool sixteenBit); + + uchar* getImageSelection(); + void putImageSelection(const TQString &caller, uchar* data); + + void setEmbeddedICCToOriginalImage( TQString profilePath); + + /** Convert a DImg image to a pixmap for screen using color + managemed view if necessary */ + TQPixmap convertToPixmap(DImg& img); + + TQByteArray getEmbeddedICC(); + TQByteArray getExif(); + TQByteArray getIptc(); + + ICCSettingsContainer *getICCSettings(); + + TQString getImageFileName(); + TQString getImageFilePath(); + TQString getImageFormat(); + + TQColor underExposureColor(); + TQColor overExposureColor(); + +protected slots: + + void slotImageLoaded(const LoadingDescription &loadingDescription, const DImg& img); + void slotImageSaved(const TQString& filePath, bool success); + void slotLoadingProgress(const LoadingDescription &loadingDescription, float progress); + void slotSavingProgress(const TQString& filePath, float progress); + +signals: + + void signalModified(); + void signalUndoStateChanged(bool moreUndo, bool moreRedo, bool canSave); + void signalLoadingStarted(const TQString& filename); + void signalLoadingProgress(const TQString& filePath, float progress); + void signalImageLoaded(const TQString& filePath, bool success); + void signalSavingProgress(const TQString& filePath, float progress); + void signalImageSaved(const TQString& filePath, bool success); + +private slots: + + void slotUseRawImportSettings(); + void slotUseDefaultSettings(); + +private: + + void exifRotate(const TQString& filename); + void resetValues(); + +private: + + static DImgInterface *m_defaultInterface; + + DImgInterfacePrivate *d; +}; + +} // namespace Digikam + +#endif /* DIMGINTERFACE_H */ diff --git a/src/utilities/imageeditor/canvas/iccsettingscontainer.h b/src/utilities/imageeditor/canvas/iccsettingscontainer.h new file mode 100644 index 00000000..eadb12fb --- /dev/null +++ b/src/utilities/imageeditor/canvas/iccsettingscontainer.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-08 + * Description : ICC Settings Container. + * + * Copyright (C) 2005-2007 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef ICCSETTINGSCONTAINER_H +#define ICCSETTINGSCONTAINER_H + +// TQt includes. + +#include <tqstring.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT ICCSettingsContainer +{ + +public: + + ICCSettingsContainer() + { + enableCMSetting = false; // NOTE: by default, ICC color management is disable. + + askOrApplySetting = false; + BPCSetting = false; + managedViewSetting = false; + + renderingSetting = 0; + + workspaceSetting = TQString(); + monitorSetting = TQString(); + inputSetting = TQString(); + proofSetting = TQString(); + }; + + ~ICCSettingsContainer(){}; + +public: + + bool enableCMSetting; + + // FALSE -> apply + // TRUE -> ask + bool askOrApplySetting; + bool BPCSetting; + bool managedViewSetting; + + int renderingSetting; + + TQString workspaceSetting; + TQString monitorSetting; + TQString inputSetting; + TQString proofSetting; +}; + +} // namespace Digikam + +#endif // ICCSETTINGSCONTAINER_H diff --git a/src/utilities/imageeditor/canvas/imageplugin.cpp b/src/utilities/imageeditor/canvas/imageplugin.cpp new file mode 100644 index 00000000..b330cb5a --- /dev/null +++ b/src/utilities/imageeditor/canvas/imageplugin.cpp @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// Local includes. + +#include "editortool.h" +#include "editortooliface.h" +#include "imageplugin.h" +#include "imageplugin.moc" + +namespace Digikam +{ + +ImagePlugin::ImagePlugin(TQObject *parent, const char* name) + : TQObject(parent, name) +{ +} + +ImagePlugin::~ImagePlugin() +{ +} + +void ImagePlugin::setEnabledSelectionActions(bool) +{ +} + +void ImagePlugin::setEnabledActions(bool) +{ +} + +void ImagePlugin::loadTool(EditorTool* tool) +{ + EditorToolIface::editorToolIface()->loadTool(tool); + + connect(tool, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotToolDone())); + + connect(tool, TQ_SIGNAL(cancelClicked()), + this, TQ_SLOT(slotToolDone())); +} + +void ImagePlugin::slotToolDone() +{ + EditorToolIface::editorToolIface()->unLoadTool(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/imageplugin.h b/src/utilities/imageeditor/canvas/imageplugin.h new file mode 100644 index 00000000..ef506256 --- /dev/null +++ b/src/utilities/imageeditor/canvas/imageplugin.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins interface for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_H +#define IMAGEPLUGIN_H + +// TQt includes. + +#include <tqobject.h> + +// KDE includes. + +#include <kxmlguiclient.h> + +// Local includes. + +#include "digikam_export.h" + +class TQWidget; + +namespace Digikam +{ + +class EditorTool; + +class DIGIKAM_EXPORT ImagePlugin : public TQObject, public KXMLGUIClient +{ + TQ_OBJECT + + +public: + + ImagePlugin(TQObject *parent, const char* name=0); + virtual ~ImagePlugin(); + + virtual void setEnabledSelectionActions(bool enable); + virtual void setEnabledActions(bool enable); + + void loadTool(EditorTool* tool); + +private slots: + + void slotToolDone(); +}; + +} //namespace Digikam + +#endif /* IMAGEPLUGIN_H */ + diff --git a/src/utilities/imageeditor/canvas/imagepluginloader.cpp b/src/utilities/imageeditor/canvas/imagepluginloader.cpp new file mode 100644 index 00000000..43c8a16c --- /dev/null +++ b/src/utilities/imageeditor/canvas/imagepluginloader.cpp @@ -0,0 +1,278 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins loader for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// KDE includes. + +#include <ktrader.h> +#include <tdeparts/componentfactory.h> +#include <tdeapplication.h> +#include <tdelocale.h> +#include <kxmlguiclient.h> + +// Local includes. + +#include "ddebug.h" +#include "splashscreen.h" +#include "imagepluginloader.h" + +namespace Digikam +{ + +// List of obsolete image plugins name. + +static const char* ObsoleteImagePluginsList[] = +{ + "digikamimageplugin_blowup", // Merged with "Resize" tool since 0.9.2. + "digikamimageplugin_solarize", // Renamed "ColorFx" since 0.9.2. + "digikamimageplugin_unsharp", // Merged with "Sharpen" tool since 0.9.2. + "digikamimageplugin_refocus", // Merged with "Sharpen" tool since 0.9.2. + "digikamimageplugin_despeckle", // Renamed "Noise Reduction" since 0.9.2. + "-1" +}; + +class ImagePluginLoaderPrivate +{ + +public: + + typedef TQPair<TQString, ImagePlugin*> PluginType; + typedef TQValueList< PluginType > PluginList; + +public: + + ImagePluginLoaderPrivate() + { + splash = 0; + + for (int i=0 ; TQString(ObsoleteImagePluginsList[i]) != TQString("-1") ; i++) + obsoleteImagePluginsList << ObsoleteImagePluginsList[i]; + } + + TQStringList obsoleteImagePluginsList; + + SplashScreen *splash; + + PluginList pluginList; +}; + +ImagePluginLoader* ImagePluginLoader::m_instance=0; + +ImagePluginLoader* ImagePluginLoader::instance() +{ + return m_instance; +} + +ImagePluginLoader::ImagePluginLoader(TQObject *parent, SplashScreen *splash) + : TQObject(parent) +{ + m_instance = this; + d = new ImagePluginLoaderPrivate; + d->splash = splash; + + TQStringList imagePluginsList2Load; + + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for (iter = offers.begin() ; iter != offers.end() ; ++iter) + { + KService::Ptr service = *iter; + if (!d->obsoleteImagePluginsList.contains(service->library())) + imagePluginsList2Load.append(service->library()); + } + + loadPluginsFromList(imagePluginsList2Load); +} + +ImagePluginLoader::~ImagePluginLoader() +{ + delete d; + m_instance = 0; +} + +void ImagePluginLoader::loadPluginsFromList(const TQStringList& list) +{ + if (d->splash) + d->splash->message(i18n("Loading Image Plugins")); + + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + int cpt = 0; + + // Load plugin core at the first time. + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + ImagePlugin *plugin; + + if (service->library() == "digikamimageplugin_core") + { + if (!pluginIsLoaded(service->name()) ) + { + int error=-1; + plugin = KParts::ComponentFactory::createInstanceFromService<ImagePlugin>( + service, this, service->name().local8Bit(), 0, &error); + + if (plugin && (dynamic_cast<KXMLGUIClient*>(plugin) != 0)) + { + d->pluginList.append(ImagePluginLoaderPrivate::PluginType(service->name(), plugin)); + + DDebug() << "ImagePluginLoader: Loaded plugin " << service->name() << endl; + + ++cpt; + } + else + { + DWarning() << "ImagePluginLoader:: createInstanceFromLibrary returned 0 for " + << service->name() + << " (" << service->library() << ")" + << " with error code " + << error << endl; + if (error == KParts::ComponentFactory::ErrNoLibrary) + DWarning() << "KLibLoader says: " + << KLibLoader::self()->lastErrorMessage() << endl; + } + } + break; + } + } + + // Load all other image plugins after (make a coherant menu construction in Image Editor). + + for (iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + ImagePlugin *plugin; + + if (!list.contains(service->library()) && service->library() != "digikamimageplugin_core") + { + if ((plugin = pluginIsLoaded(service->name())) != NULL) + d->pluginList.remove(ImagePluginLoaderPrivate::PluginType(service->name(),plugin)); + } + else + { + if( pluginIsLoaded(service->name()) ) + continue; + else + { + int error=-1; + plugin = KParts::ComponentFactory::createInstanceFromService<ImagePlugin>( + service, this, service->name().local8Bit(), 0); + + if (plugin && (dynamic_cast<KXMLGUIClient*>(plugin) != 0)) + { + d->pluginList.append(ImagePluginLoaderPrivate::PluginType(service->name(), plugin)); + + DDebug() << "ImagePluginLoader: Loaded plugin " << service->name() << endl; + + ++cpt; + } + else + { + DWarning() << "ImagePluginLoader:: createInstanceFromLibrary returned 0 for " + << service->name() + << " (" << service->library() << ")" + << " with error code " + << error << endl; + if (error == KParts::ComponentFactory::ErrNoLibrary) + DWarning() << "KLibLoader says: " + << KLibLoader::self()->lastErrorMessage() << endl; + } + } + } + } + + d->splash = 0; // Splashcreen is only lanched at the first time. + // If user change plugins list to use in setup, don't try to + // use the old splashscreen instance. +} + +ImagePlugin* ImagePluginLoader::pluginIsLoaded(const TQString& name) +{ + if ( d->pluginList.isEmpty() ) + return 0; + + for (ImagePluginLoaderPrivate::PluginList::iterator it = d->pluginList.begin(); + it != d->pluginList.end(); ++it) + { + if ((*it).first == name ) + return (*it).second; + } + + return 0; +} + +ImagePlugin* ImagePluginLoader::pluginInstance(const TQString& libraryName) +{ + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + + if(service->library() == libraryName) + { + return ( pluginIsLoaded(service->name()) ); + } + } + + return 0; +} + +bool ImagePluginLoader::pluginLibraryIsLoaded(const TQString& libraryName) +{ + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + + if(service->library() == libraryName) + { + if( pluginIsLoaded(service->name()) ) + return true; + } + } + + return false; +} + +TQPtrList<ImagePlugin> ImagePluginLoader::pluginList() +{ + TQPtrList<ImagePlugin> list; + + for (ImagePluginLoaderPrivate::PluginList::iterator it = d->pluginList.begin(); + it != d->pluginList.end(); ++it) + { + list.append((*it).second); + } + + return list; +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/imagepluginloader.h b/src/utilities/imageeditor/canvas/imagepluginloader.h new file mode 100644 index 00000000..55ffe933 --- /dev/null +++ b/src/utilities/imageeditor/canvas/imagepluginloader.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins loader for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGINLOADER_H +#define IMAGEPLUGINLOADER_H + +// TQt includes. + +#include <tqobject.h> +#include <tqptrlist.h> +#include <tqstring.h> +#include <tqvaluelist.h> +#include <tqpair.h> + +// Local includes. + +#include "digikam_export.h" +#include "imageplugin.h" + +namespace Digikam +{ + +class SplashScreen; +class ImagePluginLoaderPrivate; + +class DIGIKAM_EXPORT ImagePluginLoader : public TQObject +{ + +public: + + ImagePluginLoader(TQObject *parent, SplashScreen *splash=0); + ~ImagePluginLoader(); + + static ImagePluginLoader* instance(); + + TQPtrList<ImagePlugin> pluginList(); + void loadPluginsFromList(const TQStringList& list); + + // Return true if plugin library is loaded in memory. + // 'libraryName' is internal plugin library name not i18n. + bool pluginLibraryIsLoaded(const TQString& libraryName); + + ImagePlugin* pluginInstance(const TQString& libraryName); + +private: + + ImagePlugin* pluginIsLoaded(const TQString& name); + +private: + + static ImagePluginLoader *m_instance; + + ImagePluginLoaderPrivate *d; +}; + +} // namespace Digikam + +#endif /* IMAGEPLUGINLOADER_H */ diff --git a/src/utilities/imageeditor/canvas/iofilesettingscontainer.h b/src/utilities/imageeditor/canvas/iofilesettingscontainer.h new file mode 100644 index 00000000..360299b3 --- /dev/null +++ b/src/utilities/imageeditor/canvas/iofilesettingscontainer.h @@ -0,0 +1,84 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-03 + * Description : IO file Settings Container. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef IOFILESETTINGSCONTAINER_H +#define IOFILESETTINGSCONTAINER_H + +// Local includes. + +#include "drawdecoding.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT IOFileSettingsContainer +{ + +public: + + IOFileSettingsContainer() + { + JPEGCompression = 75; + JPEGSubSampling = 1; // Medium subsampling + PNGCompression = 9; + TIFFCompression = false; + JPEG2000Compression = 75; + JPEG2000LossLess = true; + useRAWImport = true; + }; + + ~IOFileSettingsContainer(){}; + +public: + + // JPEG quality value. + int JPEGCompression; + + // JPEG chroma subsampling value. + int JPEGSubSampling; + + // PNG compression value. + int PNGCompression; + + // TIFF deflat compression. + bool TIFFCompression; + + // JPEG2000 quality value. + int JPEG2000Compression; + + // JPEG2000 lossless compression. + bool JPEG2000LossLess; + + // Use Raw Import tool to load a RAW picture. + bool useRAWImport; + + // ------------------------------------------------------ + // RAW File decoding options : + + DRawDecoding rawDecodingSettings; +}; + +} // namespace Digikam + +#endif // IOFILESETTINGSCONTAINER_H diff --git a/src/utilities/imageeditor/canvas/undoaction.cpp b/src/utilities/imageeditor/canvas/undoaction.cpp new file mode 100644 index 00000000..bb4ff756 --- /dev/null +++ b/src/utilities/imageeditor/canvas/undoaction.cpp @@ -0,0 +1,185 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : undo actions manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// Local includes. + +#include "ddebug.h" +#include "dimginterface.h" +#include "undoaction.h" + +namespace Digikam +{ + +UndoAction::UndoAction(DImgInterface* iface) + : m_iface(iface) +{ + m_title = i18n("unknown"); +} + +UndoAction::~UndoAction() +{ +} + +TQString UndoAction::getTitle() const +{ + return m_title; +} + +UndoActionRotate::UndoActionRotate(DImgInterface* iface, + UndoActionRotate::Angle angle) + : UndoAction(iface), m_angle(angle) +{ + switch(m_angle) + { + case(R90): + m_title = i18n("Rotate 90 Degrees"); + break; + case(R180): + m_title = i18n("Rotate 180 Degrees"); + break; + case(R270): + m_title = i18n("Rotate 270 Degrees"); + break; + } +} + +UndoActionRotate::~UndoActionRotate() +{ +} + +void UndoActionRotate::rollBack() +{ + switch(m_angle) + { + case(R90): + m_iface->rotate270(false); + return; + case(R180): + m_iface->rotate180(false); + return; + case(R270): + m_iface->rotate90(false); + return; + default: + DWarning() << "Unknown rotate angle specified" << endl; + } +} + +void UndoActionRotate::execute() +{ + switch(m_angle) + { + case R90: + m_iface->rotate90(false); + return; + case R180: + m_iface->rotate180(false); + return; + case R270: + m_iface->rotate270(false); + return; + default: + DWarning() << "Unknown rotate angle specified" << endl; + } +} + +UndoActionFlip::UndoActionFlip(DImgInterface* iface, + UndoActionFlip::Direction dir) + : UndoAction(iface), m_dir(dir) +{ + if(m_dir ==TQt::Horizontal) + m_title = i18n("Flip Horizontal"); + else if(m_dir ==TQt::Vertical) + m_title = i18n("Flip Vertical"); +} + +UndoActionFlip::~UndoActionFlip() +{ +} + +void UndoActionFlip::rollBack() +{ + switch(m_dir) + { + case TQt::Horizontal: + m_iface->flipHoriz(false); + return; + case TQt::Vertical: + m_iface->flipVert(false); + return; + default: + DWarning() << "Unknown flip direction specified" << endl; + } +} + +void UndoActionFlip::execute() +{ + rollBack(); +} + +UndoActionBCG::UndoActionBCG(DImgInterface* iface, + double oldGamma, double oldBrightness, + double oldContrast, double newGamma, + double newBrightness, double newContrast) + : UndoAction(iface), m_oldGamma(oldGamma), m_oldBrightness(oldBrightness), + m_oldContrast(oldContrast), m_newGamma(newGamma), m_newBrightness(newBrightness), + m_newContrast(newContrast) +{ + m_title = i18n("Brightness,Contrast,Gamma"); +} + +UndoActionBCG::~UndoActionBCG() +{ +} + +void UndoActionBCG::rollBack() +{ + m_iface->changeBCG(m_oldGamma, m_oldBrightness, m_oldContrast); +} + +void UndoActionBCG::execute() +{ + m_iface->changeBCG(m_newGamma, m_newBrightness, m_newContrast); +} + +UndoActionIrreversible::UndoActionIrreversible(DImgInterface* iface, + const TQString &title) + : UndoAction(iface) +{ + m_title = title; +} + +UndoActionIrreversible::~UndoActionIrreversible() +{ +} + +void UndoActionIrreversible::rollBack() +{ +} + +void UndoActionIrreversible::execute() +{ +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undoaction.h b/src/utilities/imageeditor/canvas/undoaction.h new file mode 100644 index 00000000..5f29486e --- /dev/null +++ b/src/utilities/imageeditor/canvas/undoaction.h @@ -0,0 +1,144 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : undo actions manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef UNDOACTION_H +#define UNDOACTION_H + +// KDE includes. + +#include <tdelocale.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgInterface; + +class DIGIKAM_EXPORT UndoAction +{ + +public: + + UndoAction(DImgInterface* iface); + virtual ~UndoAction(); + + virtual void rollBack() = 0; + virtual void execute() = 0; + + TQString getTitle() const; + +protected: + + DImgInterface *m_iface; + TQString m_title; +}; + +class DIGIKAM_EXPORT UndoActionRotate : public UndoAction +{ + +public: + + enum Angle + { + R90, + R180, + R270 + }; + + UndoActionRotate(DImgInterface* iface, Angle angle); + ~UndoActionRotate(); + + void rollBack(); + void execute(); + +private: + + int m_angle; +}; + +class DIGIKAM_EXPORT UndoActionFlip : public UndoAction +{ + +public: + + enum Direction + { + Horizontal, + Vertical + }; + + UndoActionFlip(DImgInterface* iface, Direction dir); + ~UndoActionFlip(); + + void rollBack(); + void execute(); + +private: + + int m_dir; +}; + +class DIGIKAM_EXPORT UndoActionBCG : public UndoAction +{ + +public: + + UndoActionBCG(DImgInterface* iface, + double oldGamma, double oldBrightness, + double oldContrast, double newGamma, + double newBrightness, double newContrast); + ~UndoActionBCG(); + + void rollBack(); + void execute(); + +private: + + double m_oldGamma; + double m_oldBrightness; + double m_oldContrast; + double m_newGamma; + double m_newBrightness; + double m_newContrast; +}; + +class DIGIKAM_EXPORT UndoActionIrreversible : public UndoAction +{ + +public: + + UndoActionIrreversible(DImgInterface* iface, + const TQString &caller=i18n("Unknown")); + ~UndoActionIrreversible(); + + void rollBack(); + void execute(); +}; + +} // namespace Digikam + +#endif /* UNDOACTION_H */ diff --git a/src/utilities/imageeditor/canvas/undocache.cpp b/src/utilities/imageeditor/canvas/undocache.cpp new file mode 100644 index 00000000..5f36ae75 --- /dev/null +++ b/src/utilities/imageeditor/canvas/undocache.cpp @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-05 + * Description : undo cache manager for image editor + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +} + +// TQt includes. + +#include <tqcstring.h> +#include <tqstring.h> +#include <tqfile.h> +#include <tqdatastream.h> +#include <tqstringlist.h> + +// KDE includes. + +#include <kstandarddirs.h> +#include <tdeaboutdata.h> +#include <kinstance.h> +#include <tdeglobal.h> + +// Local includes. + +#include "ddebug.h" +#include "undocache.h" + +namespace Digikam +{ + +class UndoCachePriv +{ +public: + + TQString cachePrefix; + TQStringList cacheFilenames; +}; + +UndoCache::UndoCache() +{ + d = new UndoCachePriv; + + TQString cacheDir; + cacheDir = locateLocal("cache", + TDEGlobal::instance()->aboutData()->programName() + '/'); + + d->cachePrefix = TQString("%1undocache-%2") + .arg(cacheDir) + .arg(getpid()); +} + +UndoCache::~UndoCache() +{ + clear(); + delete d; +} + +/** + * delete all cache files + */ +void UndoCache::clear() +{ + for (TQStringList::iterator it = d->cacheFilenames.begin(); + it != d->cacheFilenames.end(); ++it) + { + ::unlink(TQFile::encodeName(*it)); + } + + d->cacheFilenames.clear(); +} + +/** + * write the data into a cache file + */ +bool UndoCache::putData(int level, int w, int h, int bytesDepth, uchar* data) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + TQFile file(cacheFile); + + if (file.exists() || !file.open(IO_WriteOnly)) + return false; + + TQDataStream ds(&file); + ds << w; + ds << h; + ds << bytesDepth; + + TQByteArray ba(w*h*bytesDepth); + memcpy (ba.data(), data, w*h*bytesDepth); + ds << ba; + + file.close(); + + d->cacheFilenames.append(cacheFile); + + return true; +} + +/** + * get the data from a cache file + */ +uchar* UndoCache::getData(int level, int& w, int& h, int& bytesDepth, bool del) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + TQFile file(cacheFile); + if (!file.open(IO_ReadOnly)) + return 0; + + TQDataStream ds(&file); + ds >> w; + ds >> h; + ds >> bytesDepth; + + uchar *data = new uchar[w*h*bytesDepth]; + if (!data) + return 0; + + TQByteArray ba(w*h*bytesDepth); + ds >> ba; + memcpy (data, ba.data(), w*h*bytesDepth); + + file.close(); + + if(del) + { + ::unlink(TQFile::encodeName(cacheFile)); + d->cacheFilenames.remove(cacheFile); + } + + return data; +} + +/** + * delete a cache file + */ +void UndoCache::erase(int level) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + if(d->cacheFilenames.find(cacheFile) == d->cacheFilenames.end()) + return; + + ::unlink(TQFile::encodeName(cacheFile)); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undocache.h b/src/utilities/imageeditor/canvas/undocache.h new file mode 100644 index 00000000..732c7c3e --- /dev/null +++ b/src/utilities/imageeditor/canvas/undocache.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-05 + * Description : undo cache manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef UNDOCACHE_H +#define UNDOCACHE_H + +// TQt includes. + +#include <tqglobal.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class UndoCachePriv; + +class DIGIKAM_EXPORT UndoCache +{ + +public: + + UndoCache(); + ~UndoCache(); + + void clear(); + bool putData(int level, int w, int h, int bytesDepth, uchar* data); + uchar *getData(int level, int& w, int& h, int& bytesDepth, bool del=true); + + void erase(int level); + +private: + + UndoCachePriv *d; +}; + +} // namespace Digikam + +#endif /* UNDOCACHE_H */ diff --git a/src/utilities/imageeditor/canvas/undomanager.cpp b/src/utilities/imageeditor/canvas/undomanager.cpp new file mode 100644 index 00000000..87085d5d --- /dev/null +++ b/src/utilities/imageeditor/canvas/undomanager.cpp @@ -0,0 +1,253 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : an image editor actions undo/redo manager + * + * Copyright (C) 2005-2006 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2006 Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +// C++ includes. + +#include <typeinfo> +#include <climits> + +// Local includes. + +#include "ddebug.h" +#include "dimginterface.h" +#include "undoaction.h" +#include "undocache.h" +#include "undomanager.h" + +namespace Digikam +{ + +class UndoManagerPriv +{ + +public: + + UndoManagerPriv() + { + dimgiface = 0; + undoCache = 0; + origin = 0; + } + + TQValueList<UndoAction*> undoActions; + TQValueList<UndoAction*> redoActions; + int origin; + + UndoCache *undoCache; + + DImgInterface *dimgiface; +}; + +UndoManager::UndoManager(DImgInterface* iface) +{ + d = new UndoManagerPriv; + d->dimgiface = iface; + d->undoCache = new UndoCache; +} + +UndoManager::~UndoManager() +{ + clear(true); + delete d->undoCache; + delete d; +} + +void UndoManager::addAction(UndoAction* action) +{ + if (!action) + return; + + // All redo actions are invalid now + clearRedoActions(); + + d->undoActions.push_back(action); + + if (typeid(*action) == typeid(UndoActionIrreversible)) + { + int w = d->dimgiface->origWidth(); + int h = d->dimgiface->origHeight(); + int bytesDepth = d->dimgiface->bytesDepth(); + uchar* data = d->dimgiface->getImage(); + + d->undoCache->putData(d->undoActions.size(), w, h, bytesDepth, data); + } + + // if origin is at one of the redo action that are now invalid, + // it is no longer reachable + if (d->origin < 0) + d->origin = INT_MAX; + else + d->origin++; +} + +void UndoManager::undo() +{ + if (d->undoActions.isEmpty()) + return; + + UndoAction* action = d->undoActions.back(); + + if (typeid(*action) == typeid(UndoActionIrreversible)) + { + // Save the current state for the redo operation + + int w = d->dimgiface->origWidth(); + int h = d->dimgiface->origHeight(); + int bytesDepth = d->dimgiface->bytesDepth(); + uchar* data = d->dimgiface->getImage(); + + d->undoCache->putData(d->undoActions.size() + 1, w, h, bytesDepth, data); + + // And now, undo the action + + int newW, newH, newBytesDepth; + uchar *newData = d->undoCache->getData(d->undoActions.size(), newW, newH, newBytesDepth, false); + if (newData) + { + d->dimgiface->putImage(newData, newW, newH, newBytesDepth == 8 ? true : false); + delete [] newData; + } + } + else + { + action->rollBack(); + } + + d->undoActions.pop_back(); + d->redoActions.push_back(action); + d->origin--; +} + +void UndoManager::redo() +{ + if(d->redoActions.isEmpty()) + return; + + UndoAction *action = d->redoActions.back(); + + if(typeid(*action) == typeid(UndoActionIrreversible)) + { + int w, h, bytesDepth; + uchar *data = d->undoCache->getData(d->undoActions.size() + 2, w, h, bytesDepth, false); + if (data) + { + d->dimgiface->putImage(data, w, h, bytesDepth == 8 ? true : false); + delete[] data; + } + } + else + { + action->execute(); + } + + d->redoActions.pop_back(); + d->undoActions.push_back(action); + d->origin++; +} + +void UndoManager::clear(bool clearCache) +{ + clearUndoActions(); + clearRedoActions(); + setOrigin(); + + if(clearCache) + d->undoCache->clear(); +} + +void UndoManager::clearUndoActions() +{ + UndoAction *action; + TQValueList<UndoAction*>::iterator it; + + for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it) + { + action = *it; + delete action; + } + d->undoActions.clear(); +} + +void UndoManager::clearRedoActions() +{ + if(!anyMoreRedo()) + return; + + UndoAction *action; + TQValueList<UndoAction*>::iterator it; + + // get the level of the first redo action + int level = d->undoActions.size() + 1; + for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it) + { + action = *it; + d->undoCache->erase(level); + delete action; + level++; + } + d->undoCache->erase(level); + d->redoActions.clear(); +} + +bool UndoManager::anyMoreUndo() +{ + return !d->undoActions.isEmpty(); +} + +bool UndoManager::anyMoreRedo() +{ + return !d->redoActions.isEmpty(); +} + +void UndoManager::getUndoHistory(TQStringList &titles) +{ + TQValueList<UndoAction*>::iterator it; + + for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it) + { + titles.push_front((*it)->getTitle()); + } +} + +void UndoManager::getRedoHistory(TQStringList &titles) +{ + TQValueList<UndoAction*>::iterator it; + + for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it) + { + titles.push_front((*it)->getTitle()); + } +} + +bool UndoManager::isAtOrigin() +{ + return d->origin == 0; +} + +void UndoManager::setOrigin() +{ + d->origin = 0; +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undomanager.h b/src/utilities/imageeditor/canvas/undomanager.h new file mode 100644 index 00000000..a36ba12f --- /dev/null +++ b/src/utilities/imageeditor/canvas/undomanager.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : an image editor actions undo/redo manager + * + * Copyright (C) 2005-2006 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2006 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * This program 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 2, or (at your option) + * any later version. + * + * This program 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. + * + * ============================================================ */ + +#ifndef UNDOMANAGER_H +#define UNDOMANAGER_H + +// TQt includes. + +#include <tqstringlist.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgInterface; +class UndoManagerPriv; +class UndoAction; + +class DIGIKAM_EXPORT UndoManager +{ + +public: + + UndoManager(DImgInterface* iface); + ~UndoManager(); + + void undo(); + void redo(); + + void clear(bool clearCache=true); + bool anyMoreUndo(); + bool anyMoreRedo(); + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + bool isAtOrigin(); + void setOrigin(); + + void addAction(UndoAction* action); + +private: + + void clearUndoActions(); + void clearRedoActions(); + +private: + + UndoManagerPriv *d; +}; + +} // namespace Digikam + +#endif /* UNDOMANAGER_H */ |