libkipi from trunk (KDE 4.3) : add support of kipi host settings "file timestamp...
[kdegraphics.git] / gwenview / lib / imagescaler.cpp
blobfa82c04a883b6acb2f3e728574c51d6b3cfc06c6
1 /*
2 Gwenview: an image viewer
3 Copyright 2007 Aurélien Gâteau <aurelien.gateau@free.fr>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "imagescaler.moc"
22 // Qt
23 #include <QImage>
24 #include <QRegion>
26 // KDE
27 #include <kdebug.h>
29 // Local
30 #include <lib/document/document.h>
31 #include <lib/paintutils.h>
33 #undef ENABLE_LOG
34 #undef LOG
35 //#define ENABLE_LOG
36 #ifdef ENABLE_LOG
37 #define LOG(x) kDebug() << x
38 #else
39 #define LOG(x) ;
40 #endif
42 namespace Gwenview {
44 // Amount of pixels to keep so that smooth scale is correct
45 static const int SMOOTH_MARGIN = 3;
47 struct ImageScalerPrivate {
48 Qt::TransformationMode mTransformationMode;
49 Document::Ptr mDocument;
50 qreal mZoom;
51 QRegion mRegion;
54 ImageScaler::ImageScaler(QObject* parent)
55 : QObject(parent)
56 , d(new ImageScalerPrivate) {
57 d->mTransformationMode = Qt::FastTransformation;
58 d->mZoom = 0;
61 ImageScaler::~ImageScaler() {
62 delete d;
65 void ImageScaler::setDocument(Document::Ptr document) {
66 if (d->mDocument) {
67 disconnect(d->mDocument.data(), 0, this, 0);
69 d->mDocument = document;
70 connect(d->mDocument.data(), SIGNAL(downSampledImageReady()),
71 SLOT(doScale()) );
74 void ImageScaler::setZoom(qreal zoom) {
75 d->mZoom = zoom;
78 void ImageScaler::setTransformationMode(Qt::TransformationMode mode) {
79 d->mTransformationMode = mode;
82 void ImageScaler::setDestinationRegion(const QRegion& region) {
83 LOG(region);
84 d->mRegion = region;
85 if (d->mRegion.isEmpty()) {
86 return;
89 if (d->mDocument && d->mZoom > 0) {
90 doScale();
95 void ImageScaler::doScale() {
96 if (d->mZoom < Document::maxDownSampledZoom()) {
97 if (!d->mDocument->prepareDownSampledImageForZoom(d->mZoom)) {
98 LOG("Asked for a down sampled image");
99 return;
101 } else if (d->mDocument->image().isNull()) {
102 LOG("Asked for the full image");
103 d->mDocument->loadFullImage();
104 return;
107 LOG("Starting");
108 Q_FOREACH(const QRect& rect, d->mRegion.rects()) {
109 LOG(rect);
110 scaleRect(rect);
112 LOG("Done");
116 void ImageScaler::scaleRect(const QRect& rect) {
117 const qreal REAL_DELTA = 0.001;
118 if (qAbs(d->mZoom - 1.0) < REAL_DELTA) {
119 QImage tmp = d->mDocument->image().copy(rect);
120 tmp.convertToFormat(QImage::Format_ARGB32_Premultiplied);
121 scaledRect(rect.left(), rect.top(), tmp);
122 return;
125 QImage image;
126 qreal zoom;
127 if (d->mZoom < Document::maxDownSampledZoom()) {
128 image = d->mDocument->downSampledImageForZoom(d->mZoom);
129 Q_ASSERT(!image.isNull());
130 qreal zoom1 = qreal(image.width()) / d->mDocument->width();
131 zoom = d->mZoom / zoom1;
132 } else {
133 image = d->mDocument->image();
134 zoom = d->mZoom;
136 // If rect contains "half" pixels, make sure sourceRect includes them
137 QRectF sourceRectF(
138 rect.left() / zoom,
139 rect.top() / zoom,
140 rect.width() / zoom,
141 rect.height() / zoom);
143 sourceRectF = sourceRectF.intersected(image.rect());
144 QRect sourceRect = PaintUtils::containingRect(sourceRectF);
145 if (sourceRect.isEmpty()) {
146 return;
149 // Compute smooth margin
150 bool needsSmoothMargins = d->mTransformationMode == Qt::SmoothTransformation;
152 int sourceLeftMargin, sourceRightMargin, sourceTopMargin, sourceBottomMargin;
153 int destLeftMargin, destRightMargin, destTopMargin, destBottomMargin;
154 if (needsSmoothMargins) {
155 sourceLeftMargin = qMin(sourceRect.left(), SMOOTH_MARGIN);
156 sourceTopMargin = qMin(sourceRect.top(), SMOOTH_MARGIN);
157 sourceRightMargin = qMin(image.rect().right() - sourceRect.right(), SMOOTH_MARGIN);
158 sourceBottomMargin = qMin(image.rect().bottom() - sourceRect.bottom(), SMOOTH_MARGIN);
159 sourceRect.adjust(
160 -sourceLeftMargin,
161 -sourceTopMargin,
162 sourceRightMargin,
163 sourceBottomMargin);
164 destLeftMargin = int(sourceLeftMargin * zoom);
165 destTopMargin = int(sourceTopMargin * zoom);
166 destRightMargin = int(sourceRightMargin * zoom);
167 destBottomMargin = int(sourceBottomMargin * zoom);
168 } else {
169 sourceLeftMargin = sourceRightMargin = sourceTopMargin = sourceBottomMargin = 0;
170 destLeftMargin = destRightMargin = destTopMargin = destBottomMargin = 0;
173 // destRect is almost like rect, but it contains only "full" pixels
174 QRectF destRectF = QRectF(
175 sourceRect.left() * zoom,
176 sourceRect.top() * zoom,
177 sourceRect.width() * zoom,
178 sourceRect.height() * zoom
180 QRect destRect = PaintUtils::containingRect(destRectF);
182 QImage tmp;
183 tmp = image.copy(sourceRect);
184 tmp.convertToFormat(QImage::Format_ARGB32_Premultiplied);
185 tmp = tmp.scaled(
186 destRect.width(),
187 destRect.height(),
188 Qt::IgnoreAspectRatio, // Do not use KeepAspectRatio, it can lead to skipped rows or columns
189 d->mTransformationMode);
191 if (needsSmoothMargins) {
192 tmp = tmp.copy(
193 destLeftMargin, destTopMargin,
194 destRect.width() - (destLeftMargin + destRightMargin),
195 destRect.height() - (destTopMargin + destBottomMargin)
199 scaledRect(destRect.left() + destLeftMargin, destRect.top() + destTopMargin, tmp);
202 } // namespace