split_image_renderer.cc (7780B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include "tools/comparison_viewer/split_image_renderer.h" 7 8 #include <QEvent> 9 #include <QGuiApplication> 10 #include <QPainter> 11 #include <QPalette> 12 #include <QPen> 13 #include <QPoint> 14 #include <QRect> 15 #include <algorithm> 16 #include <cmath> 17 #include <utility> 18 19 namespace jpegxl { 20 namespace tools { 21 22 SplitImageRenderer::SplitImageRenderer(QWidget* const parent) 23 : QWidget(parent) { 24 setAttribute(Qt::WA_OpaquePaintEvent); 25 setMouseTracking(true); 26 setFocusPolicy(Qt::WheelFocus); 27 grabKeyboard(); 28 29 connect(&fadingPoint_, &QVariantAnimation::valueChanged, 30 [this] { update(); }); 31 } 32 33 void SplitImageRenderer::setLeftImage(QImage image) { 34 leftImage_ = QPixmap::fromImage(std::move(image)); 35 leftImage_.setDevicePixelRatio(devicePixelRatio()); 36 updateMinimumSize(); 37 update(); 38 } 39 void SplitImageRenderer::setRightImage(QImage image) { 40 rightImage_ = QPixmap::fromImage(std::move(image)); 41 rightImage_.setDevicePixelRatio(devicePixelRatio()); 42 updateMinimumSize(); 43 update(); 44 } 45 void SplitImageRenderer::setMiddleImage(QImage image) { 46 middleImage_ = QPixmap::fromImage(std::move(image)); 47 middleImage_.setDevicePixelRatio(devicePixelRatio()); 48 updateMinimumSize(); 49 update(); 50 } 51 52 void SplitImageRenderer::setRenderingSettings( 53 const SplitImageRenderingSettings& settings) { 54 renderingSettings_ = settings; 55 } 56 57 void SplitImageRenderer::setMiddleWidthPercent(const int percent) { 58 middleWidthPercent_ = percent; 59 update(); 60 } 61 62 void SplitImageRenderer::setZoomLevel(double scale) { 63 scale_ = scale; 64 updateMinimumSize(); 65 update(); 66 } 67 68 void SplitImageRenderer::keyPressEvent(QKeyEvent* const event) { 69 switch (event->key()) { 70 case Qt::Key_Left: 71 setRenderingMode(RenderingMode::LEFT); 72 break; 73 74 case Qt::Key_Right: 75 setRenderingMode(RenderingMode::RIGHT); 76 break; 77 78 case Qt::Key_Up: 79 case Qt::Key_Down: 80 setRenderingMode(RenderingMode::MIDDLE); 81 break; 82 83 case Qt::Key_Escape: 84 QCoreApplication::quit(); 85 break; 86 87 case Qt::Key_ZoomIn: 88 emit zoomLevelIncreaseRequested(); 89 break; 90 case Qt::Key_ZoomOut: 91 emit zoomLevelDecreaseRequested(); 92 break; 93 94 default: 95 QWidget::keyPressEvent(event); 96 break; 97 } 98 update(); 99 } 100 101 void SplitImageRenderer::mouseMoveEvent(QMouseEvent* const event) { 102 setRenderingMode(RenderingMode::SPLIT); 103 middleX_ = event->pos().x(); 104 update(); 105 } 106 107 void SplitImageRenderer::wheelEvent(QWheelEvent* event) { 108 if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { 109 if (event->angleDelta().y() > 0) { 110 emit zoomLevelIncreaseRequested(); 111 return; 112 } else if (event->angleDelta().y() < 0) { 113 emit zoomLevelDecreaseRequested(); 114 return; 115 } 116 } 117 118 event->ignore(); 119 } 120 121 void SplitImageRenderer::paintEvent(QPaintEvent* const event) { 122 QRectF drawingArea(0., 0., minimumWidth(), minimumHeight()); 123 124 QPainter painter(this); 125 painter.fillRect(rect(), QColor(119, 119, 119)); 126 painter.translate(QRectF(rect()).center() - drawingArea.center()); 127 painter.scale(scale_, scale_); 128 if (scale_ < 1.) { 129 painter.setRenderHint(QPainter::SmoothPixmapTransform); 130 } 131 132 const auto drawSingleImage = [&](const RenderingMode mode) { 133 const QPixmap* image = nullptr; 134 switch (mode) { 135 case RenderingMode::LEFT: 136 image = &leftImage_; 137 break; 138 case RenderingMode::RIGHT: 139 image = &rightImage_; 140 break; 141 case RenderingMode::MIDDLE: 142 image = &middleImage_; 143 break; 144 145 default: 146 return; 147 } 148 painter.drawPixmap(QPointF(0., 0.), *image); 149 }; 150 151 if (mode_ != RenderingMode::SPLIT) { 152 if (fadingPoint_.state() != QAbstractAnimation::Running) { 153 drawSingleImage(mode_); 154 return; 155 } 156 157 const float fadingPoint = fadingPoint_.currentValue().toFloat(); 158 if (renderingSettings_.gray) { 159 if (fadingPoint < renderingSettings_.fadingMSecs) { 160 painter.setOpacity((renderingSettings_.fadingMSecs - fadingPoint) / 161 renderingSettings_.fadingMSecs); 162 drawSingleImage(previousMode_); 163 } else if (fadingPoint > renderingSettings_.fadingMSecs + 164 renderingSettings_.grayMSecs) { 165 painter.setOpacity((fadingPoint - renderingSettings_.fadingMSecs - 166 renderingSettings_.grayMSecs) / 167 renderingSettings_.fadingMSecs); 168 drawSingleImage(mode_); 169 } 170 } else { 171 drawSingleImage(previousMode_); 172 painter.setOpacity(fadingPoint / renderingSettings_.fadingMSecs); 173 drawSingleImage(mode_); 174 } 175 176 return; 177 } 178 179 const qreal middleWidth = 180 std::min<qreal>((minimumWidth() / scale_) * middleWidthPercent_ / 100., 181 middleImage_.width()); 182 183 const double transformedMiddleX = 184 painter.transform().inverted().map(QPointF(middleX_, 0.)).x(); 185 QRectF middleRect = middleImage_.rect(); 186 middleRect.setWidth(middleWidth); 187 middleRect.moveCenter(QPointF(transformedMiddleX * devicePixelRatio(), 188 middleRect.center().y())); 189 middleRect.setLeft(std::round(middleRect.left())); 190 middleRect.setRight(std::round(middleRect.right())); 191 192 QRectF leftRect = leftImage_.rect(); 193 leftRect.setRight(middleRect.left()); 194 195 QRectF rightRect = rightImage_.rect(); 196 rightRect.setLeft(middleRect.right()); 197 198 painter.drawPixmap(QPointF(), leftImage_, leftRect); 199 painter.drawPixmap(middleRect.topLeft() / devicePixelRatio(), middleImage_, 200 middleRect); 201 painter.drawPixmap(rightRect.topLeft() / devicePixelRatio(), rightImage_, 202 rightRect); 203 204 QPen middlePen; 205 middlePen.setStyle(Qt::DotLine); 206 painter.setPen(middlePen); 207 painter.drawLine(leftRect.topRight() / devicePixelRatio(), 208 leftRect.bottomRight() / devicePixelRatio()); 209 painter.drawLine(rightRect.topLeft() / devicePixelRatio(), 210 rightRect.bottomLeft() / devicePixelRatio()); 211 } 212 213 void SplitImageRenderer::updateMinimumSize() { 214 const QSizeF leftSize = leftImage_.deviceIndependentSize(); 215 const QSizeF rightSize = rightImage_.deviceIndependentSize(); 216 const QSizeF middleSize = middleImage_.deviceIndependentSize(); 217 const qreal imagesWidth = std::max( 218 std::max(leftSize.width(), rightSize.width()), middleSize.width()); 219 const qreal imagesHeight = std::max( 220 std::max(leftSize.height(), rightSize.height()), middleSize.height()); 221 setMinimumSize((scale_ * QSizeF(imagesWidth, imagesHeight)).toSize()); 222 } 223 224 void SplitImageRenderer::setRenderingMode(const RenderingMode newMode) { 225 if (newMode == mode_) return; 226 previousMode_ = mode_; 227 mode_ = newMode; 228 if (previousMode_ == RenderingMode::SPLIT || mode_ == RenderingMode::SPLIT) { 229 fadingPoint_.stop(); 230 } else { 231 const int msecs = 232 renderingSettings_.gray 233 ? 2 * renderingSettings_.fadingMSecs + renderingSettings_.grayMSecs 234 : renderingSettings_.fadingMSecs; 235 const float startValue = fadingPoint_.state() == QAbstractAnimation::Running 236 ? fadingPoint_.endValue().toFloat() - 237 fadingPoint_.currentValue().toFloat() 238 : 0.f; 239 fadingPoint_.stop(); 240 fadingPoint_.setStartValue(startValue); 241 fadingPoint_.setEndValue(static_cast<float>(msecs)); 242 fadingPoint_.setDuration(fadingPoint_.endValue().toFloat() - 243 fadingPoint_.startValue().toFloat()); 244 fadingPoint_.start(); 245 } 246 emit renderingModeChanged(mode_); 247 } 248 249 } // namespace tools 250 } // namespace jpegxl