~ubuntu-branches/ubuntu/trusty/digikam/trusty

« back to all changes in this revision

Viewing changes to core/utilities/imageeditor/core/editorcore.cpp

  • Committer: Package Import Robot
  • Author(s): Rohan Garg
  • Date: 2012-11-26 18:24:20 UTC
  • mfrom: (1.9.1) (3.1.23 experimental)
  • Revision ID: package-import@ubuntu.com-20121126182420-qoy6z0nx4ai0wzcl
Tags: 4:3.0.0~beta3-0ubuntu1
* New upstream release
  - Add build-deps :  libhupnp-dev, libqtgstreamer-dev, libmagickcore-dev
* Merge from debian, remaining changes:
  - Make sure libqt4-opengl-dev, libgl1-mesa-dev and libglu1-mesa-dev only
    install on i386,amd64 and powerpc
  - Depend on libtiff-dev instead of libtiff4-dev
  - Drop digikam breaks/replaces kipi-plugins-common since we're past the
    LTS release now
  - digikam to recommend mplayerthumbs | ffmpegthumbs. We currently only
    have latter in the archives, even though former is also supposed to
    be part of kdemultimedia. (LP: #890059)
  - kipi-plugins to recommend www-browser rather than konqueror directly
    since 2.8 no direct usage of konqueror is present in the flickr
    plugin anymore (LP: #1011211)
  - Keep kubuntu_mysqld_executable_name.diff
  - Don't install libkipi translations
  - Keep deps on libcv-dev, libcvaux-dev
  - Keep split packaging of libraries
  - Replace icons from KDE 3 time in debian/xpm.d/*.xpm with the new
    versions (LP: #658047)
* Update debian/not-installed

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ============================================================
 
2
 *
 
3
 * This file is a part of digiKam project
 
4
 * http://www.digikam.org
 
5
 *
 
6
 * Date        : 2003-01-15
 
7
 * Description : DImg interface for image editor
 
8
 *
 
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>
 
11
 *
 
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)
 
16
 * any later version.
 
17
 *
 
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.
 
22
 *
 
23
 * ============================================================ */
 
24
 
 
25
#include "editorcore.moc"
 
26
 
 
27
// C++ includes
 
28
 
 
29
#include <cmath>
 
30
#include <cstdio>
 
31
#include <cstdlib>
 
32
#include <iostream>
 
33
 
 
34
// Qt includes
 
35
 
 
36
#include <QWidget>
 
37
#include <QImage>
 
38
#include <QPixmap>
 
39
#include <QBitmap>
 
40
#include <QColor>
 
41
#include <QFile>
 
42
#include <QVariant>
 
43
#include <QImageReader>
 
44
#include <QPainter>
 
45
 
 
46
// KDE includes
 
47
 
 
48
#include <kcursor.h>
 
49
#include <kmessagebox.h>
 
50
#include <kdebug.h>
 
51
 
 
52
// Local includes
 
53
 
 
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"
 
68
#include "dimg.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"
 
75
 
 
76
namespace Digikam
 
77
{
 
78
 
 
79
EditorCore* EditorCore::m_defaultInstance = 0;
 
80
 
 
81
EditorCore* EditorCore::defaultInstance()
 
82
{
 
83
    return m_defaultInstance;
 
84
}
 
85
 
 
86
void EditorCore::setDefaultInstance(EditorCore* const instance)
 
87
{
 
88
    m_defaultInstance = instance;
 
89
}
 
90
 
 
91
EditorCore::EditorCore()
 
92
    : QObject(), d(new Private)
 
93
{
 
94
    d->undoMan = new UndoManager(this);
 
95
    d->thread  = new SharedLoadSaveThread;
 
96
 
 
97
    connect( d->thread, SIGNAL(signalImageLoaded(LoadingDescription, DImg)),
 
98
             this, SLOT(slotImageLoaded(LoadingDescription, DImg)) );
 
99
 
 
100
    connect( d->thread, SIGNAL(signalImageSaved(QString, bool)),
 
101
             this, SLOT(slotImageSaved(QString, bool)) );
 
102
 
 
103
    connect( d->thread, SIGNAL(signalLoadingProgress(LoadingDescription, float)),
 
104
             this, SLOT(slotLoadingProgress(LoadingDescription, float)) );
 
105
 
 
106
    connect( d->thread, SIGNAL(signalSavingProgress(QString, float)),
 
107
             this, SLOT(slotSavingProgress(QString, float)) );
 
108
}
 
109
 
 
110
EditorCore::~EditorCore()
 
111
{
 
112
    delete d->undoMan;
 
113
    delete d->thread;
 
114
    delete d;
 
115
 
 
116
    if (m_defaultInstance == this)
 
117
    {
 
118
        m_defaultInstance = 0;
 
119
    }
 
120
}
 
121
 
 
122
void EditorCore::setDisplayingWidget(QWidget* const widget)
 
123
{
 
124
    d->displayingWidget = widget;
 
125
}
 
126
 
 
127
void EditorCore::load(const QString& filePath, IOFileSettings* const iofileSettings)
 
128
{
 
129
    LoadingDescription description(filePath, LoadingDescription::ConvertForEditor);
 
130
 
 
131
    if (DImg::fileFormat(filePath) == DImg::RAW)
 
132
    {
 
133
        description = LoadingDescription(filePath, iofileSettings->rawDecodingSettings,
 
134
                                         LoadingDescription::RawDecodingGlobalSettings,
 
135
                                         LoadingDescription::ConvertForEditor);
 
136
 
 
137
        if (EditorToolIface::editorToolIface() && iofileSettings->useRAWImport)
 
138
        {
 
139
            d->nextRawDescription = description;
 
140
 
 
141
            RawImport* rawImport = new RawImport(KUrl(filePath), this);
 
142
            EditorToolIface::editorToolIface()->loadTool(rawImport);
 
143
 
 
144
            connect(rawImport, SIGNAL(okClicked()),
 
145
                    this, SLOT(slotLoadRawFromTool()));
 
146
 
 
147
            connect(rawImport, SIGNAL(cancelClicked()),
 
148
                    this, SLOT(slotLoadRaw()));
 
149
 
 
150
            d->thread->stopLoading();
 
151
            return;
 
152
        }
 
153
    }
 
154
    else
 
155
    {
 
156
        d->nextRawDescription = LoadingDescription();
 
157
    }
 
158
 
 
159
    d->load(description);
 
160
}
 
161
 
 
162
void EditorCore::slotLoadRawFromTool()
 
163
{
 
164
    if (EditorToolIface::editorToolIface())
 
165
    {
 
166
        RawImport* rawImport = dynamic_cast<RawImport*>(EditorToolIface::editorToolIface()->currentTool());
 
167
 
 
168
        if (rawImport)
 
169
        {
 
170
            d->nextRawDescription.rawDecodingSettings = rawImport->rawDecodingSettings();
 
171
            d->nextRawDescription.rawDecodingHint     = LoadingDescription::RawDecodingCustomSettings;
 
172
        }
 
173
 
 
174
        if (rawImport->hasPostProcessedImage())
 
175
        {
 
176
            d->resetValues();
 
177
            d->currentDescription = d->nextRawDescription;
 
178
            d->nextRawDescription = LoadingDescription();
 
179
 
 
180
            emit signalLoadingStarted(d->currentDescription.filePath);
 
181
            slotImageLoaded(d->currentDescription, rawImport->postProcessedImage());
 
182
            EditorToolIface::editorToolIface()->unLoadTool();
 
183
            emit signalImageLoaded(d->currentDescription.filePath, true);
 
184
        }
 
185
        else
 
186
        {
 
187
            slotLoadRaw();
 
188
        }
 
189
    }
 
190
}
 
191
 
 
192
void EditorCore::slotLoadRaw()
 
193
{
 
194
    //    kDebug() << d->nextRawDescription.rawDecodingSettings;
 
195
    d->load(d->nextRawDescription);
 
196
    d->nextRawDescription = LoadingDescription();
 
197
}
 
198
 
 
199
void EditorCore::applyTransform(const IccTransform& transform)
 
200
{
 
201
    if (!d->valid)
 
202
    {
 
203
        return;
 
204
    }
 
205
 
 
206
    d->currentDescription.postProcessingParameters.colorManagement = LoadingDescription::ApplyTransform;
 
207
    d->currentDescription.postProcessingParameters.setTransform(transform);
 
208
    d->loadCurrent();
 
209
 
 
210
    if (EditorToolIface::editorToolIface())
 
211
    {
 
212
        EditorToolIface::editorToolIface()->unLoadTool();
 
213
    }
 
214
}
 
215
 
 
216
void EditorCore::restore()
 
217
{
 
218
    LoadingDescription description = d->currentDescription;
 
219
    d->resetValues();
 
220
    d->load(description);
 
221
}
 
222
 
 
223
void EditorCore::resetImage()
 
224
{
 
225
    if (EditorToolIface::editorToolIface())
 
226
    {
 
227
        EditorToolIface::editorToolIface()->unLoadTool();
 
228
    }
 
229
 
 
230
    d->resetValues();
 
231
    d->image.reset();
 
232
}
 
233
 
 
234
void EditorCore::setICCSettings(const ICCSettingsContainer& cmSettings)
 
235
{
 
236
    d->cmSettings = cmSettings;
 
237
}
 
238
 
 
239
ICCSettingsContainer EditorCore::getICCSettings() const
 
240
{
 
241
    return d->cmSettings;
 
242
}
 
243
 
 
244
void EditorCore::setExposureSettings(ExposureSettingsContainer* const expoSettings)
 
245
{
 
246
    d->expoSettings = expoSettings;
 
247
}
 
248
 
 
249
ExposureSettingsContainer* EditorCore::getExposureSettings() const
 
250
{
 
251
    return d->expoSettings;
 
252
}
 
253
 
 
254
void EditorCore::slotImageLoaded(const LoadingDescription& loadingDescription, const DImg& img)
 
255
{
 
256
    if (loadingDescription != d->currentDescription)
 
257
    {
 
258
        return;
 
259
    }
 
260
 
 
261
    // RAW tool active? Discard previous loaded image
 
262
    if (!d->nextRawDescription.filePath.isNull())
 
263
    {
 
264
        return;
 
265
    }
 
266
 
 
267
    bool valRet = false;
 
268
    d->image    = img;
 
269
 
 
270
    if (!d->image.isNull())
 
271
    {
 
272
        d->valid      = true;
 
273
        valRet        = true;
 
274
        d->resolvedInitialHistory = d->image.getOriginalImageHistory();
 
275
        d->resolvedInitialHistory.clearReferredImages(); // default empty, real values set by higher level
 
276
 
 
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)
 
280
 
 
281
        if (d->image.detectedFormat() == DImg::RAW)
 
282
        {
 
283
            d->rotatedOrFlipped = true;
 
284
        }
 
285
        else if (d->exifOrient)
 
286
        {
 
287
            // Do not rotate twice if already rotated, e.g. for full size preview.
 
288
            QVariant attribute(d->image.attribute("exifRotated"));
 
289
 
 
290
            if (!attribute.isValid() || !attribute.toBool())
 
291
            {
 
292
                d->rotatedOrFlipped = d->image.rotateAndFlip(LoadSaveThread::exifOrientation(d->image, loadingDescription.filePath));
 
293
            }
 
294
        }
 
295
 
 
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;
 
301
 
 
302
        updateColorManagement();
 
303
    }
 
304
    else
 
305
    {
 
306
        valRet = false;
 
307
    }
 
308
 
 
309
    emit signalImageLoaded(d->currentDescription.filePath, valRet);
 
310
    setModified();
 
311
 
 
312
    /*
 
313
     *  TODO: FilterManager test block -- to be removed later
 
314
     *
 
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);
 
320
 
 
321
        DImgThreadedFilter *f =  DImgFilterManager::instance()->createFilter("digikam:BCGFilter", 1);
 
322
        f->readParameters(fa);
 
323
        f->setupFilter(img);
 
324
        f->startFilterDirectly();
 
325
        delete f;
 
326
    */
 
327
}
 
328
 
 
329
void EditorCore::updateColorManagement()
 
330
{
 
331
    IccManager manager(d->image);
 
332
 
 
333
    if (d->doSoftProofing)
 
334
    {
 
335
        d->monitorICCtrans = manager.displaySoftProofingTransform(d->cmSettings.defaultProofProfile, d->displayingWidget);
 
336
    }
 
337
    else
 
338
    {
 
339
        d->monitorICCtrans = manager.displayTransform(d->displayingWidget);
 
340
    }
 
341
}
 
342
 
 
343
void EditorCore::setSoftProofingEnabled(bool enabled)
 
344
{
 
345
    d->doSoftProofing = enabled;
 
346
    updateColorManagement();
 
347
}
 
348
 
 
349
void EditorCore::slotLoadingProgress(const LoadingDescription& loadingDescription, float progress)
 
350
{
 
351
    if (loadingDescription == d->currentDescription)
 
352
    {
 
353
        emit signalLoadingProgress(loadingDescription.filePath, progress);
 
354
    }
 
355
}
 
356
 
 
357
bool EditorCore::exifRotated() const
 
358
{
 
359
    return d->rotatedOrFlipped;
 
360
}
 
361
 
 
362
void EditorCore::setExifOrient(bool exifOrient)
 
363
{
 
364
    d->exifOrient = exifOrient;
 
365
}
 
366
 
 
367
void EditorCore::undo()
 
368
{
 
369
    if (!d->undoMan->anyMoreUndo())
 
370
    {
 
371
        emit signalUndoStateChanged();
 
372
        return;
 
373
    }
 
374
 
 
375
    d->undoMan->undo();
 
376
    emit signalUndoStateChanged();
 
377
}
 
378
 
 
379
void EditorCore::redo()
 
380
{
 
381
    if (!d->undoMan->anyMoreRedo())
 
382
    {
 
383
        emit signalUndoStateChanged();
 
384
        return;
 
385
    }
 
386
 
 
387
    d->undoMan->redo();
 
388
    emit signalUndoStateChanged();
 
389
}
 
390
 
 
391
void EditorCore::rollbackToOrigin()
 
392
{
 
393
    d->undoMan->rollbackToOrigin();
 
394
    emit signalUndoStateChanged();
 
395
}
 
396
 
 
397
void EditorCore::saveAs(const QString& filePath, IOFileSettings* const iofileSettings,
 
398
                           bool setExifOrientationTag, const QString& givenMimeType,
 
399
                           const QString& intendedFilePath)
 
400
{
 
401
    d->saveAs(filePath, iofileSettings, setExifOrientationTag, givenMimeType,
 
402
              VersionFileOperation(), intendedFilePath);
 
403
}
 
404
 
 
405
void EditorCore::saveAs(const QString& filePath, IOFileSettings* const iofileSettings,
 
406
                           bool setExifOrientationTag, const QString& givenMimeType,
 
407
                           const VersionFileOperation& op)
 
408
{
 
409
    d->saveAs(filePath, iofileSettings, setExifOrientationTag, givenMimeType, op, op.saveFile.filePath());
 
410
}
 
411
 
 
412
void EditorCore::slotImageSaved(const QString& filePath, bool success)
 
413
{
 
414
    if (d->filesToSave.isEmpty() || d->filesToSave[d->currentFileToSave].filePath != filePath)
 
415
    {
 
416
        return;
 
417
    }
 
418
 
 
419
    Private::FileToSave& savedFile = d->filesToSave[d->currentFileToSave];
 
420
 
 
421
    if (success)
 
422
    {
 
423
        if (savedFile.historyStep == -1)
 
424
        {
 
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;
 
429
        }
 
430
        else
 
431
        {
 
432
            HistoryImageId id = savedFile.image.addAsReferredImage(filePath);
 
433
 
 
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)
 
436
            {
 
437
                d->filesToSave[i].image.insertAsReferredImage(savedFile.historyStep, id);
 
438
            }
 
439
        }
 
440
    }
 
441
    else
 
442
    {
 
443
        kWarning() << "error saving image '" << QFile::encodeName(filePath).data();
 
444
    }
 
445
 
 
446
    d->currentFileToSave++;
 
447
 
 
448
    if (d->currentFileToSave == d->filesToSave.size())
 
449
    {
 
450
        d->filesToSave.clear();
 
451
        emit signalImageSaved(filePath, success);
 
452
    }
 
453
    else
 
454
    {
 
455
        d->saveNext();
 
456
    }
 
457
}
 
458
 
 
459
void EditorCore::slotSavingProgress(const QString& filePath, float progress)
 
460
{
 
461
    if (!d->filesToSave.isEmpty() && d->filesToSave.at(d->currentFileToSave).filePath == filePath)
 
462
    {
 
463
        emit signalSavingProgress(filePath, progress);
 
464
    }
 
465
}
 
466
 
 
467
void EditorCore::abortSaving()
 
468
{
 
469
    // failure will be reported by a signal
 
470
    if (!d->filesToSave.isEmpty())
 
471
    {
 
472
        d->thread->stopSaving(d->filesToSave.at(d->currentFileToSave).filePath);
 
473
        d->filesToSave.clear();
 
474
    }
 
475
}
 
476
 
 
477
QString EditorCore::ensureHasCurrentUuid() const
 
478
{
 
479
    /*
 
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.
 
489
     */
 
490
    if (!d->image.getImageHistory().currentReferredImage().hasUuid())
 
491
    {
 
492
        // if there is no uuid in the image, we create one.
 
493
        QString uuid = d->image.createImageUniqueId();
 
494
        d->image.addCurrentUniqueImageId(uuid);
 
495
    }
 
496
 
 
497
    return d->image.getImageHistory().currentReferredImage().uuid();
 
498
}
 
499
 
 
500
void EditorCore::provideCurrentUuid(const QString& uuid)
 
501
{
 
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())
 
505
    {
 
506
        d->image.addCurrentUniqueImageId(uuid);
 
507
    }
 
508
}
 
509
 
 
510
void EditorCore::setLastSaved(const QString& filePath)
 
511
{
 
512
    if (getImageFilePath() == filePath)
 
513
    {
 
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();
 
517
    }
 
518
 
 
519
    // We cannot do it in slotImageSaved because we may operate on a temporary filePath.
 
520
    d->image.imageSavedAs(filePath);
 
521
}
 
522
 
 
523
void EditorCore::switchToLastSaved(const DImageHistory& resolvedCurrentHistory)
 
524
{
 
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();
 
529
 
 
530
    if (resolvedCurrentHistory.isNull())
 
531
    {
 
532
        d->resolvedInitialHistory = d->image.getOriginalImageHistory();
 
533
        d->resolvedInitialHistory.clearReferredImages();
 
534
    }
 
535
    else
 
536
    {
 
537
        d->resolvedInitialHistory = resolvedCurrentHistory;
 
538
    }
 
539
 
 
540
    setUndoManagerOrigin();
 
541
}
 
542
 
 
543
void EditorCore::setHistoryIsBranch(bool isBranching)
 
544
{
 
545
    // The first added step (on top of the initial history) will be marked as branch
 
546
    d->image.setHistoryBranchAfter(d->resolvedInitialHistory, isBranching);
 
547
}
 
548
 
 
549
void EditorCore::setModified()
 
550
{
 
551
    emit signalModified();
 
552
    emit signalUndoStateChanged();
 
553
}
 
554
 
 
555
void EditorCore::readMetadataFromFile(const QString& file)
 
556
{
 
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())
 
566
    {
 
567
        d->image.setImageHistory(DImageHistory::fromXml(meta.getImageHistory()));
 
568
    }
 
569
}
 
570
 
 
571
void EditorCore::clearUndoManager()
 
572
{
 
573
    d->undoMan->clear();
 
574
    d->undoMan->setOrigin();
 
575
    emit signalUndoStateChanged();
 
576
}
 
577
 
 
578
void EditorCore::setUndoManagerOrigin()
 
579
{
 
580
    d->undoMan->setOrigin();
 
581
    emit signalUndoStateChanged();
 
582
    emit signalFileOriginChanged(getImageFilePath());
 
583
}
 
584
 
 
585
bool EditorCore::imageValid() const
 
586
{
 
587
    return d->valid;
 
588
}
 
589
 
 
590
int EditorCore::width() const
 
591
{
 
592
    return d->width;
 
593
}
 
594
 
 
595
int EditorCore::height() const
 
596
{
 
597
    return d->height;
 
598
}
 
599
 
 
600
int EditorCore::origWidth() const
 
601
{
 
602
    return d->origWidth;
 
603
}
 
604
 
 
605
int EditorCore::origHeight() const
 
606
{
 
607
    return d->origHeight;
 
608
}
 
609
 
 
610
int EditorCore::bytesDepth() const
 
611
{
 
612
    return d->image.bytesDepth();
 
613
}
 
614
 
 
615
bool EditorCore::sixteenBit() const
 
616
{
 
617
    return d->image.sixteenBit();
 
618
}
 
619
 
 
620
bool EditorCore::hasAlpha() const
 
621
{
 
622
    return d->image.hasAlpha();
 
623
}
 
624
 
 
625
bool EditorCore::isReadOnly() const
 
626
{
 
627
    if (d->image.isNull())
 
628
    {
 
629
        return true;
 
630
    }
 
631
    else
 
632
    {
 
633
        return d->image.isReadOnly();
 
634
    }
 
635
}
 
636
 
 
637
void EditorCore::setSelectedArea(const QRect& rect)
 
638
{
 
639
    d->selX = rect.x();
 
640
    d->selY = rect.y();
 
641
    d->selW = rect.width();
 
642
    d->selH = rect.height();
 
643
}
 
644
 
 
645
QRect EditorCore::getSelectedArea() const
 
646
{
 
647
    return (QRect(d->selX, d->selY, d->selW, d->selH));
 
648
}
 
649
 
 
650
void EditorCore::paintOnDevice(QPaintDevice* const p,
 
651
                                  const QRect& src,
 
652
                                  const QRect& dst,
 
653
                                  int /*antialias*/)
 
654
{
 
655
    if (d->image.isNull())
 
656
    {
 
657
        return;
 
658
    }
 
659
 
 
660
    DImg img = d->image.smoothScaleSection(src, dst.size());
 
661
    img.convertDepth(32);
 
662
    QPainter painter(p);
 
663
 
 
664
    if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
 
665
    {
 
666
        QPixmap pix(img.convertToPixmap(d->monitorICCtrans));
 
667
        painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
 
668
    }
 
669
    else
 
670
    {
 
671
        QPixmap pix(img.convertToPixmap());
 
672
        painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
 
673
    }
 
674
 
 
675
    // Show the Over/Under exposure pixels indicators
 
676
 
 
677
    if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
 
678
    {
 
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()));
 
682
    }
 
683
 
 
684
    painter.end();
 
685
}
 
686
 
 
687
void EditorCore::paintOnDevice(QPaintDevice* const p,
 
688
                                  const QRect& src,
 
689
                                  const QRect& dst,
 
690
                                  const QRect& mrt,
 
691
                                  int /*antialias*/)
 
692
{
 
693
    if (d->image.isNull())
 
694
    {
 
695
        return;
 
696
    }
 
697
 
 
698
    DImg img = d->image.smoothScaleSection(src, dst.size());
 
699
    img.convertDepth(32);
 
700
    QPainter painter(p);
 
701
 
 
702
    uint* data  = (uint*)img.bits();
 
703
    uchar r, g, b, a;
 
704
 
 
705
    for (int j = 0; j < (int)img.height(); ++j)
 
706
    {
 
707
        for (int i = 0; i < (int)img.width(); ++i)
 
708
        {
 
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))
 
711
            {
 
712
                a = (*data >> 24) & 0xff;
 
713
                r = (*data >> 16) & 0xff;
 
714
                g = (*data >>  8) & 0xff;
 
715
                b = (*data)       & 0xff;
 
716
 
 
717
                r += (uchar)((RCOL - r) * OPACITY);
 
718
                g += (uchar)((GCOL - g) * OPACITY);
 
719
                b += (uchar)((BCOL - b) * OPACITY);
 
720
 
 
721
                *data = (a << 24) | (r << 16) | (g << 8) | b;
 
722
            }
 
723
 
 
724
            ++data;
 
725
        }
 
726
    }
 
727
 
 
728
    if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
 
729
    {
 
730
        QPixmap pix(img.convertToPixmap(d->monitorICCtrans));
 
731
        painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
 
732
    }
 
733
    else
 
734
    {
 
735
        QPixmap pix(img.convertToPixmap());
 
736
        painter.drawPixmap(dst.topLeft(), pix, QRect(0, 0, pix.width(), pix.height()));
 
737
    }
 
738
 
 
739
    // Show the Over/Under exposure pixels indicators
 
740
 
 
741
    if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
 
742
    {
 
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()));
 
746
    }
 
747
 
 
748
    painter.end();
 
749
}
 
750
 
 
751
void EditorCore::zoom(double val)
 
752
{
 
753
    d->zoom   = val;
 
754
    d->width  = (int)(d->origWidth  * val);
 
755
    d->height = (int)(d->origHeight * val);
 
756
}
 
757
 
 
758
void EditorCore::rotate90()
 
759
{
 
760
    d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate90));
 
761
}
 
762
 
 
763
void EditorCore::rotate180()
 
764
{
 
765
    d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate180));
 
766
}
 
767
 
 
768
void EditorCore::rotate270()
 
769
{
 
770
    d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Rotate270));
 
771
}
 
772
 
 
773
void EditorCore::flipHoriz()
 
774
{
 
775
    d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::FlipHorizontally));
 
776
}
 
777
 
 
778
void EditorCore::flipVert()
 
779
{
 
780
    d->applyReversibleBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::FlipVertically));
 
781
}
 
782
 
 
783
void EditorCore::crop(const QRect& rect)
 
784
{
 
785
    d->applyBuiltinFilter(DImgBuiltinFilter(DImgBuiltinFilter::Crop, rect), new UndoActionIrreversible(this, "Crop"));
 
786
}
 
787
 
 
788
void EditorCore::convertDepth(int depth)
 
789
{
 
790
    d->applyBuiltinFilter(DImgBuiltinFilter(depth == 32 ? DImgBuiltinFilter::ConvertTo8Bit : DImgBuiltinFilter::ConvertTo16Bit),
 
791
                          new UndoActionIrreversible(this, "Convert Color Depth"));
 
792
}
 
793
 
 
794
DImg* EditorCore::getImg() const
 
795
{
 
796
    if (!d->image.isNull())
 
797
    {
 
798
        return &d->image;
 
799
    }
 
800
    else
 
801
    {
 
802
        kWarning() << "d->image is NULL";
 
803
        return 0;
 
804
    }
 
805
}
 
806
 
 
807
DImageHistory EditorCore::getImageHistory() const
 
808
{
 
809
    return d->image.getImageHistory();
 
810
}
 
811
 
 
812
DImageHistory EditorCore::getInitialImageHistory() const
 
813
{
 
814
    return d->image.getOriginalImageHistory();
 
815
}
 
816
 
 
817
DImageHistory EditorCore::getImageHistoryOfFullRedo() const
 
818
{
 
819
    return d->undoMan->getImageHistoryOfFullRedo();
 
820
}
 
821
 
 
822
DImageHistory EditorCore::getResolvedInitialHistory() const
 
823
{
 
824
    return d->resolvedInitialHistory;
 
825
}
 
826
 
 
827
void EditorCore::setResolvedInitialHistory(const DImageHistory& history)
 
828
{
 
829
    d->resolvedInitialHistory = history;
 
830
}
 
831
 
 
832
void EditorCore::putImg(const QString& caller, const FilterAction& action, const DImg& img)
 
833
{
 
834
    d->undoMan->addAction(new UndoActionIrreversible(this, caller));
 
835
    d->putImageData(img.bits(), img.width(), img.height(), img.sixteenBit());
 
836
    d->image.addFilterAction(action);
 
837
    setModified();
 
838
}
 
839
 
 
840
void EditorCore::setUndoImg(const UndoMetadataContainer& c, const DImg& img)
 
841
{
 
842
    // called from UndoManager
 
843
    bool changesIcc = c.changesIccProfile(d->image);
 
844
 
 
845
    d->putImageData(img.bits(), img.width(), img.height(), img.sixteenBit());
 
846
    c.toImage(d->image);
 
847
 
 
848
    if (changesIcc)
 
849
    {
 
850
        updateColorManagement();
 
851
    }
 
852
}
 
853
 
 
854
void EditorCore::imageUndoChanged(const UndoMetadataContainer& c)
 
855
{
 
856
    // called from UndoManager
 
857
    bool changesIcc = c.changesIccProfile(d->image);
 
858
 
 
859
    d->origWidth  = d->image.width();
 
860
    d->origHeight = d->image.height();
 
861
    c.toImage(d->image);
 
862
 
 
863
    if (changesIcc)
 
864
    {
 
865
        updateColorManagement();
 
866
    }
 
867
}
 
868
 
 
869
void EditorCore::setFileOriginData(const QVariant& data)
 
870
{
 
871
    d->image.setFileOriginData(data);
 
872
    emit signalFileOriginChanged(getImageFilePath());
 
873
}
 
874
 
 
875
DImg EditorCore::getImgSelection() const
 
876
{
 
877
    if (!d->selW || !d->selH)
 
878
    {
 
879
        return DImg();
 
880
    }
 
881
 
 
882
    if (!d->image.isNull())
 
883
    {
 
884
        DImg im = d->image.copy(d->selX, d->selY, d->selW, d->selH);
 
885
        im.detach();
 
886
        return im;
 
887
    }
 
888
 
 
889
    return DImg();
 
890
}
 
891
 
 
892
void EditorCore::putImgSelection(const QString& caller, const FilterAction& action, const DImg& img)
 
893
{
 
894
    if (img.isNull() || d->image.isNull())
 
895
    {
 
896
        return;
 
897
    }
 
898
 
 
899
    d->undoMan->addAction(new UndoActionIrreversible(this, caller));
 
900
 
 
901
    d->image.bitBltImage(img.bits(), 0, 0, d->selW, d->selH, d->selX, d->selY, d->selW, d->selH, d->image.bytesDepth());
 
902
 
 
903
    d->image.addFilterAction(action);
 
904
    setModified();
 
905
}
 
906
 
 
907
void EditorCore::putIccProfile(const IccProfile& profile)
 
908
{
 
909
    if (d->image.isNull())
 
910
    {
 
911
        kWarning() << "d->image is NULL";
 
912
        return;
 
913
    }
 
914
 
 
915
    //kDebug() << "Embedding profile: " << profile;
 
916
    d->image.setIccProfile(profile);
 
917
    updateColorManagement();
 
918
    setModified();
 
919
}
 
920
 
 
921
QStringList EditorCore::getUndoHistory() const
 
922
{
 
923
    return d->undoMan->getUndoHistory();
 
924
}
 
925
 
 
926
QStringList EditorCore::getRedoHistory() const
 
927
{
 
928
    return d->undoMan->getRedoHistory();
 
929
}
 
930
 
 
931
int EditorCore::availableUndoSteps() const
 
932
{
 
933
    return d->undoMan->availableUndoSteps();
 
934
}
 
935
 
 
936
int EditorCore::availableRedoSteps() const
 
937
{
 
938
    return d->undoMan->availableRedoSteps();
 
939
}
 
940
 
 
941
IccProfile EditorCore::getEmbeddedICC() const
 
942
{
 
943
    return d->image.getIccProfile();
 
944
}
 
945
 
 
946
KExiv2Data EditorCore::getMetadata() const
 
947
{
 
948
    return d->image.getMetadata();
 
949
}
 
950
 
 
951
QString EditorCore::getImageFilePath() const
 
952
{
 
953
    return d->image.originalFilePath();
 
954
}
 
955
 
 
956
QString EditorCore::getImageFileName() const
 
957
{
 
958
    return getImageFilePath().section('/', -1);
 
959
}
 
960
 
 
961
QString EditorCore::getImageFormat() const
 
962
{
 
963
    if (d->image.isNull())
 
964
    {
 
965
        return QString();
 
966
    }
 
967
 
 
968
    QString mimeType = d->image.format();
 
969
 
 
970
    // It is a bug in the loader if format attribute is not given
 
971
    if (mimeType.isEmpty())
 
972
    {
 
973
        kWarning() << "DImg object does not contain attribute \"format\"";
 
974
        mimeType = QImageReader::imageFormat(getImageFilePath());
 
975
    }
 
976
 
 
977
    return mimeType;
 
978
}
 
979
 
 
980
QPixmap EditorCore::convertToPixmap(DImg& img) const
 
981
{
 
982
    QPixmap pix;
 
983
 
 
984
    if (d->cmSettings.enableCM && (d->cmSettings.useManagedView || d->doSoftProofing))
 
985
    {
 
986
        // do not use d->monitorICCtrans here, because img may have a different embedded profile
 
987
        IccManager manager(img);
 
988
        IccTransform transform;
 
989
 
 
990
        if (d->doSoftProofing)
 
991
        {
 
992
            transform = manager.displaySoftProofingTransform(d->cmSettings.defaultProofProfile, d->displayingWidget);
 
993
        }
 
994
        else
 
995
        {
 
996
            transform = manager.displayTransform(d->displayingWidget);
 
997
        }
 
998
 
 
999
        pix = img.convertToPixmap(transform);
 
1000
    }
 
1001
    else
 
1002
    {
 
1003
        pix = img.convertToPixmap();
 
1004
    }
 
1005
 
 
1006
    // Show the Over/Under exposure pixels indicators
 
1007
 
 
1008
    if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator)
 
1009
    {
 
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());
 
1014
    }
 
1015
 
 
1016
    return pix;
 
1017
}
 
1018
 
 
1019
UndoState EditorCore::undoState() const
 
1020
{
 
1021
    UndoState state;
 
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();
 
1027
 
 
1028
    return state;
 
1029
}
 
1030
 
 
1031
}  // namespace Digikam