/* ============================================================ * * 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 * Copyright (C) 2004-2009 by Gilles Caulier * * 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 #include #include #include // TQt includes. #include #include #include #include #include #include #include // KDE includes. #include #include // 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, TQT_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg&)), this, TQT_SLOT(slotImageLoaded(const LoadingDescription &, const DImg&)) ); connect( d->thread, TQT_SIGNAL(signalImageSaved(const TQString&, bool)), this, TQT_SLOT(slotImageSaved(const TQString&, bool)) ); connect( d->thread, TQT_SIGNAL(signalLoadingProgress(const LoadingDescription &, float)), this, TQT_SLOT(slotLoadingProgress(const LoadingDescription &, float)) ); connect( d->thread, TQT_SIGNAL(signalSavingProgress(const TQString&, float)), this, TQT_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, TQT_SIGNAL(okClicked()), this, TQT_SLOT(slotUseRawImportSettings())); connect(rawImport, TQT_SIGNAL(cancelClicked()), this, TQT_SLOT(slotUseDefaultSettings())); } else { slotUseDefaultSettings(); } } void DImgInterface::slotUseRawImportSettings() { RawImport *rawImport = dynamic_cast(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