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