diff options
| author | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-11-22 18:41:30 +0900 | 
|---|---|---|
| committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-11-22 20:55:03 +0900 | 
| commit | 5bed6e4a4c916a97f8fe4d1b07f7eecf4d733b90 (patch) | |
| tree | f89cc49efc9ca1d0e1579ecb079ee7e7088ff8c8 /src/utilities/imageeditor/canvas | |
| parent | 0bfbf616d9c1fd7abb1bd02732389ab35e5f8771 (diff) | |
| download | digikam-5bed6e4a.tar.gz digikam-5bed6e4a.zip | |
Rename 'digikam' folder to 'src'
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
(cherry picked from commit ee0d99607c14cb63d3ebdb3a970b508949fa8219)
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 */ | 
