File indexing completed on 2025-04-27 03:58:23

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-04-30
0007  * Description : Graphics View item for DImg
0008  *
0009  * SPDX-FileCopyrightText: 2010-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "graphicsdimgitem.h"
0017 #include "dimgitems_p.h"
0018 
0019 // Qt includes
0020 
0021 #include <QStyleOptionGraphicsItem>
0022 #include <QPainter>
0023 #include <QPoint>
0024 #include <QRect>
0025 
0026 // Local includes
0027 
0028 #include "dimg.h"
0029 #include "imagezoomsettings.h"
0030 
0031 namespace Digikam
0032 {
0033 
0034 CachedPixmaps::CachedPixmaps(int maxCount)
0035     : maxCount(maxCount)
0036 {
0037 }
0038 
0039 CachedPixmaps::~CachedPixmaps()
0040 {
0041     clear();
0042 }
0043 
0044 void CachedPixmaps::setMaxCount(int count)
0045 {
0046     maxCount = count;
0047 }
0048 
0049 void CachedPixmaps::clear()
0050 {
0051     Q_FOREACH (const CachedPixmapKey& key, keys)
0052     {
0053         QPixmapCache::remove(key.key);
0054     }
0055 
0056     keys.clear();
0057 }
0058 
0059 bool CachedPixmaps::find(const QRect& region, QPixmap* const pix, QRect* const source)
0060 {
0061     QQueue<CachedPixmapKey>::iterator key;
0062 
0063     for (key = keys.begin() ; key != keys.end() ; )
0064     {
0065         if (!key->region.contains(region))
0066         {
0067             ++key;
0068             continue;
0069         }
0070 
0071         if (!QPixmapCache::find(key->key, pix))
0072         {
0073             key = keys.erase(key);
0074             continue;
0075         }
0076 
0077         if (key->region == region)
0078         {
0079             *source = QRect();
0080         }
0081         else
0082         {
0083             QPoint startPoint = region.topLeft() - key->region.topLeft();
0084             *source           = QRect(startPoint, region.size());
0085         }
0086 
0087         return true;
0088     }
0089 
0090     return false;
0091 }
0092 
0093 void CachedPixmaps::insert(const QRect& region, const QPixmap& pixmap)
0094 {
0095     if (keys.size() >= maxCount)
0096     {
0097         CachedPixmapKey key = keys.dequeue();
0098         QPixmapCache::remove(key.key);
0099     }
0100 
0101     CachedPixmapKey key;
0102     key.region = region;
0103     key.key    = QPixmapCache::insert(pixmap);
0104     keys.enqueue(key);
0105 }
0106 
0107 // ---------------------------------------------------------------------------------------
0108 
0109 GraphicsDImgItem::GraphicsDImgItem(QGraphicsItem* const parent)
0110     : QGraphicsObject(parent),
0111       d_ptr          (new GraphicsDImgItemPrivate)
0112 {
0113     d_ptr->init(this);
0114 }
0115 
0116 GraphicsDImgItem::GraphicsDImgItem(GraphicsDImgItemPrivate& dd, QGraphicsItem* const parent)
0117     : QGraphicsObject(parent),
0118       d_ptr          (&dd)
0119 {
0120     d_ptr->init(this);
0121 }
0122 
0123 void GraphicsDImgItem::GraphicsDImgItemPrivate::init(GraphicsDImgItem* const q)
0124 {
0125     // ItemCoordinateCache is very slow, DeviceCoordinateCache makes severe render artifacts
0126 
0127     q->setCacheMode(QGraphicsItem::NoCache);
0128 
0129     // This flag is crucial for our performance! Limits redrawing area.
0130 
0131     q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption);
0132     q->setAcceptedMouseButtons(Qt::NoButton);
0133 }
0134 
0135 GraphicsDImgItem::~GraphicsDImgItem()
0136 {
0137     Q_D(GraphicsDImgItem);
0138     delete d;
0139 }
0140 
0141 void GraphicsDImgItem::setImage(const DImg& img)
0142 {
0143     Q_D(GraphicsDImgItem);
0144 
0145     d->image = img;
0146     d->zoomSettings.setImageSize(img.size(), img.originalSize());
0147     d->cachedPixmaps.clear();
0148     sizeHasChanged();
0149 
0150     Q_EMIT imageChanged();
0151 }
0152 
0153 DImg GraphicsDImgItem::image() const
0154 {
0155     Q_D(const GraphicsDImgItem);
0156 
0157     return d->image;
0158 }
0159 
0160 void GraphicsDImgItem::sizeHasChanged()
0161 {
0162     Q_D(GraphicsDImgItem);
0163 
0164     QGraphicsItem::prepareGeometryChange();
0165     d->cachedPixmaps.clear();
0166 
0167     Q_EMIT imageSizeChanged(d->zoomSettings.zoomedSize());
0168 }
0169 
0170 void GraphicsDImgItem::clearCache()
0171 {
0172     Q_D(GraphicsDImgItem);
0173 
0174     d->cachedPixmaps.clear();
0175 }
0176 
0177 const ImageZoomSettings* GraphicsDImgItem::zoomSettings() const
0178 {
0179     Q_D(const GraphicsDImgItem);
0180 
0181     return &d->zoomSettings;
0182 }
0183 
0184 ImageZoomSettings* GraphicsDImgItem::zoomSettings()
0185 {
0186     Q_D(GraphicsDImgItem);
0187 
0188     return &d->zoomSettings;
0189 }
0190 
0191 void GraphicsDImgItem::setDisplayWidget(QWidget* const widget)
0192 {
0193     Q_D(GraphicsDImgItem);
0194 
0195     d->zoomSettings.setDisplayWidget(widget);
0196 }
0197 
0198 QRectF GraphicsDImgItem::boundingRect() const
0199 {
0200     Q_D(const GraphicsDImgItem);
0201 
0202     // always return full integer sizes, we can only scale to integer
0203 
0204     return QRectF(QPointF(0, 0), d->zoomSettings.zoomedSize()).toAlignedRect();
0205 }
0206 
0207 void GraphicsDImgItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
0208 {
0209     Q_D(GraphicsDImgItem);
0210 
0211     QRect   drawRect     = option->exposedRect.intersected(boundingRect()).toAlignedRect();
0212     QRect   pixSourceRect;
0213     QPixmap pix;
0214     QSize   completeSize = boundingRect().size().toSize();
0215 
0216     /* For high resolution ("retina") displays, Mac OS X / Qt
0217      * report only half of the physical resolution in terms of
0218      * pixels, i.e. every logical pixels corresponds to 2x2
0219      * physical pixels. However, UI elements and fonts are
0220      * nevertheless rendered at full resolution, and pixmaps
0221      * as well, provided their resolution is high enough (that
0222      * is, higher than the reported, logical resolution).
0223      *
0224      * To work around this, we render the photos not a logical
0225      * resolution, but with the photo's full resolution, but
0226      * at the screen's aspect ratio. When we later draw this
0227      * high resolution bitmap, it is up to Qt to scale the
0228      * photo to the true physical resolution.  The ratio
0229      * computed below is the ratio between the photo and
0230      * screen resolutions, or equivalently the factor by which
0231      * we need to increase the pixel size of the rendered
0232      * pixmap.
0233      */
0234 
0235     qreal dpr             = widget->devicePixelRatio();
0236 
0237     QRect  scaledDrawRect = QRectF(dpr * drawRect.x(),
0238                                    dpr * drawRect.y(),
0239                                    dpr * drawRect.width(),
0240                                    dpr * drawRect.height()).toRect();
0241 
0242     if (d->cachedPixmaps.find(scaledDrawRect, &pix, &pixSourceRect))
0243     {
0244         if (pixSourceRect.isNull())
0245         {
0246             painter->drawPixmap(drawRect, pix);
0247         }
0248         else
0249         {
0250             painter->drawPixmap(drawRect, pix, pixSourceRect);
0251         }
0252     }
0253     else
0254     {
0255         // scale "as if" scaling to whole image, but clip output to our exposed region
0256 
0257         QSize scaledCompleteSize = QSizeF(dpr * completeSize.width(),
0258                                           dpr * completeSize.height()).toSize();
0259         DImg scaledImage         = d->image.smoothScaleClipped(scaledCompleteSize.width(),
0260                                                                scaledCompleteSize.height(),
0261                                                                scaledDrawRect.x(),
0262                                                                scaledDrawRect.y(),
0263                                                                scaledDrawRect.width(),
0264                                                                scaledDrawRect.height());
0265         pix                      = scaledImage.convertToPixmap();
0266         d->cachedPixmaps.insert(scaledDrawRect, pix);
0267         painter->drawPixmap(drawRect, pix);
0268     }
0269 }
0270 
0271 void GraphicsDImgItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* e)
0272 {
0273     Q_EMIT showContextMenu(e);
0274 }
0275 
0276 } // namespace Digikam
0277 
0278 #include "moc_graphicsdimgitem.cpp"