diff --git a/app/configdialog.cpp b/app/configdialog.cpp index 28c75da2..3b43a8ac 100644 --- a/app/configdialog.cpp +++ b/app/configdialog.cpp @@ -1,131 +1,132 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "configdialog.h" // Qt // KDE #include // Local #include #include namespace Gwenview { template QWidget* setupPage(Ui& ui) { QWidget* widget = new QWidget; ui.setupUi(widget); widget->layout()->setMargin(0); return widget; } ConfigDialog::ConfigDialog(QWidget* parent) : KConfigDialog(parent, "Settings", GwenviewConfig::self()) { setFaceType(KPageDialog::List); QWidget* widget; KPageWidgetItem* pageItem; // General widget = setupPage(mGeneralConfigPage); mThumbnailActionsGroup = new InvisibleButtonGroup(widget); mThumbnailActionsGroup->setObjectName(QLatin1String("kcfg_ThumbnailActions")); mThumbnailActionsGroup->addButton(mGeneralConfigPage.allButtonsThumbnailActionsRadioButton, int(ThumbnailActions::AllButtons)); mThumbnailActionsGroup->addButton(mGeneralConfigPage.selectionOnlyThumbnailActionsRadioButton, int(ThumbnailActions::ShowSelectionButtonOnly)); mThumbnailActionsGroup->addButton(mGeneralConfigPage.noneThumbnailActionsRadioButton, int(ThumbnailActions::None)); pageItem = addPage(widget, i18n("General")); pageItem->setIcon(QIcon::fromTheme("gwenview")); connect(mGeneralConfigPage.kcfg_ViewBackgroundValue, SIGNAL(valueChanged(int)), SLOT(updateViewBackgroundFrame())); // Image View widget = setupPage(mImageViewConfigPage); mAlphaBackgroundModeGroup = new InvisibleButtonGroup(widget); mAlphaBackgroundModeGroup->setObjectName(QLatin1String("kcfg_AlphaBackgroundMode")); + mAlphaBackgroundModeGroup->addButton(mImageViewConfigPage.noBackgroundRadioButton, int(AbstractImageView::AlphaBackgroundNone)); mAlphaBackgroundModeGroup->addButton(mImageViewConfigPage.checkBoardRadioButton, int(AbstractImageView::AlphaBackgroundCheckBoard)); mAlphaBackgroundModeGroup->addButton(mImageViewConfigPage.solidColorRadioButton, int(AbstractImageView::AlphaBackgroundSolid)); mWheelBehaviorGroup = new InvisibleButtonGroup(widget); mWheelBehaviorGroup->setObjectName(QLatin1String("kcfg_MouseWheelBehavior")); mWheelBehaviorGroup->addButton(mImageViewConfigPage.mouseWheelScrollRadioButton, int(MouseWheelBehavior::Scroll)); mWheelBehaviorGroup->addButton(mImageViewConfigPage.mouseWheelBrowseRadioButton, int(MouseWheelBehavior::Browse)); mAnimationMethodGroup = new InvisibleButtonGroup(widget); mAnimationMethodGroup->setObjectName(QLatin1String("kcfg_AnimationMethod")); mAnimationMethodGroup->addButton(mImageViewConfigPage.glAnimationRadioButton, int(DocumentView::GLAnimation)); mAnimationMethodGroup->addButton(mImageViewConfigPage.softwareAnimationRadioButton, int(DocumentView::SoftwareAnimation)); mAnimationMethodGroup->addButton(mImageViewConfigPage.noAnimationRadioButton, int(DocumentView::NoAnimation)); mZoomModeGroup = new InvisibleButtonGroup(widget); mZoomModeGroup->setObjectName(QLatin1String("kcfg_ZoomMode")); mZoomModeGroup->addButton(mImageViewConfigPage.autofitZoomModeRadioButton, int(ZoomMode::Autofit)); mZoomModeGroup->addButton(mImageViewConfigPage.keepSameZoomModeRadioButton, int(ZoomMode::KeepSame)); mZoomModeGroup->addButton(mImageViewConfigPage.individualZoomModeRadioButton, int(ZoomMode::Individual)); mThumbnailBarOrientationGroup = new InvisibleButtonGroup(widget); mThumbnailBarOrientationGroup->setObjectName(QLatin1String("kcfg_ThumbnailBarOrientation")); mThumbnailBarOrientationGroup->addButton(mImageViewConfigPage.horizontalRadioButton, int(Qt::Horizontal)); mThumbnailBarOrientationGroup->addButton(mImageViewConfigPage.verticalRadioButton, int(Qt::Vertical)); pageItem = addPage(widget, i18n("Image View")); pageItem->setIcon(QIcon::fromTheme("view-preview")); // Advanced widget = setupPage(mAdvancedConfigPage); mRenderingIntentGroup = new InvisibleButtonGroup(widget); mRenderingIntentGroup->setObjectName(QLatin1String("kcfg_RenderingIntent")); mRenderingIntentGroup->addButton(mAdvancedConfigPage.relativeRenderingIntentRadioButton, int(RenderingIntent::Relative)); mRenderingIntentGroup->addButton(mAdvancedConfigPage.perceptualRenderingIntentRadioButton, int(RenderingIntent::Perceptual)); pageItem = addPage(widget, i18n("Advanced")); pageItem->setIcon(QIcon::fromTheme("preferences-other")); mAdvancedConfigPage.cacheHelpLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); mAdvancedConfigPage.perceptualHelpLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); mAdvancedConfigPage.relativeHelpLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); updateViewBackgroundFrame(); } void ConfigDialog::updateViewBackgroundFrame() { QColor color = QColor::fromHsv(0, 0, mGeneralConfigPage.kcfg_ViewBackgroundValue->value()); QString css = QString( "background-color: %1;" "border-radius: 5px;" "border: 1px solid %1;") .arg(color.name()); // When using Oxygen, setting the background color via palette causes the // pixels outside the frame to be painted with the new background color as // well. Using CSS works more like expected. mGeneralConfigPage.backgroundValueFrame->setStyleSheet(css); } } // namespace diff --git a/app/imageviewconfigpage.ui b/app/imageviewconfigpage.ui index 1bc694ae..669648f1 100644 --- a/app/imageviewconfigpage.ui +++ b/app/imageviewconfigpage.ui @@ -1,628 +1,661 @@ ImageViewConfigPage 0 0 500 - 600 + 650 Transparent background: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter checkBoardRadioButton + + + 6 + + + 0 + + + + + None + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + 6 0 &Check board - true + false Qt::Horizontal 40 20 - + 6 0 &Solid color: false Qt::Horizontal 20 20 - + Qt::Vertical QSizePolicy::Fixed 207 17 - + Mouse wheel behavior: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter mouseWheelScrollRadioButton - + Scroll true Qt::Horizontal 40 20 - + Browse Qt::Horizontal 40 20 - + Qt::Vertical QSizePolicy::Fixed 207 17 - + Zoom mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter autofitZoomModeRadioButton - + Autofit each image true Qt::Horizontal 40 20 - + Keep same zoom and position Qt::Horizontal 40 20 - + Per image zoom and position Qt::Horizontal 40 20 - + Qt::Vertical QSizePolicy::Fixed 207 17 - + Enlarge smaller images Qt::Horizontal 40 20 - + Qt::Vertical QSizePolicy::Fixed 204 17 - + Animations: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter glAnimationRadioButton - + OpenGL true Qt::Horizontal 40 20 - + Software Qt::Horizontal 40 20 - + None Qt::Horizontal 40 20 - + Qt::Vertical QSizePolicy::Fixed 204 17 - + <b>Thumbnail Bar</b> - + Qt::Vertical QSizePolicy::Fixed 204 12 - + Orientation: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter horizontalRadioButton - + Horizontal Qt::Horizontal 40 20 - + Vertical Qt::Horizontal 40 20 - + Row count: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter kcfg_ThumbnailBarRowCount - + 1 10 Qt::Horizontal 40 20 - + Qt::Vertical 20 60 KColorButton QPushButton
kcolorbutton.h
QSpinBox QSpinBox
knuminput.h
checkBoardRadioButton solidColorRadioButton kcfg_AlphaBackgroundColor mouseWheelScrollRadioButton mouseWheelBrowseRadioButton kcfg_EnlargeSmallerImages glAnimationRadioButton softwareAnimationRadioButton noAnimationRadioButton horizontalRadioButton verticalRadioButton kcfg_ThumbnailBarRowCount solidColorRadioButton toggled(bool) kcfg_AlphaBackgroundColor setEnabled(bool) 195 49 292 56
diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h index d01f4d1e..d15a1e6d 100644 --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -1,156 +1,157 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ #ifndef ABSTRACTIMAGEVIEW_H #define ABSTRACTIMAGEVIEW_H // Local #include // KDE // Qt #include namespace Gwenview { struct AbstractImageViewPrivate; /** * */ class AbstractImageView : public QGraphicsWidget { Q_OBJECT public: enum UpdateType { UpdateIfNecessary, ForceUpdate }; enum AlphaBackgroundMode { + AlphaBackgroundNone, AlphaBackgroundCheckBoard, AlphaBackgroundSolid }; AbstractImageView(QGraphicsItem* parent); ~AbstractImageView(); qreal zoom() const; virtual void setZoom(qreal zoom, const QPointF& center = QPointF(-1, -1), UpdateType updateType = UpdateIfNecessary); bool zoomToFit() const; bool zoomToFill() const; virtual void setZoomToFit(bool value); virtual void setZoomToFill(bool value); virtual void setDocument(Document::Ptr doc); Document::Ptr document() const; qreal computeZoomToFit() const; qreal computeZoomToFill() const; QSizeF documentSize() const; QSizeF visibleImageSize() const; /** * If the image is smaller than the view, imageOffset is the distance from * the topleft corner of the view to the topleft corner of the image. * Neither x nor y can be negative. */ QPointF imageOffset() const; /** * The scroll position, in zoomed image coordinates. * x and y are always between 0 and (docsize * zoom - viewsize) */ QPointF scrollPos() const; void setScrollPos(const QPointF& pos); QPointF mapToView(const QPointF& imagePos) const; QPoint mapToView(const QPoint& imagePos) const; QRectF mapToView(const QRectF& imageRect) const; QRect mapToView(const QRect& imageRect) const; QPointF mapToImage(const QPointF& viewPos) const; QPoint mapToImage(const QPoint& viewPos) const; QRectF mapToImage(const QRectF& viewRect) const; QRect mapToImage(const QRect& viewRect) const; void setEnlargeSmallerImages(bool value); void applyPendingScrollPos(); public Q_SLOTS: void updateCursor(); Q_SIGNALS: void zoomToFitChanged(bool); void zoomToFillChanged(bool); void zoomChanged(qreal); void zoomInRequested(const QPointF&); void zoomOutRequested(const QPointF&); void scrollPosChanged(); void completed(); void previousImageRequested(); void nextImageRequested(); void toggleFullScreenRequested(); protected: virtual void setAlphaBackgroundMode(AlphaBackgroundMode mode) = 0; virtual void setAlphaBackgroundColor(const QColor& color) = 0; const QPixmap& alphaBackgroundTexture() const; virtual void loadFromDocument() = 0; virtual void onZoomChanged() = 0; /** * Called when the offset changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onImageOffsetChanged() = 0; /** * Called when the scrollPos changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onScrollPosChanged(const QPointF& oldPos) = 0; void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; private: friend struct AbstractImageViewPrivate; AbstractImageViewPrivate* const d; }; } // namespace #endif /* ABSTRACTIMAGEVIEW_H */ diff --git a/lib/documentview/rasterimageview.cpp b/lib/documentview/rasterimageview.cpp index 8fbd2335..0d8bf6c0 100644 --- a/lib/documentview/rasterimageview.cpp +++ b/lib/documentview/rasterimageview.cpp @@ -1,554 +1,565 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "rasterimageview.h" // Local #include #include #include #include // KDE // Qt #include #include #include #include #include namespace Gwenview { struct RasterImageViewPrivate { RasterImageView* q; ImageScaler* mScaler; bool mEmittedCompleted; // Config AbstractImageView::AlphaBackgroundMode mAlphaBackgroundMode; QColor mAlphaBackgroundColor; cmsUInt32Number mRenderingIntent; bool mEnlargeSmallerImages; // /Config bool mBufferIsEmpty; QPixmap mCurrentBuffer; // The alternate buffer is useful when scrolling: existing content is copied // to mAlternateBuffer and buffers are swapped. This avoids allocating a new // QPixmap every time the image is scrolled. QPixmap mAlternateBuffer; QTimer* mUpdateTimer; QPointer mTool; bool mApplyDisplayTransform; // Defaults to true. Can be set to false if there is no need or no way to apply color profile cmsHTRANSFORM mDisplayTransform; void updateDisplayTransform(QImage::Format format) { GV_RETURN_IF_FAIL(format != QImage::Format_Invalid); mApplyDisplayTransform = false; if (mDisplayTransform) { cmsDeleteTransform(mDisplayTransform); } mDisplayTransform = 0; Cms::Profile::Ptr profile = q->document()->cmsProfile(); if (!profile) { // The assumption that something unmarked is *probably* sRGB is better than failing to apply any transform when one // has a wide-gamut screen. profile = Cms::Profile::getSRgbProfile(); } Cms::Profile::Ptr monitorProfile = Cms::Profile::getMonitorProfile(); if (!monitorProfile) { qWarning() << "Could not get monitor color profile"; return; } cmsUInt32Number cmsFormat = 0; switch (format) { case QImage::Format_RGB32: case QImage::Format_ARGB32: cmsFormat = TYPE_BGRA_8; break; #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) case QImage::Format_Grayscale8: cmsFormat = TYPE_GRAY_8; break; #endif default: qWarning() << "Gwenview can only apply color profile on RGB32 or ARGB32 images"; return; } mDisplayTransform = cmsCreateTransform(profile->handle(), cmsFormat, monitorProfile->handle(), cmsFormat, mRenderingIntent, cmsFLAGS_BLACKPOINTCOMPENSATION); mApplyDisplayTransform = true; } void setupUpdateTimer() { mUpdateTimer = new QTimer(q); mUpdateTimer->setInterval(500); mUpdateTimer->setSingleShot(true); QObject::connect(mUpdateTimer, SIGNAL(timeout()), q, SLOT(updateBuffer())); } void startAnimationIfNecessary() { if (q->document() && q->isVisible()) { q->document()->startAnimation(); } } QRectF mapViewportToZoomedImage(const QRectF& viewportRect) const { return QRectF( viewportRect.topLeft() - q->imageOffset() + q->scrollPos(), viewportRect.size() ); } void setScalerRegionToVisibleRect() { QRectF rect = mapViewportToZoomedImage(q->boundingRect()); mScaler->setDestinationRegion(QRegion(rect.toRect())); } void resizeBuffer() { QSize size = q->visibleImageSize().toSize(); if (size == mCurrentBuffer.size()) { return; } if (!size.isValid()) { mAlternateBuffer = QPixmap(); mCurrentBuffer = QPixmap(); return; } mAlternateBuffer = QPixmap(size); mAlternateBuffer.fill(Qt::transparent); { QPainter painter(&mAlternateBuffer); painter.drawPixmap(0, 0, mCurrentBuffer); } qSwap(mAlternateBuffer, mCurrentBuffer); mAlternateBuffer = QPixmap(); } void drawAlphaBackground(QPainter* painter, const QRect& viewportRect, const QPoint& zoomedImageTopLeft, QPixmap texture) { - if (mAlphaBackgroundMode == AbstractImageView::AlphaBackgroundCheckBoard) { - QPoint textureOffset( - zoomedImageTopLeft.x() % texture.width(), - zoomedImageTopLeft.y() % texture.height() - ); - painter->drawTiledPixmap( - viewportRect, - texture, - textureOffset); - } else { - painter->fillRect(viewportRect, mAlphaBackgroundColor); + switch (mAlphaBackgroundMode) { + case AbstractImageView::AlphaBackgroundNone: + painter->fillRect(viewportRect, Qt::transparent); + break; + case AbstractImageView::AlphaBackgroundCheckBoard: + { + const QPoint textureOffset( + zoomedImageTopLeft.x() % texture.width(), + zoomedImageTopLeft.y() % texture.height()); + painter->drawTiledPixmap( + viewportRect, + texture, + textureOffset); + break; + } + case AbstractImageView::AlphaBackgroundSolid: + painter->fillRect(viewportRect, mAlphaBackgroundColor); + break; + default: + Q_ASSERT(0); } } }; RasterImageView::RasterImageView(QGraphicsItem* parent) : AbstractImageView(parent) , d(new RasterImageViewPrivate) { d->q = this; d->mEmittedCompleted = false; d->mApplyDisplayTransform = true; d->mDisplayTransform = 0; - d->mAlphaBackgroundMode = AlphaBackgroundCheckBoard; + d->mAlphaBackgroundMode = AlphaBackgroundNone; d->mAlphaBackgroundColor = Qt::black; d->mRenderingIntent = INTENT_PERCEPTUAL; d->mEnlargeSmallerImages = false; d->mBufferIsEmpty = true; d->mScaler = new ImageScaler(this); connect(d->mScaler, &ImageScaler::scaledRect, this, &RasterImageView::updateFromScaler); d->setupUpdateTimer(); } RasterImageView::~RasterImageView() { if (d->mTool) { d->mTool.data()->toolDeactivated(); } if (d->mDisplayTransform) { cmsDeleteTransform(d->mDisplayTransform); } delete d; } void RasterImageView::setAlphaBackgroundMode(AlphaBackgroundMode mode) { d->mAlphaBackgroundMode = mode; if (document() && document()->hasAlphaChannel()) { d->mCurrentBuffer = QPixmap(); updateBuffer(); } } void RasterImageView::setAlphaBackgroundColor(const QColor& color) { d->mAlphaBackgroundColor = color; if (document() && document()->hasAlphaChannel()) { d->mCurrentBuffer = QPixmap(); updateBuffer(); } } void RasterImageView::setRenderingIntent(const RenderingIntent::Enum& renderingIntent) { if (d->mRenderingIntent != renderingIntent) { d->mRenderingIntent = renderingIntent; updateBuffer(); } } void RasterImageView::loadFromDocument() { Document::Ptr doc = document(); if (!doc) { return; } connect(doc.data(), SIGNAL(metaInfoLoaded(QUrl)), SLOT(slotDocumentMetaInfoLoaded())); connect(doc.data(), SIGNAL(isAnimatedUpdated()), SLOT(slotDocumentIsAnimatedUpdated())); const Document::LoadingState state = doc->loadingState(); if (state == Document::MetaInfoLoaded || state == Document::Loaded) { slotDocumentMetaInfoLoaded(); } } void RasterImageView::slotDocumentMetaInfoLoaded() { if (document()->size().isValid()) { QMetaObject::invokeMethod(this, "finishSetDocument", Qt::QueuedConnection); } else { // Could not retrieve image size from meta info, we need to load the // full image now. connect(document().data(), SIGNAL(loaded(QUrl)), SLOT(finishSetDocument())); document()->startLoadingFullImage(); } } void RasterImageView::finishSetDocument() { GV_RETURN_IF_FAIL(document()->size().isValid()); d->mScaler->setDocument(document()); d->resizeBuffer(); applyPendingScrollPos(); connect(document().data(), SIGNAL(imageRectUpdated(QRect)), SLOT(updateImageRect(QRect))); if (zoomToFit()) { // Force the update otherwise if computeZoomToFit() returns 1, setZoom() // will think zoom has not changed and won't update the image setZoom(computeZoomToFit(), QPointF(-1, -1), ForceUpdate); } else if (zoomToFill()) { setZoom(computeZoomToFill(), QPointF(-1, -1), ForceUpdate); } else { updateBuffer(); } d->startAnimationIfNecessary(); update(); } void RasterImageView::updateImageRect(const QRect& imageRect) { QRectF viewRect = mapToView(imageRect); if (!viewRect.intersects(boundingRect())) { return; } if (zoomToFit()) { setZoom(computeZoomToFit()); } else if (zoomToFill()) { setZoom(computeZoomToFill()); } d->setScalerRegionToVisibleRect(); update(); emit imageRectUpdated(); } void RasterImageView::slotDocumentIsAnimatedUpdated() { d->startAnimationIfNecessary(); } void RasterImageView::updateFromScaler(int zoomedImageLeft, int zoomedImageTop, const QImage& image) { if (d->mApplyDisplayTransform) { d->updateDisplayTransform(image.format()); if (d->mDisplayTransform) { quint8 *bytes = const_cast(image.bits()); cmsDoTransform(d->mDisplayTransform, bytes, bytes, image.width() * image.height()); } } d->resizeBuffer(); int viewportLeft = zoomedImageLeft - scrollPos().x(); int viewportTop = zoomedImageTop - scrollPos().y(); d->mBufferIsEmpty = false; { QPainter painter(&d->mCurrentBuffer); + painter.setCompositionMode(QPainter::CompositionMode_Source); if (document()->hasAlphaChannel()) { d->drawAlphaBackground( &painter, QRect(viewportLeft, viewportTop, image.width(), image.height()), QPoint(zoomedImageLeft, zoomedImageTop), alphaBackgroundTexture() ); - } else { - painter.setCompositionMode(QPainter::CompositionMode_Source); + // This is required so transparent pixels don't replace our background + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); } painter.drawImage(viewportLeft, viewportTop, image); } update(); if (!d->mEmittedCompleted) { d->mEmittedCompleted = true; completed(); } } void RasterImageView::onZoomChanged() { // If we zoom more than twice, then assume the user wants to see the real // pixels, for example to fine tune a crop operation if (zoom() < 2.) { d->mScaler->setTransformationMode(Qt::SmoothTransformation); } else { d->mScaler->setTransformationMode(Qt::FastTransformation); } if (!d->mUpdateTimer->isActive()) { updateBuffer(); } } void RasterImageView::onImageOffsetChanged() { update(); } void RasterImageView::onScrollPosChanged(const QPointF& oldPos) { QPointF delta = scrollPos() - oldPos; // Scroll existing { if (d->mAlternateBuffer.size() != d->mCurrentBuffer.size()) { d->mAlternateBuffer = QPixmap(d->mCurrentBuffer.size()); } + d->mAlternateBuffer.fill(Qt::transparent); QPainter painter(&d->mAlternateBuffer); painter.drawPixmap(-delta, d->mCurrentBuffer); } qSwap(d->mCurrentBuffer, d->mAlternateBuffer); // Scale missing parts QRegion bufferRegion = QRegion(d->mCurrentBuffer.rect().translated(scrollPos().toPoint())); QRegion updateRegion = bufferRegion - bufferRegion.translated(-delta.toPoint()); updateBuffer(updateRegion); update(); } void RasterImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { QPointF topLeft = imageOffset(); if (zoomToFit()) { // In zoomToFit mode, scale crudely the buffer to fit the screen. This // provide an approximate rendered which will be replaced when the scheduled // proper scale is ready. QSizeF size = documentSize() * zoom(); painter->drawPixmap(topLeft.x(), topLeft.y(), size.width(), size.height(), d->mCurrentBuffer); } else { painter->drawPixmap(topLeft, d->mCurrentBuffer); } if (d->mTool) { d->mTool.data()->paint(painter); } // Debug #if 0 QSizeF visibleSize = documentSize() * zoom(); painter->setPen(Qt::red); painter->drawRect(topLeft.x(), topLeft.y(), visibleSize.width() - 1, visibleSize.height() - 1); painter->setPen(Qt::blue); painter->drawRect(topLeft.x(), topLeft.y(), d->mCurrentBuffer.width() - 1, d->mCurrentBuffer.height() - 1); #endif } void RasterImageView::resizeEvent(QGraphicsSceneResizeEvent* event) { // If we are in zoomToFit mode and have something in our buffer, delay the // update: paint() will paint a scaled version of the buffer until resizing // is done. This is much faster than rescaling the whole image for each // resize event we receive. // mUpdateTimer must be started before calling AbstractImageView::resizeEvent() // because AbstractImageView::resizeEvent() will call onZoomChanged(), which // will trigger an immediate update unless the mUpdateTimer is active. if (zoomToFit() && !d->mBufferIsEmpty) { d->mUpdateTimer->start(); } else if (zoomToFill() && !d->mBufferIsEmpty) { d->mUpdateTimer->start(); } AbstractImageView::resizeEvent(event); if (!zoomToFit()) { // Only update buffer if we are not in zoomToFit mode: if we are // onZoomChanged() will have already updated the buffer. updateBuffer(); } else if (!zoomToFill()) { updateBuffer(); } } void RasterImageView::updateBuffer(const QRegion& region) { d->mUpdateTimer->stop(); d->mScaler->setZoom(zoom()); if (region.isEmpty()) { d->setScalerRegionToVisibleRect(); } else { d->mScaler->setDestinationRegion(region); } } void RasterImageView::setCurrentTool(AbstractRasterImageViewTool* tool) { if (d->mTool) { d->mTool.data()->toolDeactivated(); d->mTool.data()->deleteLater(); } d->mTool = tool; if (d->mTool) { d->mTool.data()->toolActivated(); } updateCursor(); currentToolChanged(tool); update(); } AbstractRasterImageViewTool* RasterImageView::currentTool() const { return d->mTool.data(); } void RasterImageView::mousePressEvent(QGraphicsSceneMouseEvent* event) { if (d->mTool) { d->mTool.data()->mousePressEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::mousePressEvent(event); } void RasterImageView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if (d->mTool) { d->mTool.data()->mouseMoveEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::mouseMoveEvent(event); } void RasterImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { if (d->mTool) { d->mTool.data()->mouseReleaseEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::mouseReleaseEvent(event); } void RasterImageView::wheelEvent(QGraphicsSceneWheelEvent* event) { if (d->mTool) { d->mTool.data()->wheelEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::wheelEvent(event); } void RasterImageView::keyPressEvent(QKeyEvent* event) { if (d->mTool) { d->mTool.data()->keyPressEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::keyPressEvent(event); } void RasterImageView::keyReleaseEvent(QKeyEvent* event) { if (d->mTool) { d->mTool.data()->keyReleaseEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::keyReleaseEvent(event); } void RasterImageView::hoverMoveEvent(QGraphicsSceneHoverEvent* event) { if (d->mTool) { d->mTool.data()->hoverMoveEvent(event); if (event->isAccepted()) { return; } } AbstractImageView::hoverMoveEvent(event); } } // namespace diff --git a/lib/documentview/svgviewadapter.cpp b/lib/documentview/svgviewadapter.cpp index 038756c1..055a3cc0 100644 --- a/lib/documentview/svgviewadapter.cpp +++ b/lib/documentview/svgviewadapter.cpp @@ -1,254 +1,258 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "svgviewadapter.h" // Qt #include #include #include #include #include #include #include // KDE // Local #include "document/documentfactory.h" #include #include #include namespace Gwenview { /// SvgImageView //// SvgImageView::SvgImageView(QGraphicsItem* parent) : AbstractImageView(parent) , mSvgItem(new QGraphicsSvgItem(this)) , mAlphaBackgroundMode(AbstractImageView::AlphaBackgroundCheckBoard) , mAlphaBackgroundColor(Qt::black) , mImageFullyLoaded(false) { // So we aren't unnecessarily drawing the background for every paint() setCacheMode(QGraphicsItem::DeviceCoordinateCache); } void SvgImageView::loadFromDocument() { Document::Ptr doc = document(); GV_RETURN_IF_FAIL(doc); if (doc->loadingState() < Document::Loaded) { connect(doc.data(), SIGNAL(loaded(QUrl)), SLOT(finishLoadFromDocument())); } else { QMetaObject::invokeMethod(this, "finishLoadFromDocument", Qt::QueuedConnection); } } void SvgImageView::finishLoadFromDocument() { QSvgRenderer* renderer = document()->svgRenderer(); GV_RETURN_IF_FAIL(renderer); mSvgItem->setSharedRenderer(renderer); if (zoomToFit()) { setZoom(computeZoomToFit(), QPointF(-1, -1), ForceUpdate); } else if (zoomToFill()) { setZoom(computeZoomToFill(), QPointF(-1, -1), ForceUpdate); } else { mSvgItem->setScale(zoom()); } applyPendingScrollPos(); completed(); mImageFullyLoaded = true; } void SvgImageView::onZoomChanged() { mSvgItem->setScale(zoom()); adjustItemPos(); update(); } void SvgImageView::onImageOffsetChanged() { adjustItemPos(); } void SvgImageView::onScrollPosChanged(const QPointF& /* oldPos */) { adjustItemPos(); } void SvgImageView::adjustItemPos() { mSvgItem->setPos(imageOffset() - scrollPos()); update(); } void SvgImageView::setAlphaBackgroundMode(AbstractImageView::AlphaBackgroundMode mode) { mAlphaBackgroundMode = mode; update(); } void SvgImageView::setAlphaBackgroundColor(const QColor& color) { mAlphaBackgroundColor = color; update(); } void SvgImageView::drawAlphaBackground(QPainter* painter) { const QRectF imageRect = QRectF(imageOffset(), visibleImageSize()); switch (mAlphaBackgroundMode) { + case AbstractImageView::AlphaBackgroundNone: + // Unlike RasterImageView, SVGs are rendered directly on the image view, + // therefore we can simply not draw a background + break; case AbstractImageView::AlphaBackgroundCheckBoard: painter->drawTiledPixmap(imageRect, alphaBackgroundTexture(), scrollPos()); break; case AbstractImageView::AlphaBackgroundSolid: painter->fillRect(imageRect, mAlphaBackgroundColor); break; default: Q_ASSERT(0); } } void SvgImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { if (mImageFullyLoaded) { drawAlphaBackground(painter); } } //// SvgViewAdapter //// struct SvgViewAdapterPrivate { SvgImageView* mView; }; SvgViewAdapter::SvgViewAdapter() : d(new SvgViewAdapterPrivate) { d->mView = new SvgImageView; setWidget(d->mView); connect(d->mView, &SvgImageView::zoomChanged, this, &SvgViewAdapter::zoomChanged); connect(d->mView, &SvgImageView::zoomToFitChanged, this, &SvgViewAdapter::zoomToFitChanged); connect(d->mView, &SvgImageView::zoomToFillChanged, this, &SvgViewAdapter::zoomToFillChanged); connect(d->mView, &SvgImageView::zoomInRequested, this, &SvgViewAdapter::zoomInRequested); connect(d->mView, &SvgImageView::zoomOutRequested, this, &SvgViewAdapter::zoomOutRequested); connect(d->mView, &SvgImageView::scrollPosChanged, this, &SvgViewAdapter::scrollPosChanged); connect(d->mView, &SvgImageView::completed, this, &SvgViewAdapter::completed); connect(d->mView, &SvgImageView::previousImageRequested, this, &SvgViewAdapter::previousImageRequested); connect(d->mView, &SvgImageView::nextImageRequested, this, &SvgViewAdapter::nextImageRequested); connect(d->mView, &SvgImageView::toggleFullScreenRequested, this, &SvgViewAdapter::toggleFullScreenRequested); } SvgViewAdapter::~SvgViewAdapter() { delete d; } QCursor SvgViewAdapter::cursor() const { return widget()->cursor(); } void SvgViewAdapter::setCursor(const QCursor& cursor) { widget()->setCursor(cursor); } void SvgViewAdapter::setDocument(Document::Ptr doc) { d->mView->setDocument(doc); } Document::Ptr SvgViewAdapter::document() const { return d->mView->document(); } void SvgViewAdapter::loadConfig() { d->mView->setAlphaBackgroundMode(GwenviewConfig::alphaBackgroundMode()); d->mView->setAlphaBackgroundColor(GwenviewConfig::alphaBackgroundColor()); d->mView->setEnlargeSmallerImages(GwenviewConfig::enlargeSmallerImages()); } void SvgViewAdapter::setZoomToFit(bool on) { d->mView->setZoomToFit(on); } void SvgViewAdapter::setZoomToFill(bool on) { d->mView->setZoomToFill(on); } bool SvgViewAdapter::zoomToFit() const { return d->mView->zoomToFit(); } bool SvgViewAdapter::zoomToFill() const { return d->mView->zoomToFill(); } qreal SvgViewAdapter::zoom() const { return d->mView->zoom(); } void SvgViewAdapter::setZoom(qreal zoom, const QPointF& center) { d->mView->setZoom(zoom, center); } qreal SvgViewAdapter::computeZoomToFit() const { return d->mView->computeZoomToFit(); } qreal SvgViewAdapter::computeZoomToFill() const { return d->mView->computeZoomToFill(); } QPointF SvgViewAdapter::scrollPos() const { return d->mView->scrollPos(); } void SvgViewAdapter::setScrollPos(const QPointF& pos) { d->mView->setScrollPos(pos); } QRectF SvgViewAdapter::visibleDocumentRect() const { return QRectF(d->mView->imageOffset(), d->mView->visibleImageSize()); } } // namespace diff --git a/lib/gwenviewconfig.kcfg b/lib/gwenviewconfig.kcfg index e1d1a94b..c9a61e06 100644 --- a/lib/gwenviewconfig.kcfg +++ b/lib/gwenviewconfig.kcfg @@ -1,332 +1,333 @@ lib/sorting.h lib/zoommode.h lib/thumbnailactions.h lib/mousewheelbehavior.h lib/documentview/documentview.h lib/documentview/rasterimageview.h lib/print/printoptionspage.h lib/renderingintent.h General.Name,General.ImageSize,Exif.Photo.ExposureTime,Exif.Photo.Flash true true false 100 true 0.5 The percentage of memory used by Gwenview before it warns the user and suggest saving changes. new A list of filename extensions Gwenview should not try to load. We exclude *.new as well because this is the extension used for temporary files by KSaveFile. false Horizontal 1 false false information ThumbnailActions::AllButtons General.Name,Exif.Image.DateTime 75 true false + - AbstractImageView::AlphaBackgroundCheckBoard + AbstractImageView::AlphaBackgroundNone #ffffff MouseWheelBehavior::Scroll false true 350, 100 DocumentView::SoftwareAnimation ZoomMode::Autofit Defines what happens when going to image B after having zoomed in on an area of image A. If set to Autofit, image B is zoomed out to fit the screen. If set to KeepSame, all images share the same zoom and position: image B is set to the same zoom parameters as image A (and if these are changed, image A will then be displayed with the updated zoom and position). If set to Individual, all images remember their own zoom and position: image B is initially set to the same zoom parameters as image A, but will then remember its own zoom and position (if these are changed, image A will NOT be displayed with the updated zoom and position). RenderingIntent::Perceptual Defines how colors are rendered when your display uses an ICC color profile and an image has colors that do not fit within the profile's color gamut. "Perceptual" will scale the colors of the entire image so that they all fit within the display's capabilities. "Relative" will squash only the colors that cannot be displayed, and leave the other colors alone. 128 3./2. false Sorting::Name 1 true Qt::AlignHCenter | Qt::AlignVCenter PrintOptionsPage::ScaleToPage false 15.0 10.0 PrintOptionsPage::Centimeters true false true false false 5.0 24 false false -1 0 0 true true false