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 Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qpaintervideosurface_p.h"
41
42#include <qmath.h>
43
44#include <qpainter.h>
45#include <qvariant.h>
46#include <qvideosurfaceformat.h>
47#include <private/qmediaopenglhelper_p.h>
48
49#if QT_CONFIG(opengl)
50#include <QOpenGLContext>
51#include <QOpenGLFunctions>
52#include <QOpenGLShaderProgram>
53#include <QtGui/QWindow>
54#ifndef GL_CLAMP_TO_EDGE
55#define GL_CLAMP_TO_EDGE 0x812F
56#endif
57#ifndef GL_RGB8
58#define GL_RGB8 0x8051
59#endif
60#endif
61
62#include <QtDebug>
63QT_BEGIN_NAMESPACE
64
65QVideoSurfacePainter::~QVideoSurfacePainter()
66{
67}
68
69class QVideoSurfaceGenericPainter : public QVideoSurfacePainter
70{
71public:
72 QVideoSurfaceGenericPainter();
73
74 QList<QVideoFrame::PixelFormat> supportedPixelFormats(
75 QAbstractVideoBuffer::HandleType handleType) const override;
76
77 bool isFormatSupported(const QVideoSurfaceFormat &format) const override;
78
79 QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format) override;
80 void stop() override;
81
82 QAbstractVideoSurface::Error setCurrentFrame(const QVideoFrame &frame) override;
83
84 QAbstractVideoSurface::Error paint(
85 const QRectF &target, QPainter *painter, const QRectF &source) override;
86
87 void updateColors(int brightness, int contrast, int hue, int saturation) override;
88
89private:
90 QList<QVideoFrame::PixelFormat> m_imagePixelFormats;
91 QVideoFrame m_frame;
92 QSize m_imageSize;
93 QImage::Format m_imageFormat;
94 QVideoSurfaceFormat::Direction m_scanLineDirection;
95 bool m_mirrored;
96};
97
98QVideoSurfaceGenericPainter::QVideoSurfaceGenericPainter()
99 : m_imageFormat(QImage::Format_Invalid)
100 , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
101 , m_mirrored(false)
102{
103 m_imagePixelFormats << QVideoFrame::Format_RGB32;
104
105 // The raster formats should be a subset of the GL formats.
106#ifndef QT_NO_OPENGL
107 if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES)
108#endif
109 m_imagePixelFormats << QVideoFrame::Format_RGB24;
110
111 m_imagePixelFormats << QVideoFrame::Format_ARGB32
112 << QVideoFrame::Format_RGB565;
113}
114
115QList<QVideoFrame::PixelFormat> QVideoSurfaceGenericPainter::supportedPixelFormats(
116 QAbstractVideoBuffer::HandleType handleType) const
117{
118 switch (handleType) {
119 case QAbstractVideoBuffer::QPixmapHandle:
120 case QAbstractVideoBuffer::NoHandle:
121 return m_imagePixelFormats;
122 default:
123 ;
124 }
125 return QList<QVideoFrame::PixelFormat>();
126}
127
128bool QVideoSurfaceGenericPainter::isFormatSupported(const QVideoSurfaceFormat &format) const
129{
130 switch (format.handleType()) {
131 case QAbstractVideoBuffer::QPixmapHandle:
132 return true;
133 case QAbstractVideoBuffer::NoHandle:
134 return m_imagePixelFormats.contains(t: format.pixelFormat())
135 && !format.frameSize().isEmpty();
136 default:
137 ;
138 }
139 return false;
140}
141
142QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::start(const QVideoSurfaceFormat &format)
143{
144 m_frame = QVideoFrame();
145 m_imageFormat = QVideoFrame::imageFormatFromPixelFormat(format: format.pixelFormat());
146 // Do not render into ARGB32 images using QPainter.
147 // Using QImage::Format_ARGB32_Premultiplied is significantly faster.
148 if (m_imageFormat == QImage::Format_ARGB32)
149 m_imageFormat = QImage::Format_ARGB32_Premultiplied;
150
151 m_imageSize = format.frameSize();
152 m_scanLineDirection = format.scanLineDirection();
153 m_mirrored = format.property(name: "mirrored").toBool();
154
155 const QAbstractVideoBuffer::HandleType t = format.handleType();
156 if (t == QAbstractVideoBuffer::NoHandle) {
157 bool ok = m_imageFormat != QImage::Format_Invalid && !m_imageSize.isEmpty();
158#ifndef QT_NO_OPENGL
159 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
160 ok &= format.pixelFormat() != QVideoFrame::Format_RGB24;
161#endif
162 if (ok)
163 return QAbstractVideoSurface::NoError;
164 } else if (t == QAbstractVideoBuffer::QPixmapHandle) {
165 return QAbstractVideoSurface::NoError;
166 }
167 return QAbstractVideoSurface::UnsupportedFormatError;
168}
169
170void QVideoSurfaceGenericPainter::stop()
171{
172 m_frame = QVideoFrame();
173}
174
175QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::setCurrentFrame(const QVideoFrame &frame)
176{
177 m_frame = frame;
178
179 return QAbstractVideoSurface::NoError;
180}
181
182QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::paint(
183 const QRectF &target, QPainter *painter, const QRectF &source)
184{
185 if (!m_frame.isValid()) {
186 painter->fillRect(r: target, c: Qt::black);
187 return QAbstractVideoSurface::NoError;
188 }
189
190 if (m_frame.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
191 painter->drawPixmap(targetRect: target, pixmap: m_frame.handle().value<QPixmap>(), sourceRect: source);
192 } else if (m_frame.map(mode: QAbstractVideoBuffer::ReadOnly)) {
193 QImage image(
194 m_frame.bits(),
195 m_imageSize.width(),
196 m_imageSize.height(),
197 m_frame.bytesPerLine(),
198 m_imageFormat);
199
200 const QTransform oldTransform = painter->transform();
201 QTransform transform = oldTransform;
202 QRectF targetRect = target;
203 if (m_scanLineDirection == QVideoSurfaceFormat::BottomToTop) {
204 transform.scale(sx: 1, sy: -1);
205 transform.translate(dx: 0, dy: -target.bottom());
206 targetRect = QRectF(target.x(), 0, target.width(), target.height());
207 }
208
209 if (m_mirrored) {
210 transform.scale(sx: -1, sy: 1);
211 transform.translate(dx: -target.right(), dy: 0);
212 targetRect = QRectF(0, targetRect.y(), target.width(), target.height());
213 }
214 painter->setTransform(transform);
215 painter->drawImage(targetRect, image, sourceRect: source);
216 painter->setTransform(transform: oldTransform);
217
218 m_frame.unmap();
219 } else if (m_frame.isValid()) {
220 return QAbstractVideoSurface::IncorrectFormatError;
221 } else {
222 painter->fillRect(r: target, c: Qt::black);
223 }
224 return QAbstractVideoSurface::NoError;
225}
226
227void QVideoSurfaceGenericPainter::updateColors(int, int, int, int)
228{
229}
230
231#if QT_CONFIG(opengl)
232
233#ifndef APIENTRYP
234# ifdef APIENTRY
235# define APIENTRYP APIENTRY *
236# else
237# define APIENTRY
238# define APIENTRYP *
239# endif
240#endif
241
242#ifndef GL_TEXTURE0
243# define GL_TEXTURE0 0x84C0
244# define GL_TEXTURE1 0x84C1
245# define GL_TEXTURE2 0x84C2
246#endif
247#ifndef GL_PROGRAM_ERROR_STRING_ARB
248# define GL_PROGRAM_ERROR_STRING_ARB 0x8874
249#endif
250
251#ifndef GL_UNSIGNED_SHORT_5_6_5
252# define GL_UNSIGNED_SHORT_5_6_5 33635
253#endif
254
255class QVideoSurfaceGLPainter : public QVideoSurfacePainter, protected QOpenGLFunctions
256{
257public:
258 QVideoSurfaceGLPainter(QOpenGLContext *context);
259 ~QVideoSurfaceGLPainter();
260 QList<QVideoFrame::PixelFormat> supportedPixelFormats(
261 QAbstractVideoBuffer::HandleType handleType) const override;
262
263 bool isFormatSupported(const QVideoSurfaceFormat &format) const override;
264
265 void stop() override;
266
267 QAbstractVideoSurface::Error setCurrentFrame(const QVideoFrame &frame) override;
268
269 QAbstractVideoSurface::Error paint(
270 const QRectF &target, QPainter *painter, const QRectF &source) override;
271
272 void updateColors(int brightness, int contrast, int hue, int saturation) override;
273 void viewportDestroyed() override;
274
275protected:
276 void initRgbTextureInfo(GLenum internalFormat, GLuint format, GLenum type, const QSize &size);
277 void initYuv420PTextureInfo(const QSize &size);
278 void initYv12TextureInfo(const QSize &size);
279
280 bool needsSwizzling(const QVideoSurfaceFormat &format) const {
281 return !QMediaOpenGLHelper::isANGLE()
282 && (format.pixelFormat() == QVideoFrame::Format_RGB32
283 || format.pixelFormat() == QVideoFrame::Format_ARGB32);
284 }
285
286 QList<QVideoFrame::PixelFormat> m_imagePixelFormats;
287 QList<QVideoFrame::PixelFormat> m_glPixelFormats;
288 QMatrix4x4 m_colorMatrix;
289 QVideoFrame m_frame;
290
291 QOpenGLContext *m_context;
292 QAbstractVideoBuffer::HandleType m_handleType;
293 QVideoSurfaceFormat::Direction m_scanLineDirection;
294 bool m_mirrored;
295 QVideoSurfaceFormat::YCbCrColorSpace m_colorSpace;
296 GLenum m_textureFormat;
297 GLuint m_textureInternalFormat;
298 GLenum m_textureType;
299 int m_textureCount;
300
301 static const uint Max_Textures = 3;
302 GLuint m_textureIds[Max_Textures];
303 int m_textureWidths[Max_Textures];
304 int m_textureHeights[Max_Textures];
305 int m_textureOffsets[Max_Textures];
306 bool m_yuv;
307};
308
309QVideoSurfaceGLPainter::QVideoSurfaceGLPainter(QOpenGLContext *context)
310 : m_context(context)
311 , m_handleType(QAbstractVideoBuffer::NoHandle)
312 , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
313 , m_mirrored(false)
314 , m_colorSpace(QVideoSurfaceFormat::YCbCr_BT601)
315 , m_textureFormat(0)
316 , m_textureInternalFormat(0)
317 , m_textureType(0)
318 , m_textureCount(0)
319 , m_yuv(false)
320{
321 memset(s: m_textureIds, c: 0, n: sizeof(m_textureIds));
322 memset(s: m_textureWidths, c: 0, n: sizeof(m_textureWidths));
323 memset(s: m_textureHeights, c: 0, n: sizeof(m_textureHeights));
324 memset(s: m_textureOffsets, c: 0, n: sizeof(m_textureOffsets));
325
326 initializeOpenGLFunctions();
327}
328
329QVideoSurfaceGLPainter::~QVideoSurfaceGLPainter()
330{
331}
332
333void QVideoSurfaceGLPainter::viewportDestroyed()
334{
335 m_context = 0;
336}
337
338QList<QVideoFrame::PixelFormat> QVideoSurfaceGLPainter::supportedPixelFormats(
339 QAbstractVideoBuffer::HandleType handleType) const
340{
341 switch (handleType) {
342 case QAbstractVideoBuffer::NoHandle:
343 return m_imagePixelFormats;
344 case QAbstractVideoBuffer::QPixmapHandle:
345 case QAbstractVideoBuffer::GLTextureHandle:
346 return m_glPixelFormats;
347 default:
348 ;
349 }
350 return QList<QVideoFrame::PixelFormat>();
351}
352
353bool QVideoSurfaceGLPainter::isFormatSupported(const QVideoSurfaceFormat &format) const
354{
355 if (format.frameSize().isEmpty()) {
356 return false;
357 } else {
358 switch (format.handleType()) {
359 case QAbstractVideoBuffer::NoHandle:
360 return m_imagePixelFormats.contains(t: format.pixelFormat());
361 case QAbstractVideoBuffer::QPixmapHandle:
362 case QAbstractVideoBuffer::GLTextureHandle:
363 return m_glPixelFormats.contains(t: format.pixelFormat());
364 default:
365 ;
366 }
367 }
368 return false;
369}
370
371
372void QVideoSurfaceGLPainter::stop()
373{
374 m_frame = QVideoFrame();
375}
376
377QAbstractVideoSurface::Error QVideoSurfaceGLPainter::setCurrentFrame(const QVideoFrame &frame)
378{
379 m_frame = frame;
380
381 if (m_handleType == QAbstractVideoBuffer::GLTextureHandle) {
382 m_textureIds[0] = frame.handle().toInt();
383 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[0]);
384 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
385 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
386 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
387 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
388 } else if (m_frame.map(mode: QAbstractVideoBuffer::ReadOnly)) {
389 for (int i = 0; i < m_textureCount; ++i) {
390 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[i]);
391 glTexImage2D(
392 GL_TEXTURE_2D,
393 level: 0,
394 internalformat: m_textureInternalFormat,
395 width: m_textureWidths[i],
396 height: m_textureHeights[i],
397 border: 0,
398 format: m_textureFormat,
399 type: m_textureType,
400 pixels: m_frame.bits() + m_textureOffsets[i]);
401 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
402 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
403 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
404 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
405 }
406 m_frame.unmap();
407 } else if (m_handleType != QAbstractVideoBuffer::QPixmapHandle && m_frame.isValid()) {
408 return QAbstractVideoSurface::IncorrectFormatError;
409 }
410
411 return QAbstractVideoSurface::NoError;
412}
413
414QAbstractVideoSurface::Error QVideoSurfaceGLPainter::paint(
415 const QRectF &target, QPainter *painter, const QRectF &source)
416{
417 if (!m_frame.isValid()) {
418 painter->fillRect(r: target, c: Qt::black);
419 return QAbstractVideoSurface::NoError;
420 }
421
422 if (m_frame.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
423 painter->drawPixmap(targetRect: target, pixmap: m_frame.handle().value<QPixmap>(), sourceRect: source);
424 } else if (m_frame.isValid()) {
425 return QAbstractVideoSurface::IncorrectFormatError;
426 } else {
427 painter->fillRect(r: target, c: Qt::black);
428 }
429 return QAbstractVideoSurface::NoError;
430}
431
432void QVideoSurfaceGLPainter::updateColors(int brightness, int contrast, int hue, int saturation)
433{
434 const qreal b = brightness / 200.0;
435 const qreal c = contrast / 100.0 + 1.0;
436 const qreal h = hue / 100.0;
437 const qreal s = saturation / 100.0 + 1.0;
438
439 const qreal cosH = qCos(M_PI * h);
440 const qreal sinH = qSin(M_PI * h);
441
442 const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213;
443 const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213;
444 const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213;
445
446 const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715;
447 const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715;
448 const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715;
449
450 const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072;
451 const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072;
452 const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072;
453
454 const qreal sr = (1.0 - s) * 0.3086;
455 const qreal sg = (1.0 - s) * 0.6094;
456 const qreal sb = (1.0 - s) * 0.0820;
457
458 const qreal sr_s = sr + s;
459 const qreal sg_s = sg + s;
460 const qreal sb_s = sr + s;
461
462 const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b);
463
464 m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31);
465 m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32);
466 m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33);
467 m_colorMatrix(0, 3) = m4;
468
469 m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31);
470 m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32);
471 m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33);
472 m_colorMatrix(1, 3) = m4;
473
474 m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31);
475 m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32);
476 m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33);
477 m_colorMatrix(2, 3) = m4;
478
479 m_colorMatrix(3, 0) = 0.0;
480 m_colorMatrix(3, 1) = 0.0;
481 m_colorMatrix(3, 2) = 0.0;
482 m_colorMatrix(3, 3) = 1.0;
483
484 if (m_yuv) {
485 QMatrix4x4 colorSpaceMatrix;
486
487 switch (m_colorSpace) {
488 case QVideoSurfaceFormat::YCbCr_JPEG:
489 colorSpaceMatrix = QMatrix4x4(
490 1.0f, 0.000f, 1.402f, -0.701f,
491 1.0f, -0.344f, -0.714f, 0.529f,
492 1.0f, 1.772f, 0.000f, -0.886f,
493 0.0f, 0.000f, 0.000f, 1.0000f);
494 break;
495 case QVideoSurfaceFormat::YCbCr_BT709:
496 case QVideoSurfaceFormat::YCbCr_xvYCC709:
497 colorSpaceMatrix = QMatrix4x4(
498 1.164f, 0.000f, 1.793f, -0.5727f,
499 1.164f, -0.534f, -0.213f, 0.3007f,
500 1.164f, 2.115f, 0.000f, -1.1302f,
501 0.0f, 0.000f, 0.000f, 1.0000f);
502 break;
503 default: //BT 601:
504 colorSpaceMatrix = QMatrix4x4(
505 1.164f, 0.000f, 1.596f, -0.8708f,
506 1.164f, -0.392f, -0.813f, 0.5296f,
507 1.164f, 2.017f, 0.000f, -1.081f,
508 0.0f, 0.000f, 0.000f, 1.0000f);
509 }
510
511 m_colorMatrix = m_colorMatrix * colorSpaceMatrix;
512 }
513}
514
515void QVideoSurfaceGLPainter::initRgbTextureInfo(
516 GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
517{
518 m_yuv = false;
519 m_textureInternalFormat = internalFormat;
520 m_textureFormat = format;
521 m_textureType = type;
522 m_textureCount = 1; // Note: ensure this is always <= Max_Textures
523 m_textureWidths[0] = size.width();
524 m_textureHeights[0] = size.height();
525 m_textureOffsets[0] = 0;
526}
527
528void QVideoSurfaceGLPainter::initYuv420PTextureInfo(const QSize &size)
529{
530 int bytesPerLine = (size.width() + 3) & ~3;
531 int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
532
533 m_yuv = true;
534 m_textureInternalFormat = GL_LUMINANCE;
535 m_textureFormat = GL_LUMINANCE;
536 m_textureType = GL_UNSIGNED_BYTE;
537 m_textureCount = 3; // Note: ensure this is always <= Max_Textures
538 m_textureWidths[0] = bytesPerLine;
539 m_textureHeights[0] = size.height();
540 m_textureOffsets[0] = 0;
541 m_textureWidths[1] = bytesPerLine2;
542 m_textureHeights[1] = size.height() / 2;
543 m_textureOffsets[1] = bytesPerLine * size.height();
544 m_textureWidths[2] = bytesPerLine2;
545 m_textureHeights[2] = size.height() / 2;
546 m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
547}
548
549void QVideoSurfaceGLPainter::initYv12TextureInfo(const QSize &size)
550{
551 int bytesPerLine = (size.width() + 3) & ~3;
552 int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
553
554 m_yuv = true;
555 m_textureInternalFormat = GL_LUMINANCE;
556 m_textureFormat = GL_LUMINANCE;
557 m_textureType = GL_UNSIGNED_BYTE;
558 m_textureCount = 3; // Note: ensure this is always <= Max_Textures
559 m_textureWidths[0] = bytesPerLine;
560 m_textureHeights[0] = size.height();
561 m_textureOffsets[0] = 0;
562 m_textureWidths[1] = bytesPerLine2;
563 m_textureHeights[1] = size.height() / 2;
564 m_textureOffsets[1] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
565 m_textureWidths[2] = bytesPerLine2;
566 m_textureHeights[2] = size.height() / 2;
567 m_textureOffsets[2] = bytesPerLine * size.height();
568}
569
570#if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_DYNAMIC)
571
572# ifndef GL_FRAGMENT_PROGRAM_ARB
573# define GL_FRAGMENT_PROGRAM_ARB 0x8804
574# define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
575# endif
576
577// Paints an RGB32 frame
578static const char *qt_arbfp_xrgbShaderProgram =
579 "!!ARBfp1.0\n"
580 "PARAM matrix[4] = { program.local[0..2],"
581 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
582 "TEMP xrgb;\n"
583 "TEX xrgb.xyz, fragment.texcoord[0], texture[0], 2D;\n"
584 "MOV xrgb.w, matrix[3].w;\n"
585 "DP4 result.color.x, xrgb.zyxw, matrix[0];\n"
586 "DP4 result.color.y, xrgb.zyxw, matrix[1];\n"
587 "DP4 result.color.z, xrgb.zyxw, matrix[2];\n"
588 "END";
589
590// Paints an ARGB frame.
591static const char *qt_arbfp_argbShaderProgram =
592 "!!ARBfp1.0\n"
593 "PARAM matrix[4] = { program.local[0..2],"
594 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
595 "TEMP argb;\n"
596 "TEX argb, fragment.texcoord[0], texture[0], 2D;\n"
597 "MOV argb.w, matrix[3].w;\n"
598 "DP4 result.color.x, argb.zyxw, matrix[0];\n"
599 "DP4 result.color.y, argb.zyxw, matrix[1];\n"
600 "DP4 result.color.z, argb.zyxw, matrix[2];\n"
601 "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
602 "END";
603
604// Paints an RGB(A) frame.
605static const char *qt_arbfp_rgbShaderProgram =
606 "!!ARBfp1.0\n"
607 "PARAM matrix[4] = { program.local[0..2],"
608 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
609 "TEMP rgb;\n"
610 "TEX rgb, fragment.texcoord[0], texture[0], 2D;\n"
611 "MOV rgb.w, matrix[3].w;\n"
612 "DP4 result.color.x, rgb, matrix[0];\n"
613 "DP4 result.color.y, rgb, matrix[1];\n"
614 "DP4 result.color.z, rgb, matrix[2];\n"
615 "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
616 "END";
617
618// Paints a YUV420P or YV12 frame.
619static const char *qt_arbfp_yuvPlanarShaderProgram =
620 "!!ARBfp1.0\n"
621 "PARAM matrix[4] = { program.local[0..2],"
622 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
623 "TEMP yuv;\n"
624 "TEX yuv.x, fragment.texcoord[0], texture[0], 2D;\n"
625 "TEX yuv.y, fragment.texcoord[0], texture[1], 2D;\n"
626 "TEX yuv.z, fragment.texcoord[0], texture[2], 2D;\n"
627 "MOV yuv.w, matrix[3].w;\n"
628 "DP4 result.color.x, yuv, matrix[0];\n"
629 "DP4 result.color.y, yuv, matrix[1];\n"
630 "DP4 result.color.z, yuv, matrix[2];\n"
631 "END";
632
633// Paints a YUV444 frame.
634static const char *qt_arbfp_xyuvShaderProgram =
635 "!!ARBfp1.0\n"
636 "PARAM matrix[4] = { program.local[0..2],"
637 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
638 "TEMP ayuv;\n"
639 "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
640 "MOV ayuv.x, matrix[3].w;\n"
641 "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
642 "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
643 "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
644 "END";
645
646// Paints a AYUV444 frame.
647static const char *qt_arbfp_ayuvShaderProgram =
648 "!!ARBfp1.0\n"
649 "PARAM matrix[4] = { program.local[0..2],"
650 "{ 0.0, 0.0, 0.0, 1.0 } };\n"
651 "TEMP ayuv;\n"
652 "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
653 "MOV ayuv.x, matrix[3].w;\n"
654 "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
655 "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
656 "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
657 "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
658 "END";
659
660class QVideoSurfaceArbFpPainter : public QVideoSurfaceGLPainter
661{
662public:
663 QVideoSurfaceArbFpPainter(QOpenGLContext *context);
664
665 QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format) override;
666 void stop() override;
667
668 QAbstractVideoSurface::Error paint(
669 const QRectF &target, QPainter *painter, const QRectF &source) override;
670
671private:
672 typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
673 typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
674 typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
675 typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
676 typedef void (APIENTRY *_glProgramLocalParameter4fARB) (
677 GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
678 typedef void (APIENTRY *_glActiveTexture) (GLenum);
679
680 _glProgramStringARB glProgramStringARB;
681 _glBindProgramARB glBindProgramARB;
682 _glDeleteProgramsARB glDeleteProgramsARB;
683 _glGenProgramsARB glGenProgramsARB;
684 _glProgramLocalParameter4fARB glProgramLocalParameter4fARB;
685
686 GLuint m_programId;
687 QSize m_frameSize;
688};
689
690QVideoSurfaceArbFpPainter::QVideoSurfaceArbFpPainter(QOpenGLContext *context)
691 : QVideoSurfaceGLPainter(context)
692 , m_programId(0)
693{
694 glProgramStringARB = (_glProgramStringARB) m_context->getProcAddress(
695 procName: QByteArray("glProgramStringARB"));
696 glBindProgramARB = (_glBindProgramARB) m_context->getProcAddress(
697 procName: QByteArray("glBindProgramARB"));
698 glDeleteProgramsARB = (_glDeleteProgramsARB) m_context->getProcAddress(
699 procName: QByteArray("glDeleteProgramsARB"));
700 glGenProgramsARB = (_glGenProgramsARB) m_context->getProcAddress(
701 procName: QByteArray("glGenProgramsARB"));
702 glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) m_context->getProcAddress(
703 procName: QByteArray("glProgramLocalParameter4fARB"));
704
705 m_imagePixelFormats
706 << QVideoFrame::Format_RGB32
707 << QVideoFrame::Format_BGR32
708 << QVideoFrame::Format_ARGB32
709 << QVideoFrame::Format_RGB24
710 << QVideoFrame::Format_BGR24
711 << QVideoFrame::Format_RGB565
712 << QVideoFrame::Format_AYUV444
713 << QVideoFrame::Format_YUV444
714 << QVideoFrame::Format_YV12
715 << QVideoFrame::Format_YUV420P;
716 m_glPixelFormats
717 << QVideoFrame::Format_RGB32
718 << QVideoFrame::Format_ARGB32
719 << QVideoFrame::Format_BGR32
720 << QVideoFrame::Format_BGRA32;
721}
722
723QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::start(const QVideoSurfaceFormat &format)
724{
725 Q_ASSERT(m_textureCount == 0);
726
727 QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError;
728
729 const char *program = 0;
730
731 if (format.handleType() == QAbstractVideoBuffer::NoHandle) {
732 switch (format.pixelFormat()) {
733 case QVideoFrame::Format_RGB32:
734 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
735 program = qt_arbfp_xrgbShaderProgram;
736 break;
737 case QVideoFrame::Format_BGR32:
738 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
739 program = qt_arbfp_rgbShaderProgram;
740 break;
741 case QVideoFrame::Format_ARGB32:
742 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
743 program = qt_arbfp_argbShaderProgram;
744 break;
745 case QVideoFrame::Format_RGB24:
746 initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
747 program = qt_arbfp_rgbShaderProgram;
748 break;
749 case QVideoFrame::Format_BGR24:
750 initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
751 program = qt_arbfp_xrgbShaderProgram;
752 break;
753 case QVideoFrame::Format_RGB565:
754 initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, size: format.frameSize());
755 program = qt_arbfp_rgbShaderProgram;
756 break;
757 case QVideoFrame::Format_YUV444:
758 initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, size: format.frameSize());
759 program = qt_arbfp_xyuvShaderProgram;
760 m_yuv = true;
761 break;
762 case QVideoFrame::Format_AYUV444:
763 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
764 program = qt_arbfp_ayuvShaderProgram;
765 m_yuv = true;
766 break;
767 case QVideoFrame::Format_YV12:
768 initYv12TextureInfo(size: format.frameSize());
769 program = qt_arbfp_yuvPlanarShaderProgram;
770 break;
771 case QVideoFrame::Format_YUV420P:
772 initYuv420PTextureInfo(size: format.frameSize());
773 program = qt_arbfp_yuvPlanarShaderProgram;
774 break;
775 default:
776 break;
777 }
778 } else if (format.handleType() == QAbstractVideoBuffer::GLTextureHandle) {
779 switch (format.pixelFormat()) {
780 case QVideoFrame::Format_RGB32:
781 case QVideoFrame::Format_ARGB32:
782 case QVideoFrame::Format_BGR32:
783 case QVideoFrame::Format_BGRA32:
784 m_yuv = false;
785 m_textureCount = 1;
786 if (needsSwizzling(format))
787 program = qt_arbfp_xrgbShaderProgram;
788 else
789 program = qt_arbfp_rgbShaderProgram;
790 break;
791 default:
792 break;
793 }
794 } else if (format.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
795 m_handleType = QAbstractVideoBuffer::QPixmapHandle;
796 return QAbstractVideoSurface::NoError;
797 }
798
799 if (!program) {
800 error = QAbstractVideoSurface::UnsupportedFormatError;
801 } else {
802 while (glGetError() != GL_NO_ERROR) { } // clear previous unrelated errors
803
804 glGenProgramsARB(1, &m_programId);
805
806 GLenum glError = glGetError();
807 if (glError != GL_NO_ERROR) {
808 qWarning(msg: "QPainterVideoSurface: ARBfb Shader allocation error %x", int(glError));
809 m_textureCount = 0;
810 m_programId = 0;
811
812 error = QAbstractVideoSurface::ResourceError;
813 } else {
814 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
815 glProgramStringARB(
816 GL_FRAGMENT_PROGRAM_ARB,
817 GL_PROGRAM_FORMAT_ASCII_ARB,
818 qstrlen(str: program),
819 reinterpret_cast<const GLvoid *>(program));
820
821 if ((glError = glGetError()) != GL_NO_ERROR) {
822 const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
823
824 qWarning(msg: "QPainterVideoSurface: ARBfp Shader compile error %x, %s",
825 int(glError),
826 reinterpret_cast<const char *>(errorString));
827 glDeleteProgramsARB(1, &m_programId);
828
829 m_textureCount = 0;
830 m_programId = 0;
831
832 error = QAbstractVideoSurface::ResourceError;
833 } else {
834 m_handleType = format.handleType();
835 m_scanLineDirection = format.scanLineDirection();
836 m_mirrored = format.property(name: "mirrored").toBool();
837 m_frameSize = format.frameSize();
838 m_colorSpace = format.yCbCrColorSpace();
839
840 if (m_handleType == QAbstractVideoBuffer::NoHandle)
841 glGenTextures(n: m_textureCount, textures: m_textureIds);
842 }
843 }
844 }
845
846 return error;
847}
848
849void QVideoSurfaceArbFpPainter::stop()
850{
851 if (m_context) {
852 if (m_handleType != QAbstractVideoBuffer::GLTextureHandle)
853 glDeleteTextures(n: m_textureCount, textures: m_textureIds);
854 glDeleteProgramsARB(1, &m_programId);
855 }
856
857 m_textureCount = 0;
858 m_programId = 0;
859 m_handleType = QAbstractVideoBuffer::NoHandle;
860
861 QVideoSurfaceGLPainter::stop();
862}
863
864QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::paint(
865 const QRectF &target, QPainter *painter, const QRectF &source)
866{
867 if (!m_frame.isValid()) {
868 painter->fillRect(r: target, c: Qt::black);
869 return QAbstractVideoSurface::NoError;
870 }
871
872 const QAbstractVideoBuffer::HandleType h = m_frame.handleType();
873 if (h == QAbstractVideoBuffer::NoHandle || h == QAbstractVideoBuffer::GLTextureHandle) {
874 bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
875 bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
876
877 painter->beginNativePainting();
878
879 if (stencilTestEnabled)
880 glEnable(GL_STENCIL_TEST);
881 if (scissorTestEnabled)
882 glEnable(GL_SCISSOR_TEST);
883
884 const float txLeft = m_mirrored ? source.right() / m_frameSize.width()
885 : source.left() / m_frameSize.width();
886 const float txRight = m_mirrored ? source.left() / m_frameSize.width()
887 : source.right() / m_frameSize.width();
888 const float txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
889 ? source.top() / m_frameSize.height()
890 : source.bottom() / m_frameSize.height();
891 const float txBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
892 ? source.bottom() / m_frameSize.height()
893 : source.top() / m_frameSize.height();
894
895 const float tx_array[] =
896 {
897 txLeft , txBottom,
898 txRight, txBottom,
899 txLeft , txTop,
900 txRight, txTop
901 };
902
903 const GLfloat v_array[] =
904 {
905 GLfloat(target.left()) , GLfloat(target.bottom() + 1),
906 GLfloat(target.right() + 1), GLfloat(target.bottom() + 1),
907 GLfloat(target.left()) , GLfloat(target.top()),
908 GLfloat(target.right() + 1), GLfloat(target.top())
909 };
910
911 glEnable(GL_FRAGMENT_PROGRAM_ARB);
912 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
913
914 glProgramLocalParameter4fARB(
915 GL_FRAGMENT_PROGRAM_ARB,
916 0,
917 m_colorMatrix(0, 0),
918 m_colorMatrix(0, 1),
919 m_colorMatrix(0, 2),
920 m_colorMatrix(0, 3));
921 glProgramLocalParameter4fARB(
922 GL_FRAGMENT_PROGRAM_ARB,
923 1,
924 m_colorMatrix(1, 0),
925 m_colorMatrix(1, 1),
926 m_colorMatrix(1, 2),
927 m_colorMatrix(1, 3));
928 glProgramLocalParameter4fARB(
929 GL_FRAGMENT_PROGRAM_ARB,
930 2,
931 m_colorMatrix(2, 0),
932 m_colorMatrix(2, 1),
933 m_colorMatrix(2, 2),
934 m_colorMatrix(2, 3));
935
936 glActiveTexture(GL_TEXTURE0);
937 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[0]);
938
939 if (m_textureCount == 3) {
940 glActiveTexture(GL_TEXTURE1);
941 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[1]);
942 glActiveTexture(GL_TEXTURE2);
943 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[2]);
944 glActiveTexture(GL_TEXTURE0);
945 }
946
947 glVertexPointer(size: 2, GL_FLOAT, stride: 0, ptr: v_array);
948 glTexCoordPointer(size: 2, GL_FLOAT, stride: 0, ptr: tx_array);
949
950 glEnableClientState(GL_VERTEX_ARRAY);
951 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
952
953 glDrawArrays(GL_TRIANGLE_STRIP, first: 0, count: 4);
954
955 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
956 glDisableClientState(GL_VERTEX_ARRAY);
957 glDisable(GL_FRAGMENT_PROGRAM_ARB);
958
959 painter->endNativePainting();
960
961 return QAbstractVideoSurface::NoError;
962 }
963
964 return QVideoSurfaceGLPainter::paint(target, painter, source);
965}
966
967#endif // !QT_OPENGL_ES && !QT_OPENGL_DYNAMIC
968
969static const char *qt_glsl_vertexShaderProgram =
970 "attribute highp vec4 vertexCoordArray;\n"
971 "attribute highp vec2 textureCoordArray;\n"
972 "uniform highp mat4 positionMatrix;\n"
973 "varying highp vec2 textureCoord;\n"
974 "void main(void)\n"
975 "{\n"
976 " gl_Position = positionMatrix * vertexCoordArray;\n"
977 " textureCoord = textureCoordArray;\n"
978 "}\n";
979
980// Paints an RGB32 frame
981static const char *qt_glsl_xrgbShaderProgram =
982 "uniform sampler2D texRgb;\n"
983 "uniform mediump mat4 colorMatrix;\n"
984 "varying highp vec2 textureCoord;\n"
985 "void main(void)\n"
986 "{\n"
987 " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
988 " gl_FragColor = colorMatrix * color;\n"
989 "}\n";
990
991// Paints an ARGB frame.
992static const char *qt_glsl_argbShaderProgram =
993 "uniform sampler2D texRgb;\n"
994 "uniform mediump mat4 colorMatrix;\n"
995 "varying highp vec2 textureCoord;\n"
996 "void main(void)\n"
997 "{\n"
998 " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
999 " color = colorMatrix * color;\n"
1000 " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
1001 "}\n";
1002
1003// Paints an RGB(A) frame.
1004static const char *qt_glsl_rgbShaderProgram =
1005 "uniform sampler2D texRgb;\n"
1006 "uniform mediump mat4 colorMatrix;\n"
1007 "varying highp vec2 textureCoord;\n"
1008 "void main(void)\n"
1009 "{\n"
1010 " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).rgb, 1.0);\n"
1011 " color = colorMatrix * color;\n"
1012 " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
1013 "}\n";
1014
1015// Paints a YUV420P or YV12 frame.
1016static const char *qt_glsl_yuvPlanarShaderProgram =
1017 "uniform sampler2D texY;\n"
1018 "uniform sampler2D texU;\n"
1019 "uniform sampler2D texV;\n"
1020 "uniform mediump mat4 colorMatrix;\n"
1021 "varying highp vec2 textureCoord;\n"
1022 "void main(void)\n"
1023 "{\n"
1024 " highp vec4 color = vec4(\n"
1025 " texture2D(texY, textureCoord.st).r,\n"
1026 " texture2D(texU, textureCoord.st).r,\n"
1027 " texture2D(texV, textureCoord.st).r,\n"
1028 " 1.0);\n"
1029 " gl_FragColor = colorMatrix * color;\n"
1030 "}\n";
1031
1032// Paints a YUV444 frame.
1033static const char *qt_glsl_xyuvShaderProgram =
1034 "uniform sampler2D texRgb;\n"
1035 "uniform mediump mat4 colorMatrix;\n"
1036 "varying highp vec2 textureCoord;\n"
1037 "void main(void)\n"
1038 "{\n"
1039 " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
1040 " gl_FragColor = colorMatrix * color;\n"
1041 "}\n";
1042
1043// Paints a AYUV444 frame.
1044static const char *qt_glsl_ayuvShaderProgram =
1045 "uniform sampler2D texRgb;\n"
1046 "uniform mediump mat4 colorMatrix;\n"
1047 "varying highp vec2 textureCoord;\n"
1048 "void main(void)\n"
1049 "{\n"
1050 " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
1051 " color = colorMatrix * color;\n"
1052 " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).r);\n"
1053 "}\n";
1054
1055class QVideoSurfaceGlslPainter : public QVideoSurfaceGLPainter
1056{
1057public:
1058 QVideoSurfaceGlslPainter(QOpenGLContext *context);
1059
1060 QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format) override;
1061 void stop() override;
1062
1063 QAbstractVideoSurface::Error paint(
1064 const QRectF &target, QPainter *painter, const QRectF &source) override;
1065
1066private:
1067 QOpenGLShaderProgram m_program;
1068 QSize m_frameSize;
1069};
1070
1071QVideoSurfaceGlslPainter::QVideoSurfaceGlslPainter(QOpenGLContext *context)
1072 : QVideoSurfaceGLPainter(context)
1073 , m_program(context)
1074{
1075 m_imagePixelFormats
1076 << QVideoFrame::Format_RGB32
1077 << QVideoFrame::Format_BGR32
1078 << QVideoFrame::Format_ARGB32;
1079 if (!context->isOpenGLES()) {
1080 m_imagePixelFormats
1081 << QVideoFrame::Format_RGB24
1082 << QVideoFrame::Format_BGR24;
1083 }
1084 m_imagePixelFormats
1085 << QVideoFrame::Format_RGB565
1086 << QVideoFrame::Format_YUV444
1087 << QVideoFrame::Format_AYUV444
1088 << QVideoFrame::Format_YV12
1089 << QVideoFrame::Format_YUV420P;
1090 m_glPixelFormats
1091 << QVideoFrame::Format_RGB32
1092 << QVideoFrame::Format_ARGB32
1093 << QVideoFrame::Format_BGR32
1094 << QVideoFrame::Format_BGRA32;
1095}
1096
1097QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::start(const QVideoSurfaceFormat &format)
1098{
1099 Q_ASSERT(m_textureCount == 0);
1100
1101 QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError;
1102
1103 const char *fragmentProgram = 0;
1104
1105 if (format.handleType() == QAbstractVideoBuffer::NoHandle) {
1106 switch (format.pixelFormat()) {
1107 case QVideoFrame::Format_RGB32:
1108 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
1109 fragmentProgram = qt_glsl_xrgbShaderProgram;
1110 break;
1111 case QVideoFrame::Format_BGR32:
1112 initRgbTextureInfo(GL_RGB, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
1113 fragmentProgram = qt_glsl_rgbShaderProgram;
1114 break;
1115 case QVideoFrame::Format_ARGB32:
1116 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
1117 fragmentProgram = qt_glsl_argbShaderProgram;
1118 break;
1119 case QVideoFrame::Format_RGB24:
1120 if (!m_context->isOpenGLES()) {
1121 initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, size: format.frameSize());
1122 fragmentProgram = qt_glsl_rgbShaderProgram;
1123 }
1124 break;
1125 case QVideoFrame::Format_BGR24:
1126 if (!m_context->isOpenGLES()) {
1127 initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, size: format.frameSize());
1128 fragmentProgram = qt_glsl_argbShaderProgram;
1129 }
1130 break;
1131 case QVideoFrame::Format_RGB565:
1132 initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, size: format.frameSize());
1133 fragmentProgram = qt_glsl_rgbShaderProgram;
1134 break;
1135 case QVideoFrame::Format_YUV444:
1136 initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, size: format.frameSize());
1137 fragmentProgram = qt_glsl_xyuvShaderProgram;
1138 m_yuv = true;
1139 break;
1140 case QVideoFrame::Format_AYUV444:
1141 initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, size: format.frameSize());
1142 fragmentProgram = qt_glsl_ayuvShaderProgram;
1143 m_yuv = true;
1144 break;
1145 case QVideoFrame::Format_YV12:
1146 initYv12TextureInfo(size: format.frameSize());
1147 fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
1148 break;
1149 case QVideoFrame::Format_YUV420P:
1150 initYuv420PTextureInfo(size: format.frameSize());
1151 fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
1152 break;
1153 default:
1154 break;
1155 }
1156 } else if (format.handleType() == QAbstractVideoBuffer::GLTextureHandle) {
1157 switch (format.pixelFormat()) {
1158 case QVideoFrame::Format_RGB32:
1159 case QVideoFrame::Format_ARGB32:
1160 case QVideoFrame::Format_BGR32:
1161 case QVideoFrame::Format_BGRA32:
1162 m_yuv = false;
1163 m_textureCount = 1;
1164 if (needsSwizzling(format))
1165 fragmentProgram = qt_glsl_xrgbShaderProgram;
1166 else
1167 fragmentProgram = qt_glsl_rgbShaderProgram;
1168 break;
1169 default:
1170 break;
1171 }
1172 } else if (format.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
1173 m_handleType = QAbstractVideoBuffer::QPixmapHandle;
1174 return QAbstractVideoSurface::NoError;
1175 }
1176
1177 if (!fragmentProgram) {
1178 error = QAbstractVideoSurface::UnsupportedFormatError;
1179 } else if (!m_program.addShaderFromSourceCode(type: QOpenGLShader::Vertex, source: qt_glsl_vertexShaderProgram)) {
1180 qWarning(msg: "QPainterVideoSurface: Vertex shader compile error %s",
1181 qPrintable(m_program.log()));
1182 error = QAbstractVideoSurface::ResourceError;
1183 } else if (!m_program.addShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragmentProgram)) {
1184 qWarning(msg: "QPainterVideoSurface: Shader compile error %s", qPrintable(m_program.log()));
1185 error = QAbstractVideoSurface::ResourceError;
1186 m_program.removeAllShaders();
1187 } else if(!m_program.link()) {
1188 qWarning(msg: "QPainterVideoSurface: Shader link error %s", qPrintable(m_program.log()));
1189 m_program.removeAllShaders();
1190 error = QAbstractVideoSurface::ResourceError;
1191 } else {
1192 m_handleType = format.handleType();
1193 m_scanLineDirection = format.scanLineDirection();
1194 m_mirrored = format.property(name: "mirrored").toBool();
1195 m_frameSize = format.frameSize();
1196 m_colorSpace = format.yCbCrColorSpace();
1197
1198 if (m_handleType == QAbstractVideoBuffer::NoHandle)
1199 glGenTextures(n: m_textureCount, textures: m_textureIds);
1200 }
1201
1202 return error;
1203}
1204
1205void QVideoSurfaceGlslPainter::stop()
1206{
1207 if (m_context) {
1208 if (m_handleType != QAbstractVideoBuffer::GLTextureHandle)
1209 glDeleteTextures(n: m_textureCount, textures: m_textureIds);
1210 }
1211
1212 m_program.removeAllShaders();
1213
1214 m_textureCount = 0;
1215 m_handleType = QAbstractVideoBuffer::NoHandle;
1216
1217 QVideoSurfaceGLPainter::stop();
1218}
1219
1220QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::paint(
1221 const QRectF &target, QPainter *painter, const QRectF &source)
1222{
1223 if (!m_frame.isValid()) {
1224 painter->fillRect(r: target, c: Qt::black);
1225 return QAbstractVideoSurface::NoError;
1226 }
1227
1228 const QAbstractVideoBuffer::HandleType h = m_frame.handleType();
1229 if (h == QAbstractVideoBuffer::NoHandle || h == QAbstractVideoBuffer::GLTextureHandle) {
1230 bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
1231 bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
1232
1233 painter->beginNativePainting();
1234
1235 if (stencilTestEnabled)
1236 glEnable(GL_STENCIL_TEST);
1237 if (scissorTestEnabled)
1238 glEnable(GL_SCISSOR_TEST);
1239
1240 const int width = painter->viewport().width();
1241 const int height = painter->viewport().height();
1242
1243 const QTransform transform = painter->deviceTransform();
1244
1245 const GLfloat wfactor = 2.0 / width;
1246 const GLfloat hfactor = -2.0 / height;
1247
1248 const GLfloat positionMatrix[4][4] =
1249 {
1250 {
1251 /*(0,0)*/ GLfloat(wfactor * transform.m11() - transform.m13()),
1252 /*(0,1)*/ GLfloat(hfactor * transform.m12() + transform.m13()),
1253 /*(0,2)*/ 0.0,
1254 /*(0,3)*/ GLfloat(transform.m13())
1255 }, {
1256 /*(1,0)*/ GLfloat(wfactor * transform.m21() - transform.m23()),
1257 /*(1,1)*/ GLfloat(hfactor * transform.m22() + transform.m23()),
1258 /*(1,2)*/ 0.0,
1259 /*(1,3)*/ GLfloat(transform.m23())
1260 }, {
1261 /*(2,0)*/ 0.0,
1262 /*(2,1)*/ 0.0,
1263 /*(2,2)*/ -1.0,
1264 /*(2,3)*/ 0.0
1265 }, {
1266 /*(3,0)*/ GLfloat(wfactor * transform.dx() - transform.m33()),
1267 /*(3,1)*/ GLfloat(hfactor * transform.dy() + transform.m33()),
1268 /*(3,2)*/ 0.0,
1269 /*(3,3)*/ GLfloat(transform.m33())
1270 }
1271 };
1272
1273 const GLfloat vertexCoordArray[] =
1274 {
1275 GLfloat(target.left()) , GLfloat(target.bottom() + 1),
1276 GLfloat(target.right() + 1), GLfloat(target.bottom() + 1),
1277 GLfloat(target.left()) , GLfloat(target.top()),
1278 GLfloat(target.right() + 1), GLfloat(target.top())
1279 };
1280
1281 const GLfloat txLeft = m_mirrored ? source.right() / m_frameSize.width()
1282 : source.left() / m_frameSize.width();
1283 const GLfloat txRight = m_mirrored ? source.left() / m_frameSize.width()
1284 : source.right() / m_frameSize.width();
1285 const GLfloat txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
1286 ? source.top() / m_frameSize.height()
1287 : source.bottom() / m_frameSize.height();
1288 const GLfloat txBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
1289 ? source.bottom() / m_frameSize.height()
1290 : source.top() / m_frameSize.height();
1291
1292 const GLfloat textureCoordArray[] =
1293 {
1294 txLeft , txBottom,
1295 txRight, txBottom,
1296 txLeft , txTop,
1297 txRight, txTop
1298 };
1299
1300 m_program.bind();
1301
1302 m_program.enableAttributeArray(name: "vertexCoordArray");
1303 m_program.enableAttributeArray(name: "textureCoordArray");
1304 m_program.setAttributeArray(name: "vertexCoordArray", values: vertexCoordArray, tupleSize: 2);
1305 m_program.setAttributeArray(name: "textureCoordArray", values: textureCoordArray, tupleSize: 2);
1306 m_program.setUniformValue(name: "positionMatrix", value: positionMatrix);
1307
1308 if (m_textureCount == 3) {
1309 glActiveTexture(GL_TEXTURE0);
1310 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[0]);
1311 glActiveTexture(GL_TEXTURE1);
1312 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[1]);
1313 glActiveTexture(GL_TEXTURE2);
1314 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[2]);
1315 glActiveTexture(GL_TEXTURE0);
1316
1317 m_program.setUniformValue(name: "texY", value: 0);
1318 m_program.setUniformValue(name: "texU", value: 1);
1319 m_program.setUniformValue(name: "texV", value: 2);
1320 } else {
1321 glActiveTexture(GL_TEXTURE0);
1322 glBindTexture(GL_TEXTURE_2D, texture: m_textureIds[0]);
1323
1324 m_program.setUniformValue(name: "texRgb", value: 0);
1325 }
1326 m_program.setUniformValue(name: "colorMatrix", value: m_colorMatrix);
1327
1328 glDrawArrays(GL_TRIANGLE_STRIP, first: 0, count: 4);
1329
1330 m_program.release();
1331
1332 painter->endNativePainting();
1333
1334 return QAbstractVideoSurface::NoError;
1335 }
1336
1337 return QVideoSurfaceGLPainter::paint(target, painter, source);
1338}
1339
1340#endif
1341
1342/*!
1343 \class QPainterVideoSurface
1344 \internal
1345*/
1346
1347/*!
1348*/
1349QPainterVideoSurface::QPainterVideoSurface(QObject *parent)
1350 : QAbstractVideoSurface(parent)
1351 , m_painter(0)
1352#if QT_CONFIG(opengl)
1353 , m_glContext(0)
1354 , m_shaderTypes(NoShaders)
1355 , m_shaderType(NoShaders)
1356#endif
1357 , m_brightness(0)
1358 , m_contrast(0)
1359 , m_hue(0)
1360 , m_saturation(0)
1361 , m_pixelFormat(QVideoFrame::Format_Invalid)
1362 , m_colorsDirty(true)
1363 , m_ready(false)
1364{
1365}
1366
1367/*!
1368*/
1369QPainterVideoSurface::~QPainterVideoSurface()
1370{
1371 if (isActive())
1372 m_painter->stop();
1373
1374 delete m_painter;
1375}
1376
1377/*!
1378*/
1379QList<QVideoFrame::PixelFormat> QPainterVideoSurface::supportedPixelFormats(
1380 QAbstractVideoBuffer::HandleType handleType) const
1381{
1382 if (!m_painter)
1383 const_cast<QPainterVideoSurface *>(this)->createPainter();
1384
1385 return m_painter->supportedPixelFormats(handleType);
1386}
1387
1388/*!
1389*/
1390bool QPainterVideoSurface::isFormatSupported(const QVideoSurfaceFormat &format) const
1391{
1392 if (!m_painter)
1393 const_cast<QPainterVideoSurface *>(this)->createPainter();
1394
1395 return m_painter->isFormatSupported(format);
1396}
1397
1398/*!
1399*/
1400bool QPainterVideoSurface::start(const QVideoSurfaceFormat &format)
1401{
1402 if (isActive())
1403 m_painter->stop();
1404
1405 if (!m_painter)
1406 createPainter();
1407
1408 if (format.frameSize().isEmpty()) {
1409 setError(UnsupportedFormatError);
1410 } else {
1411 QAbstractVideoSurface::Error error = m_painter->start(format);
1412
1413 if (error != QAbstractVideoSurface::NoError) {
1414 setError(error);
1415 } else {
1416 m_pixelFormat = format.pixelFormat();
1417 m_frameSize = format.frameSize();
1418 m_sourceRect = format.viewport();
1419 m_colorsDirty = true;
1420 m_ready = true;
1421
1422 return QAbstractVideoSurface::start(format);
1423 }
1424 }
1425
1426 QAbstractVideoSurface::stop();
1427
1428 return false;
1429}
1430
1431/*!
1432*/
1433void QPainterVideoSurface::stop()
1434{
1435 if (isActive()) {
1436 m_painter->stop();
1437 m_ready = false;
1438
1439 QAbstractVideoSurface::stop();
1440 }
1441}
1442
1443/*!
1444*/
1445bool QPainterVideoSurface::present(const QVideoFrame &frame)
1446{
1447 if (!m_ready) {
1448 if (!isActive()) {
1449 setError(StoppedError);
1450 return false;
1451 }
1452 } else if (frame.isValid()
1453 && (frame.pixelFormat() != m_pixelFormat || frame.size() != m_frameSize)) {
1454 setError(IncorrectFormatError);
1455
1456 stop();
1457 return false;
1458 } else {
1459 QAbstractVideoSurface::Error error = m_painter->setCurrentFrame(frame);
1460 if (error != QAbstractVideoSurface::NoError) {
1461 setError(error);
1462 stop();
1463 return false;
1464 }
1465
1466 m_ready = false;
1467 emit frameChanged();
1468 }
1469 return true;
1470}
1471
1472/*!
1473*/
1474int QPainterVideoSurface::brightness() const
1475{
1476 return m_brightness;
1477}
1478
1479/*!
1480*/
1481void QPainterVideoSurface::setBrightness(int brightness)
1482{
1483 m_brightness = brightness;
1484
1485 m_colorsDirty = true;
1486}
1487
1488/*!
1489*/
1490int QPainterVideoSurface::contrast() const
1491{
1492 return m_contrast;
1493}
1494
1495/*!
1496*/
1497void QPainterVideoSurface::setContrast(int contrast)
1498{
1499 m_contrast = contrast;
1500
1501 m_colorsDirty = true;
1502}
1503
1504/*!
1505*/
1506int QPainterVideoSurface::hue() const
1507{
1508 return m_hue;
1509}
1510
1511/*!
1512*/
1513void QPainterVideoSurface::setHue(int hue)
1514{
1515 m_hue = hue;
1516
1517 m_colorsDirty = true;
1518}
1519
1520/*!
1521*/
1522int QPainterVideoSurface::saturation() const
1523{
1524 return m_saturation;
1525}
1526
1527/*!
1528*/
1529void QPainterVideoSurface::setSaturation(int saturation)
1530{
1531 m_saturation = saturation;
1532
1533 m_colorsDirty = true;
1534}
1535
1536/*!
1537*/
1538bool QPainterVideoSurface::isReady() const
1539{
1540 return m_ready;
1541}
1542
1543/*!
1544*/
1545void QPainterVideoSurface::setReady(bool ready)
1546{
1547 m_ready = ready;
1548}
1549
1550/*!
1551*/
1552void QPainterVideoSurface::paint(QPainter *painter, const QRectF &target, const QRectF &source)
1553{
1554 if (!isActive()) {
1555 painter->fillRect(target, QBrush(Qt::black));
1556 } else {
1557 if (m_colorsDirty) {
1558 m_painter->updateColors(brightness: m_brightness, contrast: m_contrast, hue: m_hue, saturation: m_saturation);
1559 m_colorsDirty = false;
1560 }
1561
1562 const QRectF sourceRect(
1563 m_sourceRect.x() + m_sourceRect.width() * source.x(),
1564 m_sourceRect.y() + m_sourceRect.height() * source.y(),
1565 m_sourceRect.width() * source.width(),
1566 m_sourceRect.height() * source.height());
1567
1568 QAbstractVideoSurface::Error error = m_painter->paint(target, painter, source: sourceRect);
1569
1570 if (error != QAbstractVideoSurface::NoError) {
1571 setError(error);
1572
1573 stop();
1574 }
1575 }
1576}
1577
1578/*!
1579 \fn QPainterVideoSurface::frameChanged()
1580*/
1581
1582#if QT_CONFIG(opengl)
1583
1584/*!
1585*/
1586const QOpenGLContext *QPainterVideoSurface::glContext() const
1587{
1588 return m_glContext;
1589}
1590
1591/*!
1592*/
1593void QPainterVideoSurface::updateGLContext()
1594{
1595 auto oldContext = m_glContext;
1596 m_glContext = QOpenGLContext::currentContext();
1597 if (oldContext == m_glContext)
1598 return;
1599
1600 m_shaderTypes = NoShaders;
1601
1602 if (m_glContext) {
1603 //Set a dynamic property to access the OpenGL context
1604 this->setProperty(name: "GLContext", value: QVariant::fromValue<QObject *>(value: m_glContext));
1605
1606 const QByteArray extensions(reinterpret_cast<const char *>(
1607 m_glContext->functions()->glGetString(GL_EXTENSIONS)));
1608#if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_DYNAMIC)
1609 if (extensions.contains(c: "ARB_fragment_program"))
1610 m_shaderTypes |= FragmentProgramShader;
1611#endif
1612 if (QOpenGLShaderProgram::hasOpenGLShaderPrograms(context: m_glContext)
1613#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_DYNAMIC)
1614 && extensions.contains(c: "ARB_shader_objects")
1615#endif
1616 )
1617 m_shaderTypes |= GlslShader;
1618 }
1619
1620 ShaderType type = (m_shaderType & m_shaderTypes)
1621 ? m_shaderType
1622 : NoShaders;
1623
1624 if (type != m_shaderType || type != NoShaders) {
1625 m_shaderType = type;
1626
1627 if (isActive()) {
1628 m_painter->stop();
1629 delete m_painter;
1630 m_painter = 0;
1631 m_ready = false;
1632
1633 setError(ResourceError);
1634 QAbstractVideoSurface::stop();
1635 }
1636 emit supportedFormatsChanged();
1637 }
1638}
1639
1640/*!
1641 \enum QPainterVideoSurface::ShaderType
1642
1643 \value NoShaders
1644 \value FragmentProgramShader
1645 \value HlslShader
1646*/
1647
1648/*!
1649 \typedef QPainterVideoSurface::ShaderTypes
1650*/
1651
1652/*!
1653*/
1654QPainterVideoSurface::ShaderTypes QPainterVideoSurface::supportedShaderTypes() const
1655{
1656 return m_shaderTypes;
1657}
1658
1659/*!
1660*/
1661QPainterVideoSurface::ShaderType QPainterVideoSurface::shaderType() const
1662{
1663 return m_shaderType;
1664}
1665
1666/*!
1667*/
1668void QPainterVideoSurface::setShaderType(ShaderType type)
1669{
1670 if (!(type & m_shaderTypes))
1671 type = NoShaders;
1672
1673 if (type != m_shaderType) {
1674 m_shaderType = type;
1675
1676 if (isActive()) {
1677 m_painter->stop();
1678 delete m_painter;
1679 m_painter = 0;
1680 m_ready = false;
1681
1682 setError(ResourceError);
1683 QAbstractVideoSurface::stop();
1684 } else {
1685 delete m_painter;
1686 m_painter = 0;
1687 }
1688 emit supportedFormatsChanged();
1689 }
1690}
1691
1692#endif
1693
1694void QPainterVideoSurface::viewportDestroyed()
1695{
1696 if (m_painter) {
1697 m_painter->viewportDestroyed();
1698
1699 setError(ResourceError);
1700 stop();
1701 delete m_painter;
1702 m_painter = 0;
1703 }
1704}
1705
1706void QPainterVideoSurface::createPainter()
1707{
1708 Q_ASSERT(!m_painter);
1709
1710#if QT_CONFIG(opengl)
1711 switch (m_shaderType) {
1712#if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_DYNAMIC)
1713 case FragmentProgramShader:
1714 Q_ASSERT(m_glContext);
1715 m_painter = new QVideoSurfaceArbFpPainter(m_glContext);
1716 break;
1717#endif // !QT_OPENGL_ES && !QT_OPENGL_DYNAMIC
1718 case GlslShader:
1719 Q_ASSERT(m_glContext);
1720 m_painter = new QVideoSurfaceGlslPainter(m_glContext);
1721 break;
1722 default:
1723 m_painter = new QVideoSurfaceGenericPainter;
1724 break;
1725 }
1726#else
1727 m_painter = new QVideoSurfaceGenericPainter;
1728#endif
1729}
1730
1731QT_END_NAMESPACE
1732
1733#include "moc_qpaintervideosurface_p.cpp"
1734

source code of qtmultimedia/src/multimediawidgets/qpaintervideosurface.cpp