1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : DImg interface for image editor
9
* Copyright (C) 2004-2005 by Renchi Raju <renchi dot raju at gmail dot com>
10
* Copyright (C) 2004-2012 by Gilles Caulier <caulier dot gilles at gmail dot com>
12
* This program is free software; you can redistribute it
13
* and/or modify it under the terms of the GNU General
14
* Public License as published by the Free Software Foundation;
15
* either version 2, or (at your option)
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* ============================================================ */
25
#include "editorcore.moc"
43
#include <QImageReader>
49
#include <kmessagebox.h>
54
#include "colorcorrectiondlg.h"
55
#include "dimgbuiltinfilter.h"
56
#include "undomanager.h"
57
#include "undoaction.h"
58
#include "undostate.h"
59
#include "iccmanager.h"
60
#include "iccsettingscontainer.h"
61
#include "icctransform.h"
62
#include "exposurecontainer.h"
63
#include "iofilesettings.h"
64
#include "sharedloadsavethread.h"
65
#include "dmetadata.h"
66
#include "rawimport.h"
67
#include "editortooliface.h"
69
#include "dimgfiltergenerator.h"
70
#include "bcgfilter.h"
71
#include "equalizefilter.h"
72
#include "dimgfiltermanager.h"
73
#include "versionmanager.h"
74
#include "editorcore_p.h"
79
EditorCore* EditorCore::m_defaultInstance = 0;
81
EditorCore* EditorCore::defaultInstance()
83
return m_defaultInstance;
86
void EditorCore::setDefaultInstance(EditorCore* const instance)
88
m_defaultInstance = instance;
91
EditorCore::EditorCore()
92
: QObject(), d(new Private)
94
d->undoMan = new UndoManager(this);
95
d->thread = new SharedLoadSaveThread;
97
connect( d->thread, SIGNAL(signalImageLoaded(LoadingDescription, DImg)),
98
this, SLOT(slotImageLoaded(LoadingDescription, DImg)) );
100
connect( d->thread, SIGNAL(signalImageSaved(QString, bool)),
101
this, SLOT(slotImageSaved(QString, bool)) );
103
connect( d->thread, SIGNAL(signalLoadingProgress(LoadingDescription, float)),
104
this, SLOT(slotLoadingProgress(LoadingDescription, float)) );
106
connect( d->thread, SIGNAL(signalSavingProgress(QString, float)),
107
this, SLOT(slotSavingProgress(QString, float)) );
110
EditorCore::~EditorCore()
116
if (m_defaultInstance == this)
118
m_defaultInstance = 0;
122
void EditorCore::setDisplayingWidget(QWidget* const widget)
124
d->displayingWidget = widget;
127
void EditorCore::load(const QString& filePath, IOFileSettings* const iofileSettings)
129
LoadingDescription description(filePath, LoadingDescription::ConvertForEditor);
131
if (DImg::fileFormat(filePath) == DImg::RAW)
133
description = LoadingDescription(filePath, iofileSettings->rawDecodingSettings,
134
LoadingDescription::RawDecodingGlobalSettings,
135
LoadingDescription::ConvertForEditor);
137
if (EditorToolIface::editorToolIface() && iofileSettings->useRAWImport)
139
d->nextRawDescription = description;
141
RawImport* rawImport = new RawImport(KUrl(filePath), this);
142
EditorToolIface::editorToolIface()->loadTool(rawImport);
144
connect(rawImport, SIGNAL(okClicked()),
145
this, SLOT(slotLoadRawFromTool()));
147
connect(rawImport, SIGNAL(cancelClicked()),
148
this, SLOT(slotLoadRaw()));
150
d->thread->stopLoading();
156
d->nextRawDescription = LoadingDescription();
159
d->load(description);
162
void EditorCore::slotLoadRawFromTool()
164
if (EditorToolIface::editorToolIface())
166
RawImport* rawImport = dynamic_cast<RawImport*>(EditorToolIface::editorToolIface()->currentTool());
170
d->nextRawDescription.rawDecodingSettings = rawImport->rawDecodingSettings();
171
d->nextRawDescription.rawDecodingHint = LoadingDescription::RawDecodingCustomSettings;
174
if (rawImport->hasPostProcessedImage())
177
d->currentDescription = d->nextRawDescription;
178
d->nextRawDescription = LoadingDescription();
180
emit signalLoadingStarted(d->currentDescription.filePath);
181
slotImageLoaded(d->currentDescription, rawImport->postProcessedImage());
182
EditorToolIface::editorToolIface()->unLoadTool();
183
emit signalImageLoaded(d->currentDescription.filePath, true);
192
void EditorCore::slotLoadRaw()
194
// kDebug() << d->nextRawDescription.rawDecodingSettings;
195
d->load(d->nextRawDescription);
196
d->nextRawDescription = LoadingDescription();
199
void EditorCore::applyTransform(const IccTransform& transform)
206
d->currentDescription.postProcessingParameters.colorManagement = LoadingDescription::ApplyTransform;
207
d->currentDescription.postProcessingParameters.setTransform(transform);
210
if (EditorToolIface::editorToolIface())
212
EditorToolIface::editorToolIface()->unLoadTool();
216
void EditorCore::restore()
218
LoadingDescription description = d->currentDescription;
220
d->load(description);
223
void EditorCore::resetImage()
225
if (EditorToolIface::editorToolIface())
227
EditorToolIface::editorToolIface()->unLoadTool();
234
void EditorCore::setICCSettings(const ICCSettingsContainer& cmSettings)
236
d->cmSettings = cmSettings;
239
ICCSettingsContainer EditorCore::getICCSettings() const
241
return d->cmSettings;
244
void EditorCore::setExposureSettings(ExposureSettingsContainer* const expoSettings)
246
d->expoSettings = expoSettings;
249
ExposureSettingsContainer* EditorCore::getExposureSettings() const
251
return d->expoSettings;
254
void EditorCore::slotImageLoaded(const LoadingDescription& loadingDescription, const DImg& img)
256
if (loadingDescription != d->currentDescription)
261
// RAW tool active? Discard previous loaded image
262
if (!d->nextRawDescription.filePath.isNull())
270
if (!d->image.isNull())
274
d->resolvedInitialHistory = d->image.getOriginalImageHistory();
275
d->resolvedInitialHistory.clearReferredImages(); // default empty, real values set by higher level
277
// Raw files are already rotated properly by dcraw. Only perform auto-rotation with non-RAW files.
278
// We don't have a feedback from dcraw about auto-rotated RAW file during decoding.
279
// Setting rotatedOrFlipped to true will reset the exif flag on save (the data is then already rotated)
281
if (d->image.detectedFormat() == DImg::RAW)
283
d->rotatedOrFlipped = true;
285
else if (d->exifOrient)
287
// Do not rotate twice if already rotated, e.g. for full size preview.
288
QVariant attribute(d->image.attribute("exifRotated"));
290
if (!attribute.isValid() || !attribute.toBool())
292
d->rotatedOrFlipped = d->image.rotateAndFlip(LoadSaveThread::exifOrientation(d->image, loadingDescription.filePath));
296
// set after rotation
297
d->origWidth = d->image.width();
298
d->origHeight = d->image.height();
299
d->width = d->origWidth;
300
d->height = d->origHeight;
302
updateColorManagement();
309
emit signalImageLoaded(d->currentDescription.filePath, valRet);
313
* TODO: FilterManager test block -- to be removed later
315
FilterAction fa("digikam:BCGFilter", 1);
316
fa.addParameter("contrast", 1);
317
fa.addParameter("channel", 1);
318
fa.addParameter("brightness", 1);
319
fa.addParameter("gamma", 1.2);
321
DImgThreadedFilter *f = DImgFilterManager::instance()->createFilter("digikam:BCGFilter", 1);
322
f->readParameters(fa);
324
f->startFilterDirectly();
329
void EditorCore::updateColorManagement()
331
IccManager manager(d->image);
333
if (d->doSoftProofing)
335
d->monitorICCtrans = manager.displaySoftProofingTransform(d->cmSettings.defaultProofProfile, d->displayingWidget);
339
d->monitorICCtrans = manager.displayTransform(d->displayingWidget);
343
void EditorCore::setSoftProofingEnabled(bool enabled)
345
d->doSoftProofing = enabled;
346
updateColorManagement();
349
void EditorCore::slotLoadingProgress(const LoadingDescription& loadingDescription, float progress)
351
if (loadingDescription == d->currentDescription)
353
emit signalLoadingProgress(loadingDescription.filePath, progress);
357
bool EditorCore::exifRotated() const
359
return d->rotatedOrFlipped;
362
void EditorCore::setExifOrient(bool exifOrient)
364
d->exifOrient = exifOrient;
367
void EditorCore::undo()
369
if (!d->undoMan->anyMoreUndo())
371
emit signalUndoStateChanged();
376
emit signalUndoStateChanged();
379
void EditorCore::redo()
381
if (!d->undoMan->anyMoreRedo())
383
emit signalUndoStateChanged();
388
emit signalUndoStateChanged();
391
void EditorCore::rollbackToOrigin()
393
d->undoMan->rollbackToOrigin();
394
emit signalUndoStateChanged();
397
void EditorCore::saveAs(const QString& filePath, IOFileSettings* const iofileSettings,
398
bool setExifOrientationTag, const QString& givenMimeType,
399
const QString& intendedFilePath)
401
d->saveAs(filePath, iofileSettings, setExifOrientationTag, givenMimeType,
402
VersionFileOperation(), intendedFilePath);
405
void EditorCore::saveAs(const QString& filePath, IOFileSettings* const iofileSettings,
406
bool setExifOrientationTag, const QString& givenMimeType,
407
const VersionFileOperation& op)
409
d->saveAs(filePath, iofileSettings, setExifOrientationTag, givenMimeType, op, op.saveFile.filePath());
412
void EditorCore::slotImageSaved(const QString& filePath, bool success)
414
if (d->filesToSave.isEmpty() || d->filesToSave[d->currentFileToSave].filePath != filePath)
419
Private::FileToSave& savedFile = d->filesToSave[d->currentFileToSave];
423
if (savedFile.historyStep == -1)
425
// Note: We operate on a temp file here, so we cannot
426
// add it as referred image yet. Done in addLastSavedToHistory
427
LoadingDescription description(filePath, LoadingDescription::ConvertForEditor);
428
d->currentDescription = description;
432
HistoryImageId id = savedFile.image.addAsReferredImage(filePath);
434
// for all images following in history, we need to insert the now saved file at the right place
435
for (int i = d->currentFileToSave + 1; i < d->filesToSave.size(); ++i)
437
d->filesToSave[i].image.insertAsReferredImage(savedFile.historyStep, id);
443
kWarning() << "error saving image '" << QFile::encodeName(filePath).data();
446
d->currentFileToSave++;
448
if (d->currentFileToSave == d->filesToSave.size())
450
d->filesToSave.clear();
451
emit signalImageSaved(filePath, success);
459
void EditorCore::slotSavingProgress(const QString& filePath, float progress)
461
if (!d->filesToSave.isEmpty() && d->filesToSave.at(d->currentFileToSave).filePath == filePath)
463
emit signalSavingProgress(filePath, progress);
467
void EditorCore::abortSaving()
469
// failure will be reported by a signal
470
if (!d->filesToSave.isEmpty())
472
d->thread->stopSaving(d->filesToSave.at(d->currentFileToSave).filePath);
473
d->filesToSave.clear();
477
QString EditorCore::ensureHasCurrentUuid() const
480
* 1) An image is loaded. The DImgLoader adds the HistoryImageId of the loaded file as "Current" entry.
481
* 2) The loaded image has no UUID (created by camera etc.). Highler level calls ensureHasCurrentUuid
482
* before any saving is started
483
* 3) We create a new UUID and add it to the image's history. When the new image is saved,
484
* it references the original by UUID. Because we, here, do not touch the original,
485
* it is out of scope to add the UUID to the original file's metadata.
486
* Higher level is responsible for this.
487
* 4) When the image is saved, DImg::updateMetadata will create a new UUID for the saved
488
* image, which is then of course written to the newly saved file.
490
if (!d->image.getImageHistory().currentReferredImage().hasUuid())
492
// if there is no uuid in the image, we create one.
493
QString uuid = d->image.createImageUniqueId();
494
d->image.addCurrentUniqueImageId(uuid);
497
return d->image.getImageHistory().currentReferredImage().uuid();
500
void EditorCore::provideCurrentUuid(const QString& uuid)
502
// If the (original) image did not yet have a UUID, one is provided by higher level
503
// Higher level decides how this UUID is stored; we dont touch the original here.
504
if (!d->image.getImageHistory().currentReferredImage().hasUuid())
506
d->image.addCurrentUniqueImageId(uuid);
510
void EditorCore::setLastSaved(const QString& filePath)
512
if (getImageFilePath() == filePath)
514
// if the file was overwritten, a complete undo, to the state of original loading,
515
// does not return to a real image anymore - it's overwritten
516
d->undoMan->clearPreviousOriginData();
519
// We cannot do it in slotImageSaved because we may operate on a temporary filePath.
520
d->image.imageSavedAs(filePath);
523
void EditorCore::switchToLastSaved(const DImageHistory& resolvedCurrentHistory)
525
// Higher level wants to use the current DImg object to represent the file
526
// it has previously been saved to.
527
// setLastSaved shall have been called before.
528
d->image.switchOriginToLastSaved();
530
if (resolvedCurrentHistory.isNull())
532
d->resolvedInitialHistory = d->image.getOriginalImageHistory();
533
d->resolvedInitialHistory.clearReferredImages();
537
d->resolvedInitialHistory = resolvedCurrentHistory;
540
setUndoManagerOrigin();
543
void EditorCore::setHistoryIsBranch(bool isBranching)
545
// The first added step (on top of the initial history) will be marked as branch
546
d->image.setHistoryBranchAfter(d->resolvedInitialHistory, isBranching);
549
void EditorCore::setModified()
551
emit signalModified();
552
emit signalUndoStateChanged();
555
void EditorCore::readMetadataFromFile(const QString& file)
557
DMetadata meta(file);
558
// This can overwrite metadata changes introduced by imageplugins.
559
// Currently, this is ProfileConversion and lensfun.
560
// ProfileConversion's changes is redone when saving by DImgLoader.
561
// Lensfun is not critical.
562
// For a clean solution, we'd need to record a sort of metadata changeset in UndoMetadataContainer.
563
d->image.setMetadata(meta.data());
564
// If we are editing, and someone else at the same time, there's nothing we can do.
565
if (!d->undoMan->hasChanges())
567
d->image.setImageHistory(DImageHistory::fromXml(meta.getImageHistory()));
571
void EditorCore::clearUndoManager()
574
d->undoMan->setOrigin();
575
emit signalUndoStateChanged();
578
void EditorCore::setUndoManagerOrigin()
580
d->undoMan->setOrigin();
581
emit signalUndoStateChanged();
582
emit signalFileOriginChanged(getImageFilePath());
585
bool EditorCore::imageValid() const
590
int EditorCore::width() const
595
int EditorCore::height() const
600
int EditorCore::origWidth() const
605
int EditorCore::origHeight() const
607
return d->origHeight;
610
int EditorCore::bytesDepth() const
612
return d->image.bytesDepth();
615
bool EditorCore::sixteenBit() const
617
return d->image.sixteenBit();
620
bool EditorCore::hasAlpha() const
622
return d->image.hasAlpha();
625
bool EditorCore::isReadOnly() const
627
if (d->image.isNull())
633
return d->image.isReadOnly();
637
void EditorCore::setSelectedArea(const QRect& rect)
641
d->selW = rect.width();
642
d->selH = rect.height();
645
QRect EditorCore::getSelectedArea() const
647
return (QRect(d->selX, d->selY, d->selW, d->selH));
650
void EditorCore::paintOnDevice(QPaintDevice* const p,
655
if (d->image.isNull())
660
DImg img = d->image.smoothScaleSection(src, dst.size());
661
img.convertDepth(32);
664
if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
666
QPixmap pix(img.convertToPixmap(d->monitorICCtrans));
667
painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
671
QPixmap pix(img.convertToPixmap());
672
painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
675
// Show the Over/Under exposure pixels indicators
677
if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
679
QImage pureColorMask = img.pureColorMask(d->expoSettings);
680
QPixmap pixMask = QPixmap::fromImage(pureColorMask);
681
painter.drawPixmap(dst.topLeft(), pixMask, QRect(0, 0, pixMask.width(), pixMask.height()));
687
void EditorCore::paintOnDevice(QPaintDevice* const p,
693
if (d->image.isNull())
698
DImg img = d->image.smoothScaleSection(src, dst.size());
699
img.convertDepth(32);
702
uint* data = (uint*)img.bits();
705
for (int j = 0; j < (int)img.height(); ++j)
707
for (int i = 0; i < (int)img.width(); ++i)
709
if (i < (mrt.x() - dst.x()) || i > (mrt.x() - dst.x() + mrt.width() - 1) ||
710
j < (mrt.y() - dst.y()) || j > (mrt.y() - dst.y() + mrt.height() - 1))
712
a = (*data >> 24) & 0xff;
713
r = (*data >> 16) & 0xff;
714
g = (*data >> 8) & 0xff;
717
r += (uchar)((RCOL - r) * OPACITY);
718
g += (uchar)((GCOL - g) * OPACITY);
719
b += (uchar)((BCOL - b) * OPACITY);
721
*data = (a << 24) | (r << 16) | (g << 8) | b;
728
if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
730
QPixmap pix(img.convertToPixmap(d->monitorICCtrans));
731
painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
735
QPixmap pix(img.convertToPixmap());
736
painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
739
// Show the Over/Under exposure pixels indicators
741
if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
743
QImage pureColorMask = img.pureColorMask(d->expoSettings);
744
QPixmap pixMask = QPixmap::fromImage(pureColorMask);
745
painter.drawPixmap(dst.topLeft(), pixMask, QRect(0, 0, pixMask.width(), pixMask.height()));
751
void EditorCore::zoom(double val)
754
d->width = (int)(d->origWidth * val);
755
d->height = (int)(d->origHeight * val);
758
void EditorCore::rotate90()
760
d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate90));
763
void EditorCore::rotate180()
765
d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate180));
768
void EditorCore::rotate270()
770
d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate270));
773
void EditorCore::flipHoriz()
775
d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::FlipHorizontally));
778
void EditorCore::flipVert()
780
d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::FlipVertically));
783
void EditorCore::crop(const QRect& rect)
785
d->applyBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Crop, rect), new UndoActionIrreversible(this, "Crop"));
788
void EditorCore::convertDepth(int depth)
790
d->applyBuiltinFilter(DImgBuiltinFilter(depth == 32 ? DImgBuiltinFilter::ConvertTo8Bit : DImgBuiltinFilter::ConvertTo16Bit),
791
new UndoActionIrreversible(this, "Convert Color Depth"));
794
DImg* EditorCore::getImg() const
796
if (!d->image.isNull())
802
kWarning() << "d->image is NULL";
807
DImageHistory EditorCore::getImageHistory() const
809
return d->image.getImageHistory();
812
DImageHistory EditorCore::getInitialImageHistory() const
814
return d->image.getOriginalImageHistory();
817
DImageHistory EditorCore::getImageHistoryOfFullRedo() const
819
return d->undoMan->getImageHistoryOfFullRedo();
822
DImageHistory EditorCore::getResolvedInitialHistory() const
824
return d->resolvedInitialHistory;
827
void EditorCore::setResolvedInitialHistory(const DImageHistory& history)
829
d->resolvedInitialHistory = history;
832
void EditorCore::putImg(const QString& caller, const FilterAction& action, const DImg& img)
834
d->undoMan->addAction(new UndoActionIrreversible(this, caller));
835
d->putImageData(img.bits(), img.width(), img.height(), img.sixteenBit());
836
d->image.addFilterAction(action);
840
void EditorCore::setUndoImg(const UndoMetadataContainer& c, const DImg& img)
842
// called from UndoManager
843
bool changesIcc = c.changesIccProfile(d->image);
845
d->putImageData(img.bits(), img.width(), img.height(), img.sixteenBit());
850
updateColorManagement();
854
void EditorCore::imageUndoChanged(const UndoMetadataContainer& c)
856
// called from UndoManager
857
bool changesIcc = c.changesIccProfile(d->image);
859
d->origWidth = d->image.width();
860
d->origHeight = d->image.height();
865
updateColorManagement();
869
void EditorCore::setFileOriginData(const QVariant& data)
871
d->image.setFileOriginData(data);
872
emit signalFileOriginChanged(getImageFilePath());
875
DImg EditorCore::getImgSelection() const
877
if (!d->selW || !d->selH)
882
if (!d->image.isNull())
884
DImg im = d->image.copy(d->selX, d->selY, d->selW, d->selH);
892
void EditorCore::putImgSelection(const QString& caller, const FilterAction& action, const DImg& img)
894
if (img.isNull() || d->image.isNull())
899
d->undoMan->addAction(new UndoActionIrreversible(this, caller));
901
d->image.bitBltImage(img.bits(), 0, 0, d->selW, d->selH, d->selX, d->selY, d->selW, d->selH, d->image.bytesDepth());
903
d->image.addFilterAction(action);
907
void EditorCore::putIccProfile(const IccProfile& profile)
909
if (d->image.isNull())
911
kWarning() << "d->image is NULL";
915
//kDebug() << "Embedding profile: " << profile;
916
d->image.setIccProfile(profile);
917
updateColorManagement();
921
QStringList EditorCore::getUndoHistory() const
923
return d->undoMan->getUndoHistory();
926
QStringList EditorCore::getRedoHistory() const
928
return d->undoMan->getRedoHistory();
931
int EditorCore::availableUndoSteps() const
933
return d->undoMan->availableUndoSteps();
936
int EditorCore::availableRedoSteps() const
938
return d->undoMan->availableRedoSteps();
941
IccProfile EditorCore::getEmbeddedICC() const
943
return d->image.getIccProfile();
946
KExiv2Data EditorCore::getMetadata() const
948
return d->image.getMetadata();
951
QString EditorCore::getImageFilePath() const
953
return d->image.originalFilePath();
956
QString EditorCore::getImageFileName() const
958
return getImageFilePath().section('/', -1);
961
QString EditorCore::getImageFormat() const
963
if (d->image.isNull())
968
QString mimeType = d->image.format();
970
// It is a bug in the loader if format attribute is not given
971
if (mimeType.isEmpty())
973
kWarning() << "DImg object does not contain attribute \"format\"";
974
mimeType = QImageReader::imageFormat(getImageFilePath());
980
QPixmap EditorCore::convertToPixmap(DImg& img) const
984
if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
986
// do not use d->monitorICCtrans here, because img may have a different embedded profile
987
IccManager manager(img);
988
IccTransform transform;
990
if (d->doSoftProofing)
992
transform = manager.displaySoftProofingTransform(d->cmSettings.defaultProofProfile, d->displayingWidget);
996
transform = manager.displayTransform(d->displayingWidget);
999
pix = img.convertToPixmap(transform);
1003
pix = img.convertToPixmap();
1006
// Show the Over/Under exposure pixels indicators
1008
if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
1010
QPainter painter(&pix);
1011
QImage pureColorMask = img.pureColorMask(d->expoSettings);
1012
QPixmap pixMask = QPixmap::fromImage(pureColorMask);
1013
painter.drawPixmap(0, 0, pixMask, 0, 0, pixMask.width(), pixMask.height());
1019
UndoState EditorCore::undoState() const
1022
state.hasUndo = d->undoMan->anyMoreUndo();
1023
state.hasRedo = d->undoMan->anyMoreRedo();
1024
state.hasUndoableChanges = !d->undoMan->isAtOrigin();
1025
// Includes the edit step performed by RAW import, which is not undoable
1026
state.hasChanges = d->undoMan->hasChanges();
1031
} // namespace Digikam