2
* Copyright (C) 2013-2014 Canonical Ltd
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.
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.
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/>.
17
#include "photo-edit-thread.h"
18
#include "photo-data.h"
21
#include "photo-metadata.h"
29
* \brief PhotoEditThread::PhotoEditThread
31
PhotoEditThread::PhotoEditThread(PhotoData *photo, const PhotoEditCommand &command)
39
* \brief PhotoEditThread::command resturns the editing command used for this processing
42
const PhotoEditCommand &PhotoEditThread::command() const
48
* \brief PhotoEditThread::run \reimp
50
void PhotoEditThread::run()
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);
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());
63
qWarning() << "Error loading" << m_photo->file().filePath() << "for editing";
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());
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);
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) {
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());
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);
98
qWarning() << "Edit thread running with unknown or no operation.";
102
bool saved = image.save(m_photo->file().filePath(),
103
m_photo->fileFormat().toStdString().c_str(), 90);
105
qWarning() << "Error saving edited" << m_photo->file().filePath();
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);
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.
123
void PhotoEditThread::handleSimpleMetadataRotation(const PhotoEditCommand& state)
125
PhotoMetadata* metadata = PhotoMetadata::fromFile(m_photo->file());
126
metadata->setOrientation(state.orientation);
132
* \brief PhotoEditThread::enhanceImage
134
QImage PhotoEditThread::enhanceImage(const QImage& image)
136
int width = image.width();
137
int height = image.height();
139
QImage sample_img = (image.width() > 400) ? image.scaledToWidth(400) : image;
141
AutoEnhanceTransformation enhance = AutoEnhanceTransformation(sample_img);
143
QImage::Format dest_format = image.format();
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;
149
QImage enhanced_image(width, height, dest_format);
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());
159
return enhanced_image;
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
169
QImage PhotoEditThread::compensateExposure(const QImage &image, qreal compensation)
171
int shift = qBound(-255, (int)(255*compensation), 255);
172
QImage result(image.width(), image.height(), image.format());
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));