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"