~ubuntu-branches/ubuntu/precise/gwenview/precise-proposed

« back to all changes in this revision

Viewing changes to lib/documentview/rasterimageview.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:54 UTC
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: package-import@ubuntu.com-20111215141754-z043hyx69dulbggf
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// vim: set tabstop=4 shiftwidth=4 expandtab:
 
2
/*
 
3
Gwenview: an image viewer
 
4
Copyright 2011 AurĆ©lien GĆ¢teau <agateau@kde.org>
 
5
 
 
6
This program is free software; you can redistribute it and/or
 
7
modify it under the terms of the GNU General Public License
 
8
as published by the Free Software Foundation; either version 2
 
9
of the License, or (at your option) any later version.
 
10
 
 
11
This program is distributed in the hope that it will be useful,
 
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
GNU General Public License for more details.
 
15
 
 
16
You should have received a copy of the GNU General Public License
 
17
along with this program; if not, write to the Free Software
 
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
 
19
 
 
20
*/
 
21
// Self
 
22
#include "rasterimageview.moc"
 
23
 
 
24
// Local
 
25
#include <lib/documentview/abstractrasterimageviewtool.h>
 
26
#include <lib/imagescaler.h>
 
27
 
 
28
// KDE
 
29
#include <kdebug.h>
 
30
 
 
31
// Qt
 
32
#include <QGraphicsSceneMouseEvent>
 
33
#include <QPainter>
 
34
#include <QTimer>
 
35
#include <QWeakPointer>
 
36
 
 
37
namespace Gwenview
 
38
{
 
39
 
 
40
struct RasterImageViewPrivate {
 
41
    RasterImageView* q;
 
42
    ImageScaler* mScaler;
 
43
    QPixmap mBackgroundTexture;
 
44
 
 
45
    // Config
 
46
    RasterImageView::AlphaBackgroundMode mAlphaBackgroundMode;
 
47
    QColor mAlphaBackgroundColor;
 
48
    bool mEnlargeSmallerImages;
 
49
    // /Config
 
50
 
 
51
    bool mBufferIsEmpty;
 
52
    QPixmap mCurrentBuffer;
 
53
    // The alternate buffer is useful when scrolling: existing content is copied
 
54
    // to mAlternateBuffer and buffers are swapped. This avoids allocating a new
 
55
    // QPixmap everytime the image is scrolled.
 
56
    QPixmap mAlternateBuffer;
 
57
 
 
58
    QTimer* mUpdateTimer;
 
59
 
 
60
    QWeakPointer<AbstractRasterImageViewTool> mTool;
 
61
 
 
62
    void createBackgroundTexture()
 
63
    {
 
64
        mBackgroundTexture = QPixmap(32, 32);
 
65
        QPainter painter(&mBackgroundTexture);
 
66
        painter.fillRect(mBackgroundTexture.rect(), QColor(128, 128, 128));
 
67
        QColor light = QColor(192, 192, 192);
 
68
        painter.fillRect(0, 0, 16, 16, light);
 
69
        painter.fillRect(16, 16, 16, 16, light);
 
70
    }
 
71
 
 
72
    void setupUpdateTimer()
 
73
    {
 
74
        mUpdateTimer = new QTimer(q);
 
75
        mUpdateTimer->setInterval(500);
 
76
        mUpdateTimer->setSingleShot(true);
 
77
        QObject::connect(mUpdateTimer, SIGNAL(timeout()),
 
78
                         q, SLOT(updateBuffer()));
 
79
    }
 
80
 
 
81
    void startAnimationIfNecessary()
 
82
    {
 
83
        if (q->document() && q->isVisible()) {
 
84
            q->document()->startAnimation();
 
85
        }
 
86
    }
 
87
 
 
88
    QSizeF visibleImageSize() const {
 
89
        if (!q->document()) {
 
90
            return QSizeF();
 
91
        }
 
92
        qreal zoom;
 
93
        if (q->zoomToFit()) {
 
94
            zoom = q->computeZoomToFit();
 
95
        } else {
 
96
            zoom = q->zoom();
 
97
        }
 
98
 
 
99
        QSizeF size = q->documentSize() * zoom;
 
100
        return size.boundedTo(q->boundingRect().size());
 
101
    }
 
102
 
 
103
    QRectF mapViewportToZoomedImage(const QRectF& viewportRect) const {
 
104
        return QRectF(
 
105
                   viewportRect.topLeft() - q->imageOffset() + q->scrollPos(),
 
106
                   viewportRect.size()
 
107
               );
 
108
    }
 
109
 
 
110
    void setScalerRegionToVisibleRect()
 
111
    {
 
112
        QRectF rect = mapViewportToZoomedImage(q->boundingRect());
 
113
        mScaler->setDestinationRegion(QRegion(rect.toRect()));
 
114
    }
 
115
 
 
116
    void createBuffer()
 
117
    {
 
118
        QSize size = visibleImageSize().toSize();
 
119
        if (size == mCurrentBuffer.size()) {
 
120
            return;
 
121
        }
 
122
        if (!size.isValid()) {
 
123
            mAlternateBuffer = QPixmap();
 
124
            mCurrentBuffer = QPixmap();
 
125
            return;
 
126
        }
 
127
 
 
128
        mAlternateBuffer = QPixmap(size);
 
129
        mAlternateBuffer.fill(Qt::transparent);
 
130
        {
 
131
            QPainter painter(&mAlternateBuffer);
 
132
            painter.drawPixmap(0, 0, mCurrentBuffer);
 
133
        }
 
134
        qSwap(mAlternateBuffer, mCurrentBuffer);
 
135
 
 
136
        mAlternateBuffer = QPixmap();
 
137
    }
 
138
 
 
139
    void drawAlphaBackground(QPainter* painter, const QRect& viewportRect, const QPoint& zoomedImageTopLeft)
 
140
    {
 
141
        if (mAlphaBackgroundMode == RasterImageView::AlphaBackgroundCheckBoard) {
 
142
            QPoint textureOffset(
 
143
                zoomedImageTopLeft.x() % mBackgroundTexture.width(),
 
144
                zoomedImageTopLeft.y() % mBackgroundTexture.height()
 
145
            );
 
146
            painter->drawTiledPixmap(
 
147
                viewportRect,
 
148
                mBackgroundTexture,
 
149
                textureOffset);
 
150
        } else {
 
151
            painter->fillRect(viewportRect, mAlphaBackgroundColor);
 
152
        }
 
153
    }
 
154
};
 
155
 
 
156
RasterImageView::RasterImageView(QGraphicsItem* parent)
 
157
: AbstractImageView(parent)
 
158
, d(new RasterImageViewPrivate)
 
159
{
 
160
    d->q = this;
 
161
 
 
162
    d->mAlphaBackgroundMode = AlphaBackgroundCheckBoard;
 
163
    d->mAlphaBackgroundColor = Qt::black;
 
164
    d->mEnlargeSmallerImages = false;
 
165
 
 
166
    d->mBufferIsEmpty = true;
 
167
    d->mScaler = new ImageScaler(this);
 
168
    connect(d->mScaler, SIGNAL(scaledRect(int, int, QImage)),
 
169
            SLOT(updateFromScaler(int, int, QImage)));
 
170
    setAcceptHoverEvents(true);
 
171
 
 
172
    d->createBackgroundTexture();
 
173
    d->setupUpdateTimer();
 
174
}
 
175
 
 
176
RasterImageView::~RasterImageView()
 
177
{
 
178
    delete d;
 
179
}
 
180
 
 
181
void RasterImageView::setAlphaBackgroundMode(AlphaBackgroundMode mode)
 
182
{
 
183
    d->mAlphaBackgroundMode = mode;
 
184
    if (document() && document()->hasAlphaChannel()) {
 
185
        d->mCurrentBuffer = QPixmap();
 
186
        updateBuffer();
 
187
    }
 
188
}
 
189
 
 
190
void RasterImageView::setAlphaBackgroundColor(const QColor& color)
 
191
{
 
192
    d->mAlphaBackgroundColor = color;
 
193
    if (document() && document()->hasAlphaChannel()) {
 
194
        d->mCurrentBuffer = QPixmap();
 
195
        updateBuffer();
 
196
    }
 
197
}
 
198
 
 
199
void RasterImageView::loadFromDocument()
 
200
{
 
201
    Document::Ptr doc = document();
 
202
    connect(doc.data(), SIGNAL(metaInfoLoaded(KUrl)),
 
203
            SLOT(slotDocumentMetaInfoLoaded()));
 
204
    connect(doc.data(), SIGNAL(isAnimatedUpdated()),
 
205
            SLOT(slotDocumentIsAnimatedUpdated()));
 
206
 
 
207
    const Document::LoadingState state = doc->loadingState();
 
208
    if (state == Document::MetaInfoLoaded || state == Document::Loaded) {
 
209
        slotDocumentMetaInfoLoaded();
 
210
    }
 
211
}
 
212
 
 
213
void RasterImageView::slotDocumentMetaInfoLoaded()
 
214
{
 
215
    if (document()->size().isValid()) {
 
216
        finishSetDocument();
 
217
    } else {
 
218
        // Could not retrieve image size from meta info, we need to load the
 
219
        // full image now.
 
220
        connect(document().data(), SIGNAL(loaded(KUrl)),
 
221
                SLOT(finishSetDocument()));
 
222
        document()->startLoadingFullImage();
 
223
    }
 
224
}
 
225
 
 
226
void RasterImageView::finishSetDocument()
 
227
{
 
228
    if (!document()->size().isValid()) {
 
229
        kError() << "No valid image size available, this should not happen!";
 
230
        return;
 
231
    }
 
232
 
 
233
    d->createBuffer();
 
234
    d->mScaler->setDocument(document());
 
235
 
 
236
    connect(document().data(), SIGNAL(imageRectUpdated(QRect)),
 
237
            SLOT(updateImageRect(QRect)));
 
238
 
 
239
    if (zoomToFit()) {
 
240
        // Force the update otherwise if computeZoomToFit() returns 1, setZoom()
 
241
        // will think zoom has not changed and won't update the image
 
242
        setZoom(computeZoomToFit(), QPointF(-1, -1), ForceUpdate);
 
243
    } else {
 
244
        QRect rect(QPoint(0, 0), document()->size());
 
245
        updateImageRect(rect);
 
246
    }
 
247
 
 
248
    d->startAnimationIfNecessary();
 
249
    update();
 
250
}
 
251
 
 
252
void RasterImageView::updateImageRect(const QRect& imageRect)
 
253
{
 
254
    QRectF viewRect = mapToView(imageRect);
 
255
    if (!viewRect.intersects(boundingRect())) {
 
256
        return;
 
257
    }
 
258
 
 
259
    if (zoomToFit()) {
 
260
        setZoom(computeZoomToFit());
 
261
    }
 
262
    d->setScalerRegionToVisibleRect();
 
263
    update();
 
264
}
 
265
 
 
266
void RasterImageView::slotDocumentIsAnimatedUpdated()
 
267
{
 
268
    d->startAnimationIfNecessary();
 
269
}
 
270
 
 
271
void RasterImageView::updateFromScaler(int zoomedImageLeft, int zoomedImageTop, const QImage& image)
 
272
{
 
273
    int viewportLeft = zoomedImageLeft - scrollPos().x();
 
274
    int viewportTop = zoomedImageTop - scrollPos().y();
 
275
    d->mBufferIsEmpty = false;
 
276
    {
 
277
        QPainter painter(&d->mCurrentBuffer);
 
278
        if (document()->hasAlphaChannel()) {
 
279
            d->drawAlphaBackground(
 
280
                &painter, QRect(viewportLeft, viewportTop, image.width(), image.height()),
 
281
                QPoint(zoomedImageLeft, zoomedImageTop)
 
282
            );
 
283
        } else {
 
284
            painter.setCompositionMode(QPainter::CompositionMode_Source);
 
285
        }
 
286
        painter.drawImage(viewportLeft, viewportTop, image);
 
287
    }
 
288
    update();
 
289
}
 
290
 
 
291
void RasterImageView::onZoomChanged()
 
292
{
 
293
    // If we zoom more than twice, then assume the user wants to see the real
 
294
    // pixels, for example to fine tune a crop operation
 
295
    if (zoom() < 2.) {
 
296
        d->mScaler->setTransformationMode(Qt::SmoothTransformation);
 
297
    } else {
 
298
        d->mScaler->setTransformationMode(Qt::FastTransformation);
 
299
    }
 
300
    if (!d->mUpdateTimer->isActive()) {
 
301
        updateBuffer();
 
302
    }
 
303
}
 
304
 
 
305
void RasterImageView::onImageOffsetChanged()
 
306
{
 
307
    update();
 
308
}
 
309
 
 
310
void RasterImageView::onScrollPosChanged(const QPointF& oldPos)
 
311
{
 
312
    QPointF delta = scrollPos() - oldPos;
 
313
 
 
314
    // Scroll existing
 
315
    {
 
316
        if (d->mAlternateBuffer.size() != d->mCurrentBuffer.size()) {
 
317
            d->mAlternateBuffer = QPixmap(d->mCurrentBuffer.size());
 
318
        }
 
319
        QPainter painter(&d->mAlternateBuffer);
 
320
        painter.drawPixmap(-delta, d->mCurrentBuffer);
 
321
    }
 
322
    qSwap(d->mCurrentBuffer, d->mAlternateBuffer);
 
323
 
 
324
    // Scale missing parts
 
325
    QRegion bufferRegion = QRegion(d->mCurrentBuffer.rect().translated(scrollPos().toPoint()));
 
326
    QRegion updateRegion = bufferRegion - bufferRegion.translated(-delta.toPoint());
 
327
    updateBuffer(updateRegion);
 
328
    update();
 
329
}
 
330
 
 
331
void RasterImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
 
332
{
 
333
    QSize bufferSize = d->mCurrentBuffer.size();
 
334
 
 
335
    QSizeF paintSize;
 
336
    if (zoomToFit()) {
 
337
        paintSize = documentSize() * computeZoomToFit();
 
338
    } else {
 
339
        paintSize = bufferSize;
 
340
    }
 
341
    painter->drawPixmap(
 
342
        (boundingRect().width() - paintSize.width()) / 2,
 
343
        (boundingRect().height() - paintSize.height()) / 2,
 
344
        paintSize.width(),
 
345
        paintSize.height(),
 
346
        d->mCurrentBuffer);
 
347
 
 
348
    if (d->mTool) {
 
349
        d->mTool.data()->paint(painter);
 
350
    }
 
351
 
 
352
    // Debug
 
353
#if 0
 
354
    QPointF topLeft = imageOffset();
 
355
    QSizeF visibleSize = documentSize() * zoom();
 
356
    painter->setPen(Qt::red);
 
357
    painter->drawRect(topLeft.x(), topLeft.y(), visibleSize.width() - 1, visibleSize.height() - 1);
 
358
 
 
359
    painter->setPen(Qt::blue);
 
360
    painter->drawRect(topLeft.x(), topLeft.y(), d->mCurrentBuffer.width() - 1, d->mCurrentBuffer.height() - 1);
 
361
#endif
 
362
}
 
363
 
 
364
void RasterImageView::resizeEvent(QGraphicsSceneResizeEvent* event)
 
365
{
 
366
    // If we are in zoomToFit mode and have something in our buffer, delay the
 
367
    // update: paint() will paint a scaled version of the buffer until resizing
 
368
    // is done. This is much faster than rescaling the whole image for each
 
369
    // resize event we receive.
 
370
    // mUpdateTimer must be start before calling AbstractImageView::resizeEvent()
 
371
    // because AbstractImageView::resizeEvent() will call onZoomChanged(), which
 
372
    // will trigger an immediate update unless the mUpdateTimer is active.
 
373
    if (zoomToFit() && !d->mBufferIsEmpty) {
 
374
        d->mUpdateTimer->start();
 
375
    }
 
376
    AbstractImageView::resizeEvent(event);
 
377
    if (!zoomToFit()) {
 
378
        // Only update buffer if we are not in zoomToFit mode: if we are
 
379
        // onZoomChanged() will have already updated the buffer.
 
380
        updateBuffer();
 
381
    }
 
382
}
 
383
 
 
384
void RasterImageView::updateBuffer(const QRegion& region)
 
385
{
 
386
    d->mUpdateTimer->stop();
 
387
    d->createBuffer();
 
388
    d->mScaler->setZoom(zoom());
 
389
    if (region.isEmpty()) {
 
390
        d->setScalerRegionToVisibleRect();
 
391
    } else {
 
392
        d->mScaler->setDestinationRegion(region);
 
393
    }
 
394
}
 
395
 
 
396
void RasterImageView::setCurrentTool(AbstractRasterImageViewTool* tool)
 
397
{
 
398
    if (d->mTool) {
 
399
        d->mTool.data()->toolDeactivated();
 
400
        d->mTool.data()->deleteLater();
 
401
    }
 
402
    d->mTool = tool;
 
403
    if (d->mTool) {
 
404
        d->mTool.data()->toolActivated();
 
405
    }
 
406
    updateCursor();
 
407
    currentToolChanged(tool);
 
408
    update();
 
409
}
 
410
 
 
411
AbstractRasterImageViewTool* RasterImageView::currentTool() const
 
412
{
 
413
    return d->mTool.data();
 
414
}
 
415
 
 
416
void RasterImageView::mousePressEvent(QGraphicsSceneMouseEvent* event)
 
417
{
 
418
    if (d->mTool) {
 
419
        d->mTool.data()->mousePressEvent(event);
 
420
        if (event->isAccepted()) {
 
421
            return;
 
422
        }
 
423
    }
 
424
    AbstractImageView::mousePressEvent(event);
 
425
}
 
426
 
 
427
void RasterImageView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
 
428
{
 
429
    if (d->mTool) {
 
430
        d->mTool.data()->mouseMoveEvent(event);
 
431
        if (event->isAccepted()) {
 
432
            return;
 
433
        }
 
434
    }
 
435
    AbstractImageView::mouseMoveEvent(event);
 
436
}
 
437
 
 
438
void RasterImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
 
439
{
 
440
    if (d->mTool) {
 
441
        d->mTool.data()->mouseReleaseEvent(event);
 
442
        if (event->isAccepted()) {
 
443
            return;
 
444
        }
 
445
    }
 
446
    AbstractImageView::mouseReleaseEvent(event);
 
447
}
 
448
 
 
449
void RasterImageView::wheelEvent(QGraphicsSceneWheelEvent* event)
 
450
{
 
451
    if (d->mTool) {
 
452
        d->mTool.data()->wheelEvent(event);
 
453
        if (event->isAccepted()) {
 
454
            return;
 
455
        }
 
456
    }
 
457
    AbstractImageView::wheelEvent(event);
 
458
}
 
459
 
 
460
void RasterImageView::keyPressEvent(QKeyEvent* event)
 
461
{
 
462
    if (d->mTool) {
 
463
        d->mTool.data()->keyPressEvent(event);
 
464
        if (event->isAccepted()) {
 
465
            return;
 
466
        }
 
467
    }
 
468
    AbstractImageView::keyPressEvent(event);
 
469
}
 
470
 
 
471
void RasterImageView::keyReleaseEvent(QKeyEvent* event)
 
472
{
 
473
    if (d->mTool) {
 
474
        d->mTool.data()->keyReleaseEvent(event);
 
475
        if (event->isAccepted()) {
 
476
            return;
 
477
        }
 
478
    }
 
479
    AbstractImageView::keyReleaseEvent(event);
 
480
}
 
481
 
 
482
void RasterImageView::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
 
483
{
 
484
    if (d->mTool) {
 
485
        d->mTool.data()->hoverMoveEvent(event);
 
486
        if (event->isAccepted()) {
 
487
            return;
 
488
        }
 
489
    }
 
490
    AbstractImageView::hoverMoveEvent(event);
 
491
}
 
492
 
 
493
} // namespace