Warning, file /education/parley/src/practice/imagewidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2009 Daniel Laidig <d.laidig@gmx.de> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "imagewidget.h" 0007 #include <config-parley.h> 0008 0009 #include <QPaintEngine> 0010 #include <QPainter> 0011 #include <QTimeLine> 0012 #include <QTimer> 0013 0014 #include <QDebug> 0015 0016 #if defined(Q_WS_X11) 0017 #include <QX11Info> 0018 #include <X11/Xlib.h> 0019 #include <X11/extensions/Xrender.h> 0020 #undef KeyPress 0021 #undef FocusOut 0022 #endif 0023 0024 using namespace Practice; 0025 0026 // The functions centerPixmaps() and transition() are copied from kdelibs/plasma/paintutils.cpp, revision 1133527 0027 // License: LGPLv2+ 0028 // SPDX-FileCopyrightText: 2005 Aaron Seigo <aseigo@kde.org> 0029 // SPDX-FileCopyrightText: 2008 Andrew Lake <jamboarder@yahoo.com> 0030 // Don't just modify the code here, if there are issues they should probably also be fixed in libplasma. 0031 0032 void centerPixmaps(QPixmap &from, QPixmap &to) 0033 { 0034 if (from.size() == to.size() && from.hasAlphaChannel() && to.hasAlphaChannel()) { 0035 return; 0036 } 0037 QRect fromRect(from.rect()); 0038 QRect toRect(to.rect()); 0039 0040 QRect actualRect = QRect(QPoint(0, 0), fromRect.size().expandedTo(toRect.size())); 0041 fromRect.moveCenter(actualRect.center()); 0042 toRect.moveCenter(actualRect.center()); 0043 0044 if (from.size() != actualRect.size() || !from.hasAlphaChannel()) { 0045 QPixmap result(actualRect.size()); 0046 result.fill(Qt::transparent); 0047 QPainter p(&result); 0048 p.setCompositionMode(QPainter::CompositionMode_Source); 0049 p.drawPixmap(fromRect.topLeft(), from); 0050 p.end(); 0051 from = result; 0052 } 0053 0054 if (to.size() != actualRect.size() || !to.hasAlphaChannel()) { 0055 QPixmap result(actualRect.size()); 0056 result.fill(Qt::transparent); 0057 QPainter p(&result); 0058 p.setCompositionMode(QPainter::CompositionMode_Source); 0059 p.drawPixmap(toRect.topLeft(), to); 0060 p.end(); 0061 to = result; 0062 } 0063 } 0064 0065 QPixmap transition(const QPixmap &from, const QPixmap &to, qreal amount) 0066 { 0067 if (from.isNull() && to.isNull()) { 0068 return from; 0069 } 0070 0071 QPixmap startPixmap(from); 0072 QPixmap targetPixmap(to); 0073 0074 if (from.size() != to.size() || !from.hasAlphaChannel() || !to.hasAlphaChannel()) { 0075 centerPixmaps(startPixmap, targetPixmap); 0076 } 0077 0078 // paint to in the center of from 0079 QRect toRect = to.rect(); 0080 0081 QColor color; 0082 color.setAlphaF(amount); 0083 0084 // If the native paint engine supports Porter/Duff compositing and CompositionMode_Plus 0085 QPaintEngine *paintEngine = from.paintEngine(); 0086 if (paintEngine && paintEngine->hasFeature(QPaintEngine::PorterDuff) && paintEngine->hasFeature(QPaintEngine::BlendModes)) { 0087 QPainter p; 0088 p.begin(&targetPixmap); 0089 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0090 p.fillRect(targetPixmap.rect(), color); 0091 p.end(); 0092 0093 p.begin(&startPixmap); 0094 p.setCompositionMode(QPainter::CompositionMode_DestinationOut); 0095 p.fillRect(startPixmap.rect(), color); 0096 p.setCompositionMode(QPainter::CompositionMode_Plus); 0097 p.drawPixmap(toRect.topLeft(), targetPixmap); 0098 p.end(); 0099 0100 return startPixmap; 0101 } 0102 #if defined(Q_WS_X11) 0103 // We have Xrender support 0104 else if (paintEngine && paintEngine->hasFeature(QPaintEngine::PorterDuff)) { 0105 // QX11PaintEngine doesn't implement CompositionMode_Plus in Qt 4.3, 0106 // which we need to be able to do a transition from one pixmap to 0107 // another. 0108 // 0109 // In order to avoid the overhead of converting the pixmaps to images 0110 // and doing the operation entirely in software, this function has a 0111 // specialized path for X11 that uses Xrender directly to do the 0112 // transition. This operation can be fully accelerated in HW. 0113 // 0114 // This specialization can be removed when QX11PaintEngine supports 0115 // CompositionMode_Plus. 0116 QPixmap source(targetPixmap), destination(startPixmap); 0117 0118 source.detach(); 0119 destination.detach(); 0120 0121 Display *dpy = QX11Info::display(); 0122 0123 XRenderPictFormat *format = XRenderFindStandardFormat(dpy, PictStandardA8); 0124 XRenderPictureAttributes pa; 0125 pa.repeat = 1; // RepeatNormal 0126 0127 // Create a 1x1 8 bit repeating alpha picture 0128 Pixmap pixmap = XCreatePixmap(dpy, destination.handle(), 1, 1, 8); 0129 Picture alpha = XRenderCreatePicture(dpy, pixmap, format, CPRepeat, &pa); 0130 XFreePixmap(dpy, pixmap); 0131 0132 // Fill the alpha picture with the opacity value 0133 XRenderColor xcolor; 0134 xcolor.alpha = quint16(0xffff * amount); 0135 XRenderFillRectangle(dpy, PictOpSrc, alpha, &xcolor, 0, 0, 1, 1); 0136 0137 // Reduce the alpha of the destination with 1 - opacity 0138 XRenderComposite(dpy, PictOpOutReverse, alpha, None, destination.x11PictureHandle(), 0, 0, 0, 0, 0, 0, destination.width(), destination.height()); 0139 0140 // Add source * opacity to the destination 0141 XRenderComposite(dpy, 0142 PictOpAdd, 0143 source.x11PictureHandle(), 0144 alpha, 0145 destination.x11PictureHandle(), 0146 toRect.x(), 0147 toRect.y(), 0148 0, 0149 0, 0150 0, 0151 0, 0152 destination.width(), 0153 destination.height()); 0154 0155 XRenderFreePicture(dpy, alpha); 0156 return destination; 0157 } 0158 #endif 0159 else { 0160 // Fall back to using QRasterPaintEngine to do the transition. 0161 QImage under = startPixmap.toImage(); 0162 QImage over = targetPixmap.toImage(); 0163 0164 QPainter p; 0165 p.begin(&over); 0166 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0167 p.fillRect(over.rect(), color); 0168 p.end(); 0169 0170 p.begin(&under); 0171 p.setCompositionMode(QPainter::CompositionMode_DestinationOut); 0172 p.fillRect(under.rect(), color); 0173 p.setCompositionMode(QPainter::CompositionMode_Plus); 0174 p.drawImage(toRect.topLeft(), over); 0175 p.end(); 0176 0177 return QPixmap::fromImage(under); 0178 } 0179 } 0180 0181 ImageWidget::ImageWidget(QWidget *parent) 0182 : QWidget(parent) 0183 { 0184 m_scaleTimer = new QTimer(this); 0185 m_scaleTimer->setSingleShot(true); 0186 m_scaleTimer->setInterval(500); 0187 0188 m_animation = new QTimeLine(300, this); 0189 0190 m_scaledPixmapOutOfDate = false; 0191 connect(m_scaleTimer, SIGNAL(timeout()), this, SLOT(scalePixmap())); 0192 connect(m_animation, SIGNAL(valueChanged(qreal)), this, SLOT(update())); 0193 connect(m_animation, &QTimeLine::finished, this, &ImageWidget::animationFinished); 0194 } 0195 0196 void ImageWidget::setPixmap(const QPixmap &pixmap) 0197 { 0198 // qDebug() << "set new pixmap, size:" << pixmap.size(); 0199 if (m_animation->state() == QTimeLine::Running) { 0200 m_scaledPixmap = transition(m_animationPixmap, m_scaledPixmap, m_animation->currentValue()); 0201 m_animation->stop(); 0202 animationFinished(); 0203 } 0204 0205 m_animationPixmap = m_scaledPixmap; 0206 m_originalPixmap = pixmap; 0207 m_scaledPixmap = QPixmap(); 0208 m_scaledBackupPixmap = QPixmap(); 0209 m_scaledPixmapOutOfDate = true; 0210 if (!m_scaling) { 0211 m_scaledPixmap = pixmap; 0212 } 0213 scalePixmap(true); 0214 if (m_fading) { 0215 m_animation->start(); 0216 } 0217 update(); 0218 } 0219 0220 void ImageWidget::setScalingEnabled(bool scaling, bool onlyDownscaling) 0221 { 0222 m_scaling = scaling; 0223 m_onlyDownscaling = onlyDownscaling; 0224 } 0225 0226 void ImageWidget::setKeepAspectRatio(Qt::AspectRatioMode mode) 0227 { 0228 m_keepAspectRatio = mode; 0229 } 0230 0231 void ImageWidget::setFadingEnabled(bool fading) 0232 { 0233 m_fading = fading; 0234 } 0235 0236 void ImageWidget::setAlignment(Qt::Alignment alignment) 0237 { 0238 m_alignment = alignment; 0239 } 0240 0241 void ImageWidget::paintEvent(QPaintEvent *e) 0242 { 0243 QWidget::paintEvent(e); 0244 QPainter painter(this); 0245 if (m_scaling && m_scaledPixmapOutOfDate) { 0246 m_scaleTimer->start(); 0247 scalePixmap(false); 0248 } 0249 QPixmap pm = m_scaledPixmap; 0250 if (m_animation->state() == QTimeLine::Running) { 0251 pm = transition(m_animationPixmap, m_scaledPixmap, m_animation->currentValue()); 0252 } 0253 0254 int x = (size().width() - pm.width()) / 2; 0255 if (m_alignment.testFlag(Qt::AlignLeft)) { 0256 x = 0; 0257 } else if (m_alignment.testFlag(Qt::AlignRight)) { 0258 x = size().width() - pm.width(); 0259 } 0260 int y = (size().height() - pm.height()) / 2; 0261 if (m_alignment.testFlag(Qt::AlignTop)) { 0262 y = 0; 0263 } else if (m_alignment.testFlag(Qt::AlignBottom)) { 0264 y = size().height() - pm.height(); 0265 } 0266 painter.drawPixmap(x, y, pm); 0267 } 0268 0269 void ImageWidget::resizeEvent(QResizeEvent *e) 0270 { 0271 if (!m_scaledPixmapOutOfDate) { 0272 m_scaledBackupPixmap = m_scaledPixmap; 0273 } 0274 // stop animations when resizing 0275 if (m_animation->state() == QTimeLine::Running) { 0276 m_animation->stop(); 0277 animationFinished(); 0278 } 0279 m_scaledPixmapOutOfDate = true; 0280 QWidget::resizeEvent(e); 0281 Q_EMIT sizeChanged(); 0282 } 0283 0284 void ImageWidget::scalePixmap(bool smooth) 0285 { 0286 bool scaleUp = m_originalPixmap.width() <= size().width() && m_originalPixmap.height() <= size().height(); 0287 if ((m_onlyDownscaling && scaleUp) || m_originalPixmap.size() == size()) { 0288 // qDebug() << "no need to scale pixmap"; 0289 m_scaledPixmapOutOfDate = false; 0290 m_scaledPixmap = m_originalPixmap; 0291 m_scaledBackupPixmap = QPixmap(); 0292 } else if (smooth) { 0293 // qDebug() << "smooth scaling to" << size(); 0294 if (m_originalPixmap.isNull() || size().isEmpty()) { 0295 m_scaledPixmapOutOfDate = false; 0296 m_scaledPixmap = QPixmap(); 0297 update(); 0298 return; 0299 } 0300 m_scaledPixmap = m_originalPixmap.scaled(size(), m_keepAspectRatio, Qt::SmoothTransformation); 0301 m_scaledBackupPixmap = QPixmap(); 0302 m_scaledPixmapOutOfDate = false; 0303 update(); 0304 } else { 0305 // qDebug() << "fast scaling to" << size(); 0306 // Try to find out if it makes sense to use the scaled backup pixmap. 0307 // If the scaled backup gets too small, we use the original image. 0308 float ratio = 0; 0309 if (!size().isEmpty()) { 0310 ratio = qMin(float(m_scaledBackupPixmap.width()) / size().width(), float(m_scaledBackupPixmap.height()) / size().height()); 0311 } 0312 if (ratio > 0.4 && !m_scaledBackupPixmap.isNull()) { 0313 m_scaledPixmap = m_scaledBackupPixmap.scaled(size(), m_keepAspectRatio, Qt::FastTransformation); 0314 } else { 0315 if (m_originalPixmap.isNull() || size().isEmpty()) { 0316 m_scaledPixmap = QPixmap(); 0317 return; 0318 } 0319 // use the original pixmap 0320 m_scaledPixmap = m_originalPixmap.scaled(size(), m_keepAspectRatio, Qt::FastTransformation); 0321 m_scaledBackupPixmap = m_scaledPixmap; 0322 } 0323 m_scaledPixmapOutOfDate = true; 0324 } 0325 } 0326 0327 void ImageWidget::animationFinished() 0328 { 0329 m_animationPixmap = QPixmap(); 0330 }