1
// vim: set tabstop=4 shiftwidth=4 expandtab:
3
Gwenview: an image viewer
4
Copyright 2011 AurĆ©lien GĆ¢teau <agateau@kde.org>
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.
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.
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.
22
#include "rasterimageview.moc"
25
#include <lib/documentview/abstractrasterimageviewtool.h>
26
#include <lib/imagescaler.h>
32
#include <QGraphicsSceneMouseEvent>
35
#include <QWeakPointer>
40
struct RasterImageViewPrivate {
43
QPixmap mBackgroundTexture;
46
RasterImageView::AlphaBackgroundMode mAlphaBackgroundMode;
47
QColor mAlphaBackgroundColor;
48
bool mEnlargeSmallerImages;
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;
60
QWeakPointer<AbstractRasterImageViewTool> mTool;
62
void createBackgroundTexture()
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);
72
void setupUpdateTimer()
74
mUpdateTimer = new QTimer(q);
75
mUpdateTimer->setInterval(500);
76
mUpdateTimer->setSingleShot(true);
77
QObject::connect(mUpdateTimer, SIGNAL(timeout()),
78
q, SLOT(updateBuffer()));
81
void startAnimationIfNecessary()
83
if (q->document() && q->isVisible()) {
84
q->document()->startAnimation();
88
QSizeF visibleImageSize() const {
94
zoom = q->computeZoomToFit();
99
QSizeF size = q->documentSize() * zoom;
100
return size.boundedTo(q->boundingRect().size());
103
QRectF mapViewportToZoomedImage(const QRectF& viewportRect) const {
105
viewportRect.topLeft() - q->imageOffset() + q->scrollPos(),
110
void setScalerRegionToVisibleRect()
112
QRectF rect = mapViewportToZoomedImage(q->boundingRect());
113
mScaler->setDestinationRegion(QRegion(rect.toRect()));
118
QSize size = visibleImageSize().toSize();
119
if (size == mCurrentBuffer.size()) {
122
if (!size.isValid()) {
123
mAlternateBuffer = QPixmap();
124
mCurrentBuffer = QPixmap();
128
mAlternateBuffer = QPixmap(size);
129
mAlternateBuffer.fill(Qt::transparent);
131
QPainter painter(&mAlternateBuffer);
132
painter.drawPixmap(0, 0, mCurrentBuffer);
134
qSwap(mAlternateBuffer, mCurrentBuffer);
136
mAlternateBuffer = QPixmap();
139
void drawAlphaBackground(QPainter* painter, const QRect& viewportRect, const QPoint& zoomedImageTopLeft)
141
if (mAlphaBackgroundMode == RasterImageView::AlphaBackgroundCheckBoard) {
142
QPoint textureOffset(
143
zoomedImageTopLeft.x() % mBackgroundTexture.width(),
144
zoomedImageTopLeft.y() % mBackgroundTexture.height()
146
painter->drawTiledPixmap(
151
painter->fillRect(viewportRect, mAlphaBackgroundColor);
156
RasterImageView::RasterImageView(QGraphicsItem* parent)
157
: AbstractImageView(parent)
158
, d(new RasterImageViewPrivate)
162
d->mAlphaBackgroundMode = AlphaBackgroundCheckBoard;
163
d->mAlphaBackgroundColor = Qt::black;
164
d->mEnlargeSmallerImages = false;
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);
172
d->createBackgroundTexture();
173
d->setupUpdateTimer();
176
RasterImageView::~RasterImageView()
181
void RasterImageView::setAlphaBackgroundMode(AlphaBackgroundMode mode)
183
d->mAlphaBackgroundMode = mode;
184
if (document() && document()->hasAlphaChannel()) {
185
d->mCurrentBuffer = QPixmap();
190
void RasterImageView::setAlphaBackgroundColor(const QColor& color)
192
d->mAlphaBackgroundColor = color;
193
if (document() && document()->hasAlphaChannel()) {
194
d->mCurrentBuffer = QPixmap();
199
void RasterImageView::loadFromDocument()
201
Document::Ptr doc = document();
202
connect(doc.data(), SIGNAL(metaInfoLoaded(KUrl)),
203
SLOT(slotDocumentMetaInfoLoaded()));
204
connect(doc.data(), SIGNAL(isAnimatedUpdated()),
205
SLOT(slotDocumentIsAnimatedUpdated()));
207
const Document::LoadingState state = doc->loadingState();
208
if (state == Document::MetaInfoLoaded || state == Document::Loaded) {
209
slotDocumentMetaInfoLoaded();
213
void RasterImageView::slotDocumentMetaInfoLoaded()
215
if (document()->size().isValid()) {
218
// Could not retrieve image size from meta info, we need to load the
220
connect(document().data(), SIGNAL(loaded(KUrl)),
221
SLOT(finishSetDocument()));
222
document()->startLoadingFullImage();
226
void RasterImageView::finishSetDocument()
228
if (!document()->size().isValid()) {
229
kError() << "No valid image size available, this should not happen!";
234
d->mScaler->setDocument(document());
236
connect(document().data(), SIGNAL(imageRectUpdated(QRect)),
237
SLOT(updateImageRect(QRect)));
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);
244
QRect rect(QPoint(0, 0), document()->size());
245
updateImageRect(rect);
248
d->startAnimationIfNecessary();
252
void RasterImageView::updateImageRect(const QRect& imageRect)
254
QRectF viewRect = mapToView(imageRect);
255
if (!viewRect.intersects(boundingRect())) {
260
setZoom(computeZoomToFit());
262
d->setScalerRegionToVisibleRect();
266
void RasterImageView::slotDocumentIsAnimatedUpdated()
268
d->startAnimationIfNecessary();
271
void RasterImageView::updateFromScaler(int zoomedImageLeft, int zoomedImageTop, const QImage& image)
273
int viewportLeft = zoomedImageLeft - scrollPos().x();
274
int viewportTop = zoomedImageTop - scrollPos().y();
275
d->mBufferIsEmpty = false;
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)
284
painter.setCompositionMode(QPainter::CompositionMode_Source);
286
painter.drawImage(viewportLeft, viewportTop, image);
291
void RasterImageView::onZoomChanged()
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
296
d->mScaler->setTransformationMode(Qt::SmoothTransformation);
298
d->mScaler->setTransformationMode(Qt::FastTransformation);
300
if (!d->mUpdateTimer->isActive()) {
305
void RasterImageView::onImageOffsetChanged()
310
void RasterImageView::onScrollPosChanged(const QPointF& oldPos)
312
QPointF delta = scrollPos() - oldPos;
316
if (d->mAlternateBuffer.size() != d->mCurrentBuffer.size()) {
317
d->mAlternateBuffer = QPixmap(d->mCurrentBuffer.size());
319
QPainter painter(&d->mAlternateBuffer);
320
painter.drawPixmap(-delta, d->mCurrentBuffer);
322
qSwap(d->mCurrentBuffer, d->mAlternateBuffer);
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);
331
void RasterImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
333
QSize bufferSize = d->mCurrentBuffer.size();
337
paintSize = documentSize() * computeZoomToFit();
339
paintSize = bufferSize;
342
(boundingRect().width() - paintSize.width()) / 2,
343
(boundingRect().height() - paintSize.height()) / 2,
349
d->mTool.data()->paint(painter);
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);
359
painter->setPen(Qt::blue);
360
painter->drawRect(topLeft.x(), topLeft.y(), d->mCurrentBuffer.width() - 1, d->mCurrentBuffer.height() - 1);
364
void RasterImageView::resizeEvent(QGraphicsSceneResizeEvent* event)
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();
376
AbstractImageView::resizeEvent(event);
378
// Only update buffer if we are not in zoomToFit mode: if we are
379
// onZoomChanged() will have already updated the buffer.
384
void RasterImageView::updateBuffer(const QRegion& region)
386
d->mUpdateTimer->stop();
388
d->mScaler->setZoom(zoom());
389
if (region.isEmpty()) {
390
d->setScalerRegionToVisibleRect();
392
d->mScaler->setDestinationRegion(region);
396
void RasterImageView::setCurrentTool(AbstractRasterImageViewTool* tool)
399
d->mTool.data()->toolDeactivated();
400
d->mTool.data()->deleteLater();
404
d->mTool.data()->toolActivated();
407
currentToolChanged(tool);
411
AbstractRasterImageViewTool* RasterImageView::currentTool() const
413
return d->mTool.data();
416
void RasterImageView::mousePressEvent(QGraphicsSceneMouseEvent* event)
419
d->mTool.data()->mousePressEvent(event);
420
if (event->isAccepted()) {
424
AbstractImageView::mousePressEvent(event);
427
void RasterImageView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
430
d->mTool.data()->mouseMoveEvent(event);
431
if (event->isAccepted()) {
435
AbstractImageView::mouseMoveEvent(event);
438
void RasterImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
441
d->mTool.data()->mouseReleaseEvent(event);
442
if (event->isAccepted()) {
446
AbstractImageView::mouseReleaseEvent(event);
449
void RasterImageView::wheelEvent(QGraphicsSceneWheelEvent* event)
452
d->mTool.data()->wheelEvent(event);
453
if (event->isAccepted()) {
457
AbstractImageView::wheelEvent(event);
460
void RasterImageView::keyPressEvent(QKeyEvent* event)
463
d->mTool.data()->keyPressEvent(event);
464
if (event->isAccepted()) {
468
AbstractImageView::keyPressEvent(event);
471
void RasterImageView::keyReleaseEvent(QKeyEvent* event)
474
d->mTool.data()->keyReleaseEvent(event);
475
if (event->isAccepted()) {
479
AbstractImageView::keyReleaseEvent(event);
482
void RasterImageView::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
485
d->mTool.data()->hoverMoveEvent(event);
486
if (event->isAccepted()) {
490
AbstractImageView::hoverMoveEvent(event);