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 | |
65 | const int animationInterval = 15; // update every 16 ms = ~60FPS |
66 | |
67 | CompositionWidget::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 | |
227 | CompositionWidget::~CompositionWidget() |
228 | { |
229 | } |
230 | |
231 | |
232 | void 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 | |
252 | CompositionRenderer::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 | |
272 | CompositionRenderer::~CompositionRenderer() |
273 | { |
274 | } |
275 | |
276 | QRectF 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 | |
283 | void 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 | |
297 | void 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 | |
310 | void 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 | |
343 | void 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 | |
360 | void 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 | |
444 | void 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 | |
462 | void CompositionRenderer::mouseMoveEvent(QMouseEvent *e) |
463 | { |
464 | if (m_current_object == Circle) |
465 | setCirclePos(e->pos() + m_offset); |
466 | } |
467 | |
468 | void 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 | |
478 | void CompositionRenderer::timerEvent(QTimerEvent *event) |
479 | { |
480 | if (event->timerId() == m_animationTimer) |
481 | updateCirclePos(); |
482 | } |
483 | |
484 | void 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 | |