~artmello/ubuntu-ui-extras/ubuntu-ui-extras-fix_1524973

« back to all changes in this revision

Viewing changes to modules/Ubuntu/Components/Extras/plugin/photoeditor/photo-edit-thread.cpp

  • Committer: CI Train Bot
  • Author(s): Ugo Riboni
  • Date: 2015-02-04 20:33:44 UTC
  • mfrom: (65.1.46 ubuntu-ui-extras-photo-editor)
  • Revision ID: ci-train-bot@canonical.com-20150204203344-pfmu1ckhooy7oaca
Add a photo editor component, partially based on the Gallery photo editor Fixes: #1368787
Approved by: PS Jenkins bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013-2014 Canonical Ltd
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 */
 
16
 
 
17
#include "photo-edit-thread.h"
 
18
#include "photo-data.h"
 
19
 
 
20
// medialoader
 
21
#include "photo-metadata.h"
 
22
 
 
23
// util
 
24
#include "imaging.h"
 
25
 
 
26
#include <QDebug>
 
27
 
 
28
/*!
 
29
 * \brief PhotoEditThread::PhotoEditThread
 
30
 */
 
31
PhotoEditThread::PhotoEditThread(PhotoData *photo, const PhotoEditCommand &command)
 
32
    : QThread(),
 
33
      m_photo(photo),
 
34
      m_command(command)
 
35
{
 
36
}
 
37
 
 
38
/*!
 
39
 * \brief PhotoEditThread::command resturns the editing command used for this processing
 
40
 * \return
 
41
 */
 
42
const PhotoEditCommand &PhotoEditThread::command() const
 
43
{
 
44
    return m_command;
 
45
}
 
46
 
 
47
/*!
 
48
 * \brief PhotoEditThread::run \reimp
 
49
 */
 
50
void PhotoEditThread::run()
 
51
{
 
52
    // The only operation in which we don't have to work on the actual image
 
53
    // pixels is image rotation in the case where we can simply change the
 
54
    // metadata rotation field.
 
55
    if (m_command.type == EDIT_ROTATE && m_photo->fileFormatHasOrientation()) {
 
56
        handleSimpleMetadataRotation(m_command);
 
57
        return;
 
58
    }
 
59
 
 
60
    // In all other cases we load the image, do the work, and save it back.
 
61
    QImage image(m_photo->file().filePath(), m_photo->fileFormat().toStdString().c_str());
 
62
    if (image.isNull()) {
 
63
        qWarning() << "Error loading" << m_photo->file().filePath() << "for editing";
 
64
        return;
 
65
    }
 
66
 
 
67
    // Copy all metadata from the original image so that we can save it to the
 
68
    // new one after modifying the pixels.
 
69
    PhotoMetadata* original = PhotoMetadata::fromFile(m_photo->file());
 
70
 
 
71
    // If the photo was previously rotated through metadata and we are editing
 
72
    // the actual pixels, first rotate the image to match the orientation so
 
73
    // that the correct pixels are edited.
 
74
    // Obviously don't do this in the case we have been asked to do a rotation
 
75
    // operation on the pixels, as we would do it later as the operation itself.
 
76
    if (m_photo->fileFormatHasOrientation() && m_command.type != EDIT_ROTATE) {
 
77
        Orientation orientation = m_photo->orientation();
 
78
        QTransform transform = OrientationCorrection::fromOrientation(orientation).toTransform();
 
79
        image = image.transformed(transform);
 
80
    }
 
81
 
 
82
    if (m_command.type == EDIT_ROTATE) {
 
83
        QTransform transform = OrientationCorrection::fromOrientation(m_command.orientation).toTransform();
 
84
        image = image.transformed(transform);
 
85
    } else if (m_command.type == EDIT_CROP) {
 
86
        QRect rect;
 
87
        rect.setX(qBound(0.0, m_command.crop_rectangle.x(), 1.0) * image.width());
 
88
        rect.setY(qBound(0.0, m_command.crop_rectangle.y(), 1.0) * image.height());
 
89
        rect.setWidth(qBound(0.0, m_command.crop_rectangle.width(), 1.0) * image.width());
 
90
        rect.setHeight(qBound(0.0, m_command.crop_rectangle.height(), 1.0) * image.height());
 
91
 
 
92
        image = image.copy(rect);
 
93
    } else if (m_command.type == EDIT_ENHANCE) {
 
94
        image = enhanceImage(image);
 
95
    } else if (m_command.type == EDIT_COMPENSATE_EXPOSURE) {
 
96
        image = compensateExposure(image, m_command.exposureCompensation);
 
97
    } else {
 
98
        qWarning() << "Edit thread running with unknown or no operation.";
 
99
        return;
 
100
    }
 
101
 
 
102
    bool saved = image.save(m_photo->file().filePath(),
 
103
                            m_photo->fileFormat().toStdString().c_str(), 90);
 
104
    if (!saved)
 
105
        qWarning() << "Error saving edited" << m_photo->file().filePath();
 
106
 
 
107
    PhotoMetadata* copy = PhotoMetadata::fromFile(m_photo->file());
 
108
    original->copyTo(copy);
 
109
    copy->setOrientation(TOP_LEFT_ORIGIN); // reset previous orientation
 
110
    copy->updateThumbnail(image);
 
111
    copy->save();
 
112
 
 
113
    delete original;
 
114
    delete copy;
 
115
}
 
116
 
 
117
/*!
 
118
 * \brief PhotoEditThread::handleSimpleMetadataRotation
 
119
 * Handler for the case of an image whose only change is to its
 
120
 * orientation; used to skip re-encoding of JPEGs.
 
121
 * \param state
 
122
 */
 
123
void PhotoEditThread::handleSimpleMetadataRotation(const PhotoEditCommand& state)
 
124
{
 
125
    PhotoMetadata* metadata = PhotoMetadata::fromFile(m_photo->file());
 
126
    metadata->setOrientation(state.orientation);
 
127
    metadata->save();
 
128
    delete(metadata);
 
129
}
 
130
 
 
131
/*!
 
132
 * \brief PhotoEditThread::enhanceImage
 
133
 */
 
134
QImage PhotoEditThread::enhanceImage(const QImage& image)
 
135
{
 
136
    int width = image.width();
 
137
    int height = image.height();
 
138
 
 
139
    QImage sample_img = (image.width() > 400) ? image.scaledToWidth(400) : image;
 
140
 
 
141
    AutoEnhanceTransformation enhance = AutoEnhanceTransformation(sample_img);
 
142
 
 
143
    QImage::Format dest_format = image.format();
 
144
 
 
145
    // Can't write into indexed images, due to a limitation in Qt.
 
146
    if (dest_format == QImage::Format_Indexed8)
 
147
        dest_format = QImage::Format_RGB32;
 
148
 
 
149
    QImage enhanced_image(width, height, dest_format);
 
150
 
 
151
    for (int j = 0; j < height; j++) {
 
152
        for (int i = 0; i < width; i++) {
 
153
            QColor px = enhance.transformPixel(
 
154
                        QColor(image.pixel(i, j)));
 
155
            enhanced_image.setPixel(i, j, px.rgb());
 
156
        }
 
157
    }
 
158
 
 
159
    return enhanced_image;
 
160
}
 
161
 
 
162
/*!
 
163
 * \brief PhotoEditThread::compensateExposure Compensates the exposure
 
164
 * Compensating the exposure is a change in brightnes
 
165
 * \param image Image to change the brightnes
 
166
 * \param compansation -1.0 is total dark, +1.0 is total bright
 
167
 * \return The image with adjusted brightnes
 
168
 */
 
169
QImage PhotoEditThread::compensateExposure(const QImage &image, qreal compensation)
 
170
{
 
171
    int shift = qBound(-255, (int)(255*compensation), 255);
 
172
    QImage result(image.width(), image.height(), image.format());
 
173
 
 
174
    for (int j = 0; j < image.height(); j++) {
 
175
        for (int i = 0; i <image.width(); i++) {
 
176
            QColor px = image.pixel(i, j);
 
177
            int red = qBound(0, px.red() + shift, 255);
 
178
            int green = qBound(0, px.green() + shift, 255);
 
179
            int blue = qBound(0, px.blue() + shift, 255);
 
180
            result.setPixel(i, j, qRgb(red, green, blue));
 
181
        }
 
182
    }
 
183
 
 
184
    return result;
 
185
}