split_view.cc (5669B)
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/flicker_test/split_view.h" 7 8 #include <QMouseEvent> 9 #include <QPainter> 10 11 namespace jpegxl { 12 namespace tools { 13 14 SplitView::SplitView(QWidget* const parent) 15 : QWidget(parent), g_(std::random_device()()) { 16 blankingTimer_.setSingleShot(true); 17 blankingTimer_.setTimerType(Qt::PreciseTimer); 18 viewingTimer_.setSingleShot(true); 19 viewingTimer_.setTimerType(Qt::PreciseTimer); 20 flicker_.setLoopCount(-1); 21 connect(&blankingTimer_, &QTimer::timeout, this, &SplitView::startDisplaying); 22 connect(&flicker_, &QVariantAnimation::valueChanged, this, [&] { 23 if (gray_) { 24 update(); 25 } 26 }); 27 connect(&flicker_, &QAbstractAnimation::currentLoopChanged, [&] { 28 showingAltered_ = !showingAltered_; 29 update(); 30 }); 31 connect(&viewingTimer_, &QTimer::timeout, [&] { 32 flicker_.stop(); 33 original_.fill(Qt::black); 34 altered_.fill(Qt::black); 35 update(); 36 }); 37 } 38 39 void SplitView::setOriginalImage(QImage image) { 40 original_ = QPixmap::fromImage(std::move(image)); 41 original_.setDevicePixelRatio(devicePixelRatio()); 42 updateMinimumSize(); 43 update(); 44 } 45 46 void SplitView::setAlteredImage(QImage image) { 47 altered_ = QPixmap::fromImage(std::move(image)); 48 altered_.setDevicePixelRatio(devicePixelRatio()); 49 updateMinimumSize(); 50 update(); 51 } 52 53 void SplitView::setSpacing(int spacing) { 54 spacing_ = spacing; 55 updateMinimumSize(); 56 update(); 57 } 58 59 void SplitView::startTest(QString imageName, const int blankingTimeMSecs, 60 const int viewingTimeSecs, const int advanceTimeMSecs, 61 const bool gray, const int grayFadingTimeMSecs, 62 const int grayTimeMSecs) { 63 imageName_ = std::move(imageName); 64 std::bernoulli_distribution bernoulli; 65 originalSide_ = bernoulli(g_) ? Side::kLeft : Side::kRight; 66 viewingTimer_.setInterval(1000 * viewingTimeSecs); 67 68 flicker_.setDuration(advanceTimeMSecs); 69 gray_ = gray; 70 QVariantAnimation::KeyValues keyValues; 71 if (gray_) { 72 keyValues << QVariantAnimation::KeyValue(0., 0.f) 73 << QVariantAnimation::KeyValue( 74 static_cast<float>(grayFadingTimeMSecs) / advanceTimeMSecs, 75 1.f) 76 << QVariantAnimation::KeyValue( 77 static_cast<float>(advanceTimeMSecs - grayTimeMSecs - 78 grayFadingTimeMSecs) / 79 advanceTimeMSecs, 80 1.f) 81 << QVariantAnimation::KeyValue( 82 static_cast<float>(advanceTimeMSecs - grayTimeMSecs) / 83 advanceTimeMSecs, 84 0.f) 85 << QVariantAnimation::KeyValue(1.f, 0.f); 86 } else { 87 keyValues << QVariantAnimation::KeyValue(0., 1.f) 88 << QVariantAnimation::KeyValue(1., 1.f); 89 } 90 flicker_.setKeyValues(keyValues); 91 92 state_ = State::kBlanking; 93 blankingTimer_.start(blankingTimeMSecs); 94 } 95 96 void SplitView::mousePressEvent(QMouseEvent* const event) { 97 if (state_ != State::kDisplaying) return; 98 99 if (leftRect_.contains(event->pos())) { 100 clicking_ = true; 101 clickedSide_ = Side::kLeft; 102 } else if (rightRect_.contains(event->pos())) { 103 clicking_ = true; 104 clickedSide_ = Side::kRight; 105 } 106 } 107 108 void SplitView::mouseReleaseEvent(QMouseEvent* const event) { 109 if (!clicking_) return; 110 clicking_ = false; 111 112 const int clickDelayMSecs = viewingStartTime_.elapsed(); 113 114 if ((clickedSide_ == Side::kLeft && !leftRect_.contains(event->pos())) || 115 (clickedSide_ == Side::kRight && !rightRect_.contains(event->pos()))) { 116 return; 117 } 118 119 flicker_.stop(); 120 viewingTimer_.stop(); 121 state_ = State::kBlanking; 122 update(); 123 124 emit testResult(imageName_, originalSide_, clickedSide_, clickDelayMSecs); 125 } 126 127 void SplitView::paintEvent(QPaintEvent* const event) { 128 QPainter painter(this); 129 painter.fillRect(rect(), QColor(119, 119, 119)); 130 131 if (state_ == State::kBlanking) return; 132 133 if (gray_ && flicker_.state() == QAbstractAnimation::Running) { 134 painter.setOpacity(flicker_.currentValue().toFloat()); 135 } 136 137 const auto imageForSide = [&](const Side side) { 138 if (side == originalSide_) return &original_; 139 return showingAltered_ ? &altered_ : &original_; 140 }; 141 142 QPixmap* const leftImage = imageForSide(Side::kLeft); 143 QPixmap* const rightImage = imageForSide(Side::kRight); 144 145 leftRect_ = QRectF(QPoint(), leftImage->deviceIndependentSize()); 146 leftRect_.moveCenter(rect().center()); 147 leftRect_.moveRight(rect().center().x() - 148 (spacing_ / 2 + spacing_ % 2) / devicePixelRatio()); 149 painter.drawPixmap(leftRect_.topLeft(), *leftImage); 150 151 rightRect_ = QRectF(QPoint(), rightImage->deviceIndependentSize()); 152 rightRect_.moveCenter(rect().center()); 153 rightRect_.moveLeft(rect().center().x() + 154 (spacing_ / 2) / devicePixelRatio()); 155 painter.drawPixmap(rightRect_.topLeft(), *rightImage); 156 } 157 158 void SplitView::startDisplaying() { 159 state_ = State::kDisplaying; 160 flicker_.start(); 161 viewingStartTime_.start(); 162 if (viewingTimer_.interval() > 0) { 163 viewingTimer_.start(); 164 } 165 } 166 167 void SplitView::updateMinimumSize() { 168 setMinimumWidth(2 * std::max(original_.deviceIndependentSize().width(), 169 altered_.deviceIndependentSize().width()) + 170 spacing_ / devicePixelRatio()); 171 setMinimumHeight(std::max(original_.deviceIndependentSize().height(), 172 altered_.deviceIndependentSize().height())); 173 } 174 175 } // namespace tools 176 } // namespace jpegxl