1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the demonstration applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "composition.h"
52#include <QBoxLayout>
53#include <QRadioButton>
54#include <QTimer>
55#include <QDateTime>
56#include <QSlider>
57#include <QMouseEvent>
58#include <qmath.h>
59
60#if QT_CONFIG(opengl)
61#include <QOpenGLFunctions>
62#include <QOpenGLWindow>
63#endif
64
65const int animationInterval = 15; // update every 16 ms = ~60FPS
66
67CompositionWidget::CompositionWidget(QWidget *parent)
68 : QWidget(parent)
69{
70 CompositionRenderer *view = new CompositionRenderer(this);
71
72 QGroupBox *mainGroup = new QGroupBox(parent);
73 mainGroup->setTitle(tr(s: "Composition Modes"));
74
75 QGroupBox *modesGroup = new QGroupBox(mainGroup);
76 modesGroup->setTitle(tr(s: "Mode"));
77
78 rbClear = new QRadioButton(tr(s: "Clear"), modesGroup);
79 connect(sender: rbClear, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setClearMode);
80 rbSource = new QRadioButton(tr(s: "Source"), modesGroup);
81 connect(sender: rbSource, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSourceMode);
82 rbDest = new QRadioButton(tr(s: "Destination"), modesGroup);
83 connect(sender: rbDest, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDestMode);
84 rbSourceOver = new QRadioButton(tr(s: "Source Over"), modesGroup);
85 connect(sender: rbSourceOver, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSourceOverMode);
86 rbDestOver = new QRadioButton(tr(s: "Destination Over"), modesGroup);
87 connect(sender: rbDestOver, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDestOverMode);
88 rbSourceIn = new QRadioButton(tr(s: "Source In"), modesGroup);
89 connect(sender: rbSourceIn, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSourceInMode);
90 rbDestIn = new QRadioButton(tr(s: "Dest In"), modesGroup);
91 connect(sender: rbDestIn, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDestInMode);
92 rbSourceOut = new QRadioButton(tr(s: "Source Out"), modesGroup);
93 connect(sender: rbSourceOut, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSourceOutMode);
94 rbDestOut = new QRadioButton(tr(s: "Dest Out"), modesGroup);
95 connect(sender: rbDestOut, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDestOutMode);
96 rbSourceAtop = new QRadioButton(tr(s: "Source Atop"), modesGroup);
97 connect(sender: rbSourceAtop, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSourceAtopMode);
98 rbDestAtop = new QRadioButton(tr(s: "Dest Atop"), modesGroup);
99 connect(sender: rbDestAtop, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDestAtopMode);
100 rbXor = new QRadioButton(tr(s: "Xor"), modesGroup);
101 connect(sender: rbXor, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setXorMode);
102
103 rbPlus = new QRadioButton(tr(s: "Plus"), modesGroup);
104 connect(sender: rbPlus, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setPlusMode);
105 rbMultiply = new QRadioButton(tr(s: "Multiply"), modesGroup);
106 connect(sender: rbMultiply, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setMultiplyMode);
107 rbScreen = new QRadioButton(tr(s: "Screen"), modesGroup);
108 connect(sender: rbScreen, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setScreenMode);
109 rbOverlay = new QRadioButton(tr(s: "Overlay"), modesGroup);
110 connect(sender: rbOverlay, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setOverlayMode);
111 rbDarken = new QRadioButton(tr(s: "Darken"), modesGroup);
112 connect(sender: rbDarken, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDarkenMode);
113 rbLighten = new QRadioButton(tr(s: "Lighten"), modesGroup);
114 connect(sender: rbLighten, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setLightenMode);
115 rbColorDodge = new QRadioButton(tr(s: "Color Dodge"), modesGroup);
116 connect(sender: rbColorDodge, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setColorDodgeMode);
117 rbColorBurn = new QRadioButton(tr(s: "Color Burn"), modesGroup);
118 connect(sender: rbColorBurn, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setColorBurnMode);
119 rbHardLight = new QRadioButton(tr(s: "Hard Light"), modesGroup);
120 connect(sender: rbHardLight, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setHardLightMode);
121 rbSoftLight = new QRadioButton(tr(s: "Soft Light"), modesGroup);
122 connect(sender: rbSoftLight, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setSoftLightMode);
123 rbDifference = new QRadioButton(tr(s: "Difference"), modesGroup);
124 connect(sender: rbDifference, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setDifferenceMode);
125 rbExclusion = new QRadioButton(tr(s: "Exclusion"), modesGroup);
126 connect(sender: rbExclusion, signal: &QAbstractButton::clicked, receiver: view, slot: &CompositionRenderer::setExclusionMode);
127
128 QGroupBox *circleColorGroup = new QGroupBox(mainGroup);
129 circleColorGroup->setTitle(tr(s: "Circle color"));
130 QSlider *circleColorSlider = new QSlider(Qt::Horizontal, circleColorGroup);
131 circleColorSlider->setRange(min: 0, max: 359);
132 circleColorSlider->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed);
133 connect(sender: circleColorSlider, signal: &QAbstractSlider::valueChanged, receiver: view, slot: &CompositionRenderer::setCircleColor);
134
135 QGroupBox *circleAlphaGroup = new QGroupBox(mainGroup);
136 circleAlphaGroup->setTitle(tr(s: "Circle alpha"));
137 QSlider *circleAlphaSlider = new QSlider(Qt::Horizontal, circleAlphaGroup);
138 circleAlphaSlider->setRange(min: 0, max: 255);
139 circleAlphaSlider->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed);
140 connect(sender: circleAlphaSlider, signal: &QAbstractSlider::valueChanged, receiver: view, slot: &CompositionRenderer::setCircleAlpha);
141
142 QPushButton *showSourceButton = new QPushButton(mainGroup);
143 showSourceButton->setText(tr(s: "Show Source"));
144#if QT_CONFIG(opengl)
145 QPushButton *enableOpenGLButton = new QPushButton(mainGroup);
146 enableOpenGLButton->setText(tr(s: "Use OpenGL"));
147 enableOpenGLButton->setCheckable(true);
148 enableOpenGLButton->setChecked(view->usesOpenGL());
149#endif
150 QPushButton *whatsThisButton = new QPushButton(mainGroup);
151 whatsThisButton->setText(tr(s: "What's This?"));
152 whatsThisButton->setCheckable(true);
153
154 QPushButton *animateButton = new QPushButton(mainGroup);
155 animateButton->setText(tr(s: "Animated"));
156 animateButton->setCheckable(true);
157 animateButton->setChecked(true);
158
159 QHBoxLayout *viewLayout = new QHBoxLayout(this);
160 viewLayout->addWidget(view);
161 viewLayout->addWidget(mainGroup);
162
163 QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
164 mainGroupLayout->addWidget(circleColorGroup);
165 mainGroupLayout->addWidget(circleAlphaGroup);
166 mainGroupLayout->addWidget(modesGroup);
167 mainGroupLayout->addStretch();
168 mainGroupLayout->addWidget(animateButton);
169 mainGroupLayout->addWidget(whatsThisButton);
170 mainGroupLayout->addWidget(showSourceButton);
171#if QT_CONFIG(opengl)
172 mainGroupLayout->addWidget(enableOpenGLButton);
173#endif
174
175 QGridLayout *modesLayout = new QGridLayout(modesGroup);
176 modesLayout->addWidget(rbClear, row: 0, column: 0);
177 modesLayout->addWidget(rbSource, row: 1, column: 0);
178 modesLayout->addWidget(rbDest, row: 2, column: 0);
179 modesLayout->addWidget(rbSourceOver, row: 3, column: 0);
180 modesLayout->addWidget(rbDestOver, row: 4, column: 0);
181 modesLayout->addWidget(rbSourceIn, row: 5, column: 0);
182 modesLayout->addWidget(rbDestIn, row: 6, column: 0);
183 modesLayout->addWidget(rbSourceOut, row: 7, column: 0);
184 modesLayout->addWidget(rbDestOut, row: 8, column: 0);
185 modesLayout->addWidget(rbSourceAtop, row: 9, column: 0);
186 modesLayout->addWidget(rbDestAtop, row: 10, column: 0);
187 modesLayout->addWidget(rbXor, row: 11, column: 0);
188
189 modesLayout->addWidget(rbPlus, row: 0, column: 1);
190 modesLayout->addWidget(rbMultiply, row: 1, column: 1);
191 modesLayout->addWidget(rbScreen, row: 2, column: 1);
192 modesLayout->addWidget(rbOverlay, row: 3, column: 1);
193 modesLayout->addWidget(rbDarken, row: 4, column: 1);
194 modesLayout->addWidget(rbLighten, row: 5, column: 1);
195 modesLayout->addWidget(rbColorDodge, row: 6, column: 1);
196 modesLayout->addWidget(rbColorBurn, row: 7, column: 1);
197 modesLayout->addWidget(rbHardLight, row: 8, column: 1);
198 modesLayout->addWidget(rbSoftLight, row: 9, column: 1);
199 modesLayout->addWidget(rbDifference, row: 10, column: 1);
200 modesLayout->addWidget(rbExclusion, row: 11, column: 1);
201
202
203 QVBoxLayout *circleColorLayout = new QVBoxLayout(circleColorGroup);
204 circleColorLayout->addWidget(circleColorSlider);
205
206 QVBoxLayout *circleAlphaLayout = new QVBoxLayout(circleAlphaGroup);
207 circleAlphaLayout->addWidget(circleAlphaSlider);
208
209 view->loadDescription(filename: ":res/composition/composition.html");
210 view->loadSourceFile(fileName: ":res/composition/composition.cpp");
211
212 connect(sender: whatsThisButton, signal: &QAbstractButton::clicked, receiver: view, slot: &ArthurFrame::setDescriptionEnabled);
213 connect(sender: view, signal: &ArthurFrame::descriptionEnabledChanged, receiver: whatsThisButton, slot: &QAbstractButton::setChecked);
214 connect(sender: showSourceButton, signal: &QAbstractButton::clicked, receiver: view, slot: &ArthurFrame::showSource);
215#if QT_CONFIG(opengl)
216 connect(sender: enableOpenGLButton, signal: &QAbstractButton::clicked, receiver: view, slot: &ArthurFrame::enableOpenGL);
217#endif
218 connect(sender: animateButton, signal: &QAbstractButton::toggled, receiver: view, slot: &CompositionRenderer::setAnimationEnabled);
219
220 circleColorSlider->setValue(270);
221 circleAlphaSlider->setValue(200);
222 rbSourceOut->animateClick();
223
224 setWindowTitle(tr(s: "Composition Modes"));
225}
226
227CompositionWidget::~CompositionWidget()
228{
229}
230
231
232void CompositionWidget::nextMode()
233{
234 /*
235 if (!m_animation_enabled)
236 return;
237 if (rbClear->isChecked()) rbSource->animateClick();
238 if (rbSource->isChecked()) rbDest->animateClick();
239 if (rbDest->isChecked()) rbSourceOver->animateClick();
240 if (rbSourceOver->isChecked()) rbDestOver->animateClick();
241 if (rbDestOver->isChecked()) rbSourceIn->animateClick();
242 if (rbSourceIn->isChecked()) rbDestIn->animateClick();
243 if (rbDestIn->isChecked()) rbSourceOut->animateClick();
244 if (rbSourceOut->isChecked()) rbDestOut->animateClick();
245 if (rbDestOut->isChecked()) rbSourceAtop->animateClick();
246 if (rbSourceAtop->isChecked()) rbDestAtop->animateClick();
247 if (rbDestAtop->isChecked()) rbXor->animateClick();
248 if (rbXor->isChecked()) rbClear->animateClick();
249 */
250}
251
252CompositionRenderer::CompositionRenderer(QWidget *parent)
253 : ArthurFrame(parent)
254{
255 m_animation_enabled = true;
256 m_animationTimer = startTimer(interval: animationInterval);
257 m_image = QImage(":res/composition/flower.jpg");
258 m_image.setAlphaChannel(QImage(":res/composition/flower_alpha.jpg"));
259 m_circle_alpha = 127;
260 m_circle_hue = 255;
261 m_current_object = NoObject;
262 m_composition_mode = QPainter::CompositionMode_SourceOut;
263
264 m_circle_pos = QPoint(200, 100);
265
266 setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding);
267#if QT_CONFIG(opengl)
268 m_pbuffer_size = 1024;
269#endif
270}
271
272CompositionRenderer::~CompositionRenderer()
273{
274}
275
276QRectF rectangle_around(const QPointF &p, const QSizeF &size = QSize(250, 200))
277{
278 QRectF rect(p, size);
279 rect.translate(dx: -size.width()/2, dy: -size.height()/2);
280 return rect;
281}
282
283void CompositionRenderer::setAnimationEnabled(bool enabled)
284{
285 if (m_animation_enabled == enabled)
286 return;
287 m_animation_enabled = enabled;
288 if (enabled) {
289 Q_ASSERT(!m_animationTimer);
290 m_animationTimer = startTimer(interval: animationInterval);
291 } else {
292 killTimer(id: m_animationTimer);
293 m_animationTimer = 0;
294 }
295}
296
297void CompositionRenderer::updateCirclePos()
298{
299 if (m_current_object != NoObject)
300 return;
301 QDateTime dt = QDateTime::currentDateTime();
302 qreal t = dt.toMSecsSinceEpoch() / 1000.0;
303
304 qreal x = width() / qreal(2) + (qCos(v: t*8/11) + qSin(v: -t)) * width() / qreal(4);
305 qreal y = height() / qreal(2) + (qSin(v: t*6/7) + qCos(v: t * qreal(1.5))) * height() / qreal(4);
306
307 setCirclePos(QLineF(m_circle_pos, QPointF(x, y)).pointAt(t: 0.02));
308}
309
310void CompositionRenderer::drawBase(QPainter &p)
311{
312 p.setPen(Qt::NoPen);
313
314 QLinearGradient rect_gradient(0, 0, 0, height());
315 rect_gradient.setColorAt(pos: 0, color: Qt::red);
316 rect_gradient.setColorAt(pos: .17, color: Qt::yellow);
317 rect_gradient.setColorAt(pos: .33, color: Qt::green);
318 rect_gradient.setColorAt(pos: .50, color: Qt::cyan);
319 rect_gradient.setColorAt(pos: .66, color: Qt::blue);
320 rect_gradient.setColorAt(pos: .81, color: Qt::magenta);
321 rect_gradient.setColorAt(pos: 1, color: Qt::red);
322 p.setBrush(rect_gradient);
323 p.drawRect(x: width() / 2, y: 0, w: width() / 2, h: height());
324
325 QLinearGradient alpha_gradient(0, 0, width(), 0);
326 alpha_gradient.setColorAt(pos: 0, color: Qt::white);
327 alpha_gradient.setColorAt(pos: 0.2, color: Qt::white);
328 alpha_gradient.setColorAt(pos: 0.5, color: Qt::transparent);
329 alpha_gradient.setColorAt(pos: 0.8, color: Qt::white);
330 alpha_gradient.setColorAt(pos: 1, color: Qt::white);
331
332 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
333 p.setBrush(alpha_gradient);
334 p.drawRect(x: 0, y: 0, w: width(), h: height());
335
336 p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
337
338 p.setPen(Qt::NoPen);
339 p.setRenderHint(hint: QPainter::SmoothPixmapTransform);
340 p.drawImage(r: rect(), image: m_image);
341}
342
343void CompositionRenderer::drawSource(QPainter &p)
344{
345 p.setPen(Qt::NoPen);
346 p.setRenderHint(hint: QPainter::Antialiasing);
347 p.setCompositionMode(m_composition_mode);
348
349 QRectF circle_rect = rectangle_around(p: m_circle_pos);
350 QColor color = QColor::fromHsvF(h: m_circle_hue / 360.0, s: 1, v: 1, a: m_circle_alpha / 255.0);
351 QLinearGradient circle_gradient(circle_rect.topLeft(), circle_rect.bottomRight());
352 circle_gradient.setColorAt(pos: 0, color: color.lighter());
353 circle_gradient.setColorAt(pos: 0.5, color);
354 circle_gradient.setColorAt(pos: 1, color: color.darker());
355 p.setBrush(circle_gradient);
356
357 p.drawEllipse(r: circle_rect);
358}
359
360void CompositionRenderer::paint(QPainter *painter)
361{
362#if QT_CONFIG(opengl)
363 if (usesOpenGL() && glWindow()->isValid()) {
364
365 if (!m_blitter.isCreated())
366 m_blitter.create();
367
368 int new_pbuf_size = m_pbuffer_size;
369 while (size().width() > new_pbuf_size || size().height() > new_pbuf_size)
370 new_pbuf_size *= 2;
371
372 while (size().width() < new_pbuf_size/2 && size().height() < new_pbuf_size/2)
373 new_pbuf_size /= 2;
374
375 if (!m_fbo || new_pbuf_size != m_pbuffer_size) {
376 m_fbo.reset(p: new QFboPaintDevice(QSize(new_pbuf_size, new_pbuf_size), false, false));
377 m_pbuffer_size = new_pbuf_size;
378 }
379
380 if (size() != m_previous_size) {
381 m_previous_size = size();
382 QPainter p(m_fbo.get());
383 p.setCompositionMode(QPainter::CompositionMode_Source);
384 p.fillRect(r: QRect(QPoint(0, 0), size()), c: Qt::transparent);
385 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
386 drawBase(p);
387 p.end();
388 m_base_tex = m_fbo->takeTexture();
389 }
390
391 painter->beginNativePainting();
392 {
393 QPainter p(m_fbo.get());
394 p.beginNativePainting();
395 m_blitter.bind();
396 const QRect targetRect(QPoint(0, 0), m_fbo->size());
397 const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(target: targetRect, viewport: QRect(QPoint(0, 0), m_fbo->size()));
398 m_blitter.blit(texture: m_base_tex, targetTransform: target, sourceOrigin: QOpenGLTextureBlitter::OriginBottomLeft);
399 m_blitter.release();
400 p.endNativePainting();
401 drawSource(p);
402 p.end();
403 m_compositing_tex = m_fbo->takeTexture();
404 }
405 painter->endNativePainting();
406
407 painter->beginNativePainting();
408 auto *funcs = QOpenGLContext::currentContext()->functions();
409 funcs->glEnable(GL_BLEND);
410 funcs->glBlendEquation(GL_FUNC_ADD);
411 funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
412 m_blitter.bind();
413 const QRect targetRect(QPoint(0, 0), m_fbo->size());
414 const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(target: targetRect, viewport: QRect(QPoint(0, 0), size()));
415 m_blitter.blit(texture: m_compositing_tex, targetTransform: target, sourceOrigin: QOpenGLTextureBlitter::OriginBottomLeft);
416 m_blitter.release();
417 painter->endNativePainting();
418 } else
419#endif
420 {
421 // using a QImage
422 if (m_buffer.size() != size()) {
423 m_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
424 m_base_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
425
426 m_base_buffer.fill(pixel: 0);
427
428 QPainter p(&m_base_buffer);
429
430 drawBase(p);
431 }
432
433 memcpy(dest: m_buffer.bits(), src: m_base_buffer.bits(), n: m_buffer.sizeInBytes());
434
435 {
436 QPainter p(&m_buffer);
437 drawSource(p);
438 }
439
440 painter->drawImage(x: 0, y: 0, image: m_buffer);
441 }
442}
443
444void CompositionRenderer::mousePressEvent(QMouseEvent *e)
445{
446 setDescriptionEnabled(false);
447
448 QRectF circle = rectangle_around(p: m_circle_pos);
449
450 if (circle.contains(p: e->pos())) {
451 m_current_object = Circle;
452 m_offset = circle.center() - e->pos();
453 } else {
454 m_current_object = NoObject;
455 }
456 if (m_animation_enabled) {
457 killTimer(id: m_animationTimer);
458 m_animationTimer = 0;
459 }
460}
461
462void CompositionRenderer::mouseMoveEvent(QMouseEvent *e)
463{
464 if (m_current_object == Circle)
465 setCirclePos(e->pos() + m_offset);
466}
467
468void CompositionRenderer::mouseReleaseEvent(QMouseEvent *)
469{
470 m_current_object = NoObject;
471
472 if (m_animation_enabled) {
473 Q_ASSERT(!m_animationTimer);
474 m_animationTimer = startTimer(interval: animationInterval);
475 }
476}
477
478void CompositionRenderer::timerEvent(QTimerEvent *event)
479{
480 if (event->timerId() == m_animationTimer)
481 updateCirclePos();
482}
483
484void CompositionRenderer::setCirclePos(const QPointF &pos)
485{
486 const QRect oldRect = rectangle_around(p: m_circle_pos).toAlignedRect();
487 m_circle_pos = pos;
488 const QRect newRect = rectangle_around(p: m_circle_pos).toAlignedRect();
489#if QT_CONFIG(opengl)
490 if (usesOpenGL()) {
491 update();
492 return;
493 }
494#endif
495 update(oldRect | newRect);
496}
497
498

source code of qtbase/examples/widgets/painting/composition/composition.cpp