diff options
Diffstat (limited to 'src/utilities/imageeditor/canvas/dimginterface.cpp')
-rw-r--r-- | src/utilities/imageeditor/canvas/dimginterface.cpp | 1270 |
1 files changed, 1270 insertions, 0 deletions
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 <[email protected]> + * 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 + |