diff options
author | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2013-10-14 11:21:31 +0200 |
---|---|---|
committer | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2013-10-18 18:07:55 +0200 |
commit | 7d9a800163a92cdbfcc38aa24591c8ad20a6bb19 (patch) | |
tree | daf826cdb7c5476a6c56dbd9a7ecd3c9d7c3cf8d /elements/gstqtvideosink/painters | |
parent | d46c146834c7fff787094e1b715c973a17998a27 (diff) |
qtvideosink: Distribute classes into subdirectories for easier code navigation
Diffstat (limited to 'elements/gstqtvideosink/painters')
5 files changed, 1126 insertions, 0 deletions
diff --git a/elements/gstqtvideosink/painters/abstractsurfacepainter.h b/elements/gstqtvideosink/painters/abstractsurfacepainter.h new file mode 100644 index 0000000..3e90a77 --- /dev/null +++ b/elements/gstqtvideosink/painters/abstractsurfacepainter.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com> + Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com> + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef ABSTRACTSURFACEPAINTER_H +#define ABSTRACTSURFACEPAINTER_H + +#include "../utils/bufferformat.h" +#include <QRectF> + +class QPainter; + +/** Common interface for all the painters */ +class AbstractSurfacePainter +{ +public: + virtual ~AbstractSurfacePainter() {} + + virtual bool supportsFormat(GstVideoFormat format) const = 0; + + virtual void init(const BufferFormat & format) = 0; + virtual void cleanup() = 0; + + virtual void paint(quint8 *data, const BufferFormat & frameFormat, const QRectF & clipRect, + QPainter *painter, const PaintAreas & areas) = 0; + + virtual void updateColors(int brightness, int contrast, int hue, int saturation) = 0; +}; + +#endif diff --git a/elements/gstqtvideosink/painters/genericsurfacepainter.cpp b/elements/gstqtvideosink/painters/genericsurfacepainter.cpp new file mode 100644 index 0000000..864ea18 --- /dev/null +++ b/elements/gstqtvideosink/painters/genericsurfacepainter.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com> + Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com> + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "genericsurfacepainter.h" +#include <QPainter> + +GenericSurfacePainter::GenericSurfacePainter() + : m_imageFormat(QImage::Format_Invalid) +{ +} + +//static +QSet<GstVideoFormat> GenericSurfacePainter::supportedPixelFormats() +{ + return QSet<GstVideoFormat>() +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + << GST_VIDEO_FORMAT_ARGB + << GST_VIDEO_FORMAT_xRGB +#else + << GST_VIDEO_FORMAT_BGRA + << GST_VIDEO_FORMAT_BGRx +#endif + << GST_VIDEO_FORMAT_RGB + << GST_VIDEO_FORMAT_RGB16 + ; +} + +void GenericSurfacePainter::init(const BufferFormat &format) +{ + switch (format.videoFormat()) { + // QImage is shitty and reads integers instead of bytes, + // thus it is affected by the host's endianness +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + case GST_VIDEO_FORMAT_ARGB: +#else + case GST_VIDEO_FORMAT_BGRA: +#endif + m_imageFormat = QImage::Format_ARGB32; + break; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + case GST_VIDEO_FORMAT_xRGB: +#else + case GST_VIDEO_FORMAT_BGRx: +#endif + m_imageFormat = QImage::Format_RGB32; + break; + //16-bit RGB formats use host's endianness in GStreamer + //FIXME-0.11 do endianness checks like above if semantics have changed + case GST_VIDEO_FORMAT_RGB16: + m_imageFormat = QImage::Format_RGB16; + break; + //This is not affected by endianness + case GST_VIDEO_FORMAT_RGB: + m_imageFormat = QImage::Format_RGB888; + break; + default: + throw QString("Unsupported format"); + } +} + +void GenericSurfacePainter::cleanup() +{ + m_imageFormat = QImage::Format_Invalid; +} + +void GenericSurfacePainter::paint(quint8 *data, + const BufferFormat & frameFormat, + const QRectF & clipRect, + QPainter *painter, + const PaintAreas & areas) +{ + Q_ASSERT(m_imageFormat != QImage::Format_Invalid); + + QImage image( + data, + frameFormat.frameSize().width(), + frameFormat.frameSize().height(), + frameFormat.bytesPerLine(), + m_imageFormat); + + painter->fillRect(areas.blackArea1, Qt::black); + painter->drawImage(areas.videoArea, image, clipRect); + painter->fillRect(areas.blackArea2, Qt::black); +} + +void GenericSurfacePainter::updateColors(int, int, int, int) +{ +} diff --git a/elements/gstqtvideosink/painters/genericsurfacepainter.h b/elements/gstqtvideosink/painters/genericsurfacepainter.h new file mode 100644 index 0000000..66325df --- /dev/null +++ b/elements/gstqtvideosink/painters/genericsurfacepainter.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com> + Copyright (C) 2011 Collabora Ltd. <info@collabora.com> + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef GENERICSURFACEPAINTER_H +#define GENERICSURFACEPAINTER_H + +#include "abstractsurfacepainter.h" +#include <QSet> +#include <QImage> + +/** + * Generic painter that paints using the QPainter API. + * No colorspace conversion is done and no colors adjustment either. + */ +class GenericSurfacePainter : public AbstractSurfacePainter +{ +public: + GenericSurfacePainter(); + + static QSet<GstVideoFormat> supportedPixelFormats(); + + virtual bool supportsFormat(GstVideoFormat format) const { + return supportedPixelFormats().contains(format); + } + + virtual void init(const BufferFormat &format); + virtual void cleanup(); + + virtual void paint(quint8 *data, const BufferFormat & frameFormat, const QRectF & clipRect, + QPainter *painter, const PaintAreas & areas); + + virtual void updateColors(int brightness, int contrast, int hue, int saturation); + +private: + QImage::Format m_imageFormat; +}; + +#endif // GENERICSURFACEPAINTER_H diff --git a/elements/gstqtvideosink/painters/openglsurfacepainter.cpp b/elements/gstqtvideosink/painters/openglsurfacepainter.cpp new file mode 100644 index 0000000..b3598ec --- /dev/null +++ b/elements/gstqtvideosink/painters/openglsurfacepainter.cpp @@ -0,0 +1,797 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com> + Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com> + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "openglsurfacepainter.h" +#include <QtCore/qmath.h> + +#ifndef GL_TEXTURE0 +# define GL_TEXTURE0 0x84C0 +# define GL_TEXTURE1 0x84C1 +# define GL_TEXTURE2 0x84C2 +#endif + +#ifndef GL_PROGRAM_ERROR_STRING_ARB +# define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#endif + +#ifndef GL_UNSIGNED_SHORT_5_6_5 +# define GL_UNSIGNED_SHORT_5_6_5 33635 +#endif + +#ifndef GL_CLAMP_TO_EDGE +# define GL_CLAMP_TO_EDGE 0x812F +#endif + +#define QRECT_TO_GLMATRIX(rect) \ + { \ + GLfloat(rect.left()) , GLfloat(rect.bottom() + 1), \ + GLfloat(rect.right() + 1), GLfloat(rect.bottom() + 1), \ + GLfloat(rect.left()) , GLfloat(rect.top()), \ + GLfloat(rect.right() + 1), GLfloat(rect.top()) \ + } + +OpenGLSurfacePainter::OpenGLSurfacePainter() + : m_textureFormat(0) + , m_textureInternalFormat(0) + , m_textureType(0) + , m_textureCount(0) + , m_videoColorMatrix(GST_VIDEO_COLOR_MATRIX_UNKNOWN) +{ +#ifndef QT_OPENGL_ES + glActiveTexture = (_glActiveTexture) QGLContext::currentContext()->getProcAddress( + QLatin1String("glActiveTexture")); +#endif +} + +//static +QSet<GstVideoFormat> OpenGLSurfacePainter::supportedPixelFormats() +{ + return QSet<GstVideoFormat>() + //also handled by the generic painter on LE + << GST_VIDEO_FORMAT_BGRA + << GST_VIDEO_FORMAT_BGRx + + //also handled by the generic painter on BE + << GST_VIDEO_FORMAT_ARGB + << GST_VIDEO_FORMAT_xRGB + + //also handled by the generic painter everywhere + << GST_VIDEO_FORMAT_RGB + << GST_VIDEO_FORMAT_RGB16 + + //not handled by the generic painter + << GST_VIDEO_FORMAT_BGR + << GST_VIDEO_FORMAT_v308 + << GST_VIDEO_FORMAT_AYUV + << GST_VIDEO_FORMAT_YV12 + << GST_VIDEO_FORMAT_I420 + ; +} + +void OpenGLSurfacePainter::updateColors(int brightness, int contrast, int hue, int saturation) +{ + const qreal b = brightness / 200.0; + const qreal c = contrast / 100.0 + 1.0; + const qreal h = hue / 100.0; + const qreal s = saturation / 100.0 + 1.0; + + const qreal cosH = qCos(M_PI * h); + const qreal sinH = qSin(M_PI * h); + + const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213; + const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213; + const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213; + + const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715; + const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715; + const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715; + + const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072; + const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072; + const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072; + + const qreal sr = (1.0 - s) * 0.3086; + const qreal sg = (1.0 - s) * 0.6094; + const qreal sb = (1.0 - s) * 0.0820; + + const qreal sr_s = sr + s; + const qreal sg_s = sg + s; + const qreal sb_s = sr + s; + + const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b); + + m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31); + m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32); + m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33); + m_colorMatrix(0, 3) = m4; + + m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31); + m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32); + m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33); + m_colorMatrix(1, 3) = m4; + + m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31); + m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32); + m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33); + m_colorMatrix(2, 3) = m4; + + m_colorMatrix(3, 0) = 0.0; + m_colorMatrix(3, 1) = 0.0; + m_colorMatrix(3, 2) = 0.0; + m_colorMatrix(3, 3) = 1.0; + + switch (m_videoColorMatrix) { +#if 0 + //I have no idea what this is - it's not needed currently in this code + case BufferFormat::YCbCr_JPEG: + m_colorMatrix *= QMatrix4x4( + 1.0, 0.000, 1.402, -0.701, + 1.0, -0.344, -0.714, 0.529, + 1.0, 1.772, 0.000, -0.886, + 0.0, 0.000, 0.000, 1.0000); + break; +#endif + case GST_VIDEO_COLOR_MATRIX_BT709: + m_colorMatrix *= QMatrix4x4( + 1.164, 0.000, 1.793, -0.5727, + 1.164, -0.534, -0.213, 0.3007, + 1.164, 2.115, 0.000, -1.1302, + 0.0, 0.000, 0.000, 1.0000); + break; + case GST_VIDEO_COLOR_MATRIX_BT601: + m_colorMatrix *= QMatrix4x4( + 1.164, 0.000, 1.596, -0.8708, + 1.164, -0.392, -0.813, 0.5296, + 1.164, 2.017, 0.000, -1.081, + 0.0, 0.000, 0.000, 1.0000); + break; + default: + break; + } +} + +void OpenGLSurfacePainter::paint(quint8 *data, + const BufferFormat & frameFormat, + const QRectF & clipRect, + QPainter *painter, + const PaintAreas & areas) +{ + // if these are enabled, we need to reenable them after beginNativePainting() + // has been called, as they may get disabled + bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST); + bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST); + + painter->beginNativePainting(); + + if (stencilTestEnabled) + glEnable(GL_STENCIL_TEST); + if (scissorTestEnabled) + glEnable(GL_SCISSOR_TEST); + + const GLfloat vertexCoordArray[] = QRECT_TO_GLMATRIX(areas.videoArea); + + const GLfloat txLeft = clipRect.left() / frameFormat.frameSize().width(); + const GLfloat txRight = (clipRect.right() + 1) / frameFormat.frameSize().width(); + const GLfloat txTop = clipRect.top() / frameFormat.frameSize().height(); + const GLfloat txBottom = (clipRect.bottom() + 1) / frameFormat.frameSize().height(); + + const GLfloat textureCoordArray[] = + { + txLeft , txBottom, + txRight, txBottom, + txLeft , txTop, + txRight, txTop + }; + + for (int i = 0; i < m_textureCount; ++i) { + glBindTexture(GL_TEXTURE_2D, m_textureIds[i]); + glTexImage2D( + GL_TEXTURE_2D, + 0, + m_textureInternalFormat, + m_textureWidths[i], + m_textureHeights[i], + 0, + m_textureFormat, + m_textureType, + data + m_textureOffsets[i]); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + paintImpl(painter, vertexCoordArray, textureCoordArray); + + painter->endNativePainting(); + painter->fillRect(areas.blackArea1, Qt::black); + painter->fillRect(areas.blackArea2, Qt::black); +} + +void OpenGLSurfacePainter::initRgbTextureInfo( + GLenum internalFormat, GLuint format, GLenum type, const QSize &size) +{ +#ifndef QT_OPENGL_ES + //make sure we get 8 bits per component, at least on the desktop GL where we can + switch(internalFormat) { + case GL_RGBA: + internalFormat = GL_RGBA8; + break; + case GL_RGB: + internalFormat = GL_RGB8; + break; + default: + break; + } +#endif + + m_textureInternalFormat = internalFormat; + m_textureFormat = format; + m_textureType = type; + m_textureCount = 1; + m_textureWidths[0] = size.width(); + m_textureHeights[0] = size.height(); + m_textureOffsets[0] = 0; +} + +void OpenGLSurfacePainter::initYuv420PTextureInfo(const QSize &size) +{ + int bytesPerLine = (size.width() + 3) & ~3; + int bytesPerLine2 = (size.width() / 2 + 3) & ~3; + + m_textureInternalFormat = GL_LUMINANCE; + m_textureFormat = GL_LUMINANCE; + m_textureType = GL_UNSIGNED_BYTE; + m_textureCount = 3; + m_textureWidths[0] = bytesPerLine; + m_textureHeights[0] = size.height(); + m_textureOffsets[0] = 0; + m_textureWidths[1] = bytesPerLine2; + m_textureHeights[1] = size.height() / 2; + m_textureOffsets[1] = bytesPerLine * size.height(); + m_textureWidths[2] = bytesPerLine2; + m_textureHeights[2] = size.height() / 2; + m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2; +} + +void OpenGLSurfacePainter::initYv12TextureInfo(const QSize &size) +{ + int bytesPerLine = (size.width() + 3) & ~3; + int bytesPerLine2 = (size.width() / 2 + 3) & ~3; + + m_textureInternalFormat = GL_LUMINANCE; + m_textureFormat = GL_LUMINANCE; + m_textureType = GL_UNSIGNED_BYTE; + m_textureCount = 3; + m_textureWidths[0] = bytesPerLine; + m_textureHeights[0] = size.height(); + m_textureOffsets[0] = 0; + m_textureWidths[1] = bytesPerLine2; + m_textureHeights[1] = size.height() / 2; + m_textureOffsets[1] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2; + m_textureWidths[2] = bytesPerLine2; + m_textureHeights[2] = size.height() / 2; + m_textureOffsets[2] = bytesPerLine * size.height(); +} + +#ifndef QT_OPENGL_ES + +# ifndef GL_FRAGMENT_PROGRAM_ARB +# define GL_FRAGMENT_PROGRAM_ARB 0x8804 +# define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +# endif + +// Interprets the RGBA texture as in fact being BGRx and paints it. +static const char *qt_arbfp_bgrxShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP bgrx;\n" + "TEX bgrx.xyz, fragment.texcoord[0], texture[0], 2D;\n" + "MOV bgrx.w, matrix[3].w;\n" + "DP4 result.color.x, bgrx.zyxw, matrix[0];\n" + "DP4 result.color.y, bgrx.zyxw, matrix[1];\n" + "DP4 result.color.z, bgrx.zyxw, matrix[2];\n" + "END"; + +// Interprets the RGBA texture as in fact being BGRA and paints it. +static const char *qt_arbfp_bgraShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP bgra;\n" + "TEX bgra, fragment.texcoord[0], texture[0], 2D;\n" + "MOV bgra.w, matrix[3].w;\n" + "DP4 result.color.x, bgra.zyxw, matrix[0];\n" + "DP4 result.color.y, bgra.zyxw, matrix[1];\n" + "DP4 result.color.z, bgra.zyxw, matrix[2];\n" + "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n" + "END"; + +// Interprets the RGBA texture as in fact being xRGB and paints it. +static const char *qt_arbfp_xrgbShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP xrgb;\n" + "TEX xrgb, fragment.texcoord[0], texture[0], 2D;\n" + "MOV xrgb.x, matrix[3].w;\n" + "DP4 result.color.x, xrgb.yzwx, matrix[0];\n" + "DP4 result.color.y, xrgb.yzwx, matrix[1];\n" + "DP4 result.color.z, xrgb.yzwx, matrix[2];\n" + "END"; + +// Interprets the RGBA texture as in fact being ARGB and paints it. +static const char *qt_arbfp_argbShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP argb;\n" + "TEX argb, fragment.texcoord[0], texture[0], 2D;\n" + "MOV argb.x, matrix[3].w;\n" + "DP4 result.color.x, argb.yzwx, matrix[0];\n" + "DP4 result.color.y, argb.yzwx, matrix[1];\n" + "DP4 result.color.z, argb.yzwx, matrix[2];\n" + "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n" + "END"; + +// Paints RGB frames without doing any color channel flipping. +static const char *qt_arbfp_rgbxShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP rgb;\n" + "TEX rgb.xyz, fragment.texcoord[0], texture[0], 2D;\n" + "MOV rgb.w, matrix[3].w;\n" + "DP4 result.color.x, rgb, matrix[0];\n" + "DP4 result.color.y, rgb, matrix[1];\n" + "DP4 result.color.z, rgb, matrix[2];\n" + "END"; + +// Paints a YUV420P or YV12 frame. +static const char *qt_arbfp_yuvPlanarShaderProgram = + "!!ARBfp1.0\n" + "PARAM matrix[4] = { program.local[0..2]," + "{ 0.0, 0.0, 0.0, 1.0 } };\n" + "TEMP yuv;\n" + "TEX yuv.x, fragment.texcoord[0], texture[0], 2D;\n" + "TEX yuv.y, fragment.texcoord[0], texture[1], 2D;\n" + "TEX yuv.z, fragment.texcoord[0], texture[2], 2D;\n" + "MOV yuv.w, matrix[3].w;\n" + "DP4 result.color.x, yuv, matrix[0];\n" + "DP4 result.color.y, yuv, matrix[1];\n" + "DP4 result.color.z, yuv, matrix[2];\n" + "END"; + + + +ArbFpSurfacePainter::ArbFpSurfacePainter() + : OpenGLSurfacePainter() + , m_programId(0) +{ + const QGLContext *context = QGLContext::currentContext(); + + glProgramStringARB = (_glProgramStringARB) context->getProcAddress( + QLatin1String("glProgramStringARB")); + glBindProgramARB = (_glBindProgramARB) context->getProcAddress( + QLatin1String("glBindProgramARB")); + glDeleteProgramsARB = (_glDeleteProgramsARB) context->getProcAddress( + QLatin1String("glDeleteProgramsARB")); + glGenProgramsARB = (_glGenProgramsARB) context->getProcAddress( + QLatin1String("glGenProgramsARB")); + glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) context->getProcAddress( + QLatin1String("glProgramLocalParameter4fARB")); +} + +void ArbFpSurfacePainter::init(const BufferFormat &format) +{ + Q_ASSERT(m_textureCount == 0); + + const char *program = 0; + + switch (format.videoFormat()) { + case GST_VIDEO_FORMAT_BGRx: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_bgrxShaderProgram; + break; + case GST_VIDEO_FORMAT_xRGB: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_xrgbShaderProgram; + break; + case GST_VIDEO_FORMAT_BGRA: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_bgraShaderProgram; + break; + case GST_VIDEO_FORMAT_ARGB: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_argbShaderProgram; + break; + case GST_VIDEO_FORMAT_RGB: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_BGR: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_bgrxShaderProgram; + break; + //NOTE: unlike the other formats, this is endianness-dependent, + //but using GL_UNSIGNED_SHORT_5_6_5 ensures that it's handled correctly + case GST_VIDEO_FORMAT_RGB16: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize()); + program = qt_arbfp_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_v308: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_AYUV: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + program = qt_arbfp_argbShaderProgram; + break; + case GST_VIDEO_FORMAT_YV12: + initYv12TextureInfo(format.frameSize()); + program = qt_arbfp_yuvPlanarShaderProgram; + break; + case GST_VIDEO_FORMAT_I420: + initYuv420PTextureInfo(format.frameSize()); + program = qt_arbfp_yuvPlanarShaderProgram; + break; + default: + Q_ASSERT(false); + break; + } + + m_videoColorMatrix = format.colorMatrix(); + + glGenProgramsARB(1, &m_programId); + + GLenum glError = glGetError(); + if (glError != GL_NO_ERROR) { + throw QString("ARBfb Shader allocation error ") + + QString::number(static_cast<int>(glError), 16); + } else { + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId); + glProgramStringARB( + GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + qstrlen(program), + reinterpret_cast<const GLvoid *>(program)); + + if ((glError = glGetError()) != GL_NO_ERROR) { + const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB); + + glDeleteProgramsARB(1, &m_programId); + m_textureCount = 0; + m_programId = 0; + + throw QString("ARBfp Shader compile error ") + + QString::number(static_cast<int>(glError), 16) + + reinterpret_cast<const char *>(errorString); + } else { + glGenTextures(m_textureCount, m_textureIds); + } + } +} + +void ArbFpSurfacePainter::cleanup() +{ + glDeleteTextures(m_textureCount, m_textureIds); + glDeleteProgramsARB(1, &m_programId); + + m_textureCount = 0; + m_programId = 0; +} + +void ArbFpSurfacePainter::paintImpl(const QPainter *painter, + const GLfloat *vertexCoordArray, + const GLfloat *textureCoordArray) +{ + Q_UNUSED(painter); + + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId); + + glProgramLocalParameter4fARB( + GL_FRAGMENT_PROGRAM_ARB, + 0, + m_colorMatrix(0, 0), + m_colorMatrix(0, 1), + m_colorMatrix(0, 2), + m_colorMatrix(0, 3)); + glProgramLocalParameter4fARB( + GL_FRAGMENT_PROGRAM_ARB, + 1, + m_colorMatrix(1, 0), + m_colorMatrix(1, 1), + m_colorMatrix(1, 2), + m_colorMatrix(1, 3)); + glProgramLocalParameter4fARB( + GL_FRAGMENT_PROGRAM_ARB, + 2, + m_colorMatrix(2, 0), + m_colorMatrix(2, 1), + m_colorMatrix(2, 2), + m_colorMatrix(2, 3)); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureIds[0]); + + if (m_textureCount == 3) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_textureIds[1]); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_textureIds[2]); + glActiveTexture(GL_TEXTURE0); + } + + glVertexPointer(2, GL_FLOAT, 0, vertexCoordArray); + glTexCoordPointer(2, GL_FLOAT, 0, textureCoordArray); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_FRAGMENT_PROGRAM_ARB); +} + +#endif + +static const char *qt_glsl_vertexShaderProgram = + "attribute highp vec4 vertexCoordArray;\n" + "attribute highp vec2 textureCoordArray;\n" + "uniform highp mat4 positionMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " gl_Position = positionMatrix * vertexCoordArray;\n" + " textureCoord = textureCoordArray;\n" + "}\n"; + +// Interprets the RGBA texture as in fact being BGRx and paints it. +static const char *qt_glsl_bgrxShaderProgram = + "uniform sampler2D texRgb;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n" + " gl_FragColor = colorMatrix * color;\n" + "}\n"; + +// Interprets the RGBA texture as in fact being BGRA and paints it. +static const char *qt_glsl_bgraShaderProgram = + "uniform sampler2D texRgb;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n" + " color = colorMatrix * color;\n" + " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n" + "}\n"; + +// Interprets the RGBA texture as in fact being xRGB and paints it. +static const char *qt_glsl_xrgbShaderProgram = + "uniform sampler2D texRgb;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n" + " gl_FragColor = colorMatrix * color;\n" + "}\n"; + +// Interprets the RGBA texture as in fact being ARGB and paints it. +static const char *qt_glsl_argbShaderProgram = + "uniform sampler2D texRgb;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n" + " color = colorMatrix * color;\n" + " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).r);\n" + "}\n"; + +// Paints RGB frames without doing any color channel flipping. +static const char *qt_glsl_rgbxShaderProgram = + "uniform sampler2D texRgb;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).rgb, 1.0);\n" + " gl_FragColor = colorMatrix * color;\n" + "}\n"; + +// Paints planar yuv frames. +static const char *qt_glsl_yuvPlanarShaderProgram = + "uniform sampler2D texY;\n" + "uniform sampler2D texU;\n" + "uniform sampler2D texV;\n" + "uniform mediump mat4 colorMatrix;\n" + "varying highp vec2 textureCoord;\n" + "void main(void)\n" + "{\n" + " highp vec4 color = vec4(\n" + " texture2D(texY, textureCoord.st).r,\n" + " texture2D(texU, textureCoord.st).r,\n" + " texture2D(texV, textureCoord.st).r,\n" + " 1.0);\n" + " gl_FragColor = colorMatrix * color;\n" + "}\n"; + + +GlslSurfacePainter::GlslSurfacePainter() + : OpenGLSurfacePainter() +{ +} + +void GlslSurfacePainter::init(const BufferFormat &format) +{ + Q_ASSERT(m_textureCount == 0); + + const char *fragmentProgram = 0; + + switch (format.videoFormat()) { + case GST_VIDEO_FORMAT_BGRx: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_bgrxShaderProgram; + break; + case GST_VIDEO_FORMAT_xRGB: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_xrgbShaderProgram; + break; + case GST_VIDEO_FORMAT_BGRA: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_bgraShaderProgram; + break; + case GST_VIDEO_FORMAT_ARGB: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_argbShaderProgram; + break; + case GST_VIDEO_FORMAT_RGB: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_BGR: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_bgrxShaderProgram; + break; + //NOTE: unlike the other formats, this is endianness-dependent, + //but using GL_UNSIGNED_SHORT_5_6_5 ensures that it's handled correctly + case GST_VIDEO_FORMAT_RGB16: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize()); + fragmentProgram = qt_glsl_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_v308: + initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_rgbxShaderProgram; + break; + case GST_VIDEO_FORMAT_AYUV: + initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize()); + fragmentProgram = qt_glsl_argbShaderProgram; + break; + case GST_VIDEO_FORMAT_YV12: + initYv12TextureInfo(format.frameSize()); + fragmentProgram = qt_glsl_yuvPlanarShaderProgram; + break; + case GST_VIDEO_FORMAT_I420: + initYuv420PTextureInfo(format.frameSize()); + fragmentProgram = qt_glsl_yuvPlanarShaderProgram; + break; + default: + Q_ASSERT(false); + break; + } + + m_videoColorMatrix = format.colorMatrix(); + + if (!m_program.addShaderFromSourceCode(QGLShader::Vertex, qt_glsl_vertexShaderProgram)) { + throw QString("Vertex shader compile error ") + m_program.log(); + } + + if (!m_program.addShaderFromSourceCode(QGLShader::Fragment, fragmentProgram)) { + throw QString("Shader compile error ") + m_program.log(); + } + + if(!m_program.link()) { + throw QString("Shader link error ") + m_program.log(); + } + + glGenTextures(m_textureCount, m_textureIds); +} + +void GlslSurfacePainter::cleanup() +{ + glDeleteTextures(m_textureCount, m_textureIds); + m_program.removeAllShaders(); + + m_textureCount = 0; +} + +void GlslSurfacePainter::paintImpl(const QPainter *painter, + const GLfloat *vertexCoordArray, + const GLfloat *textureCoordArray) +{ + const int deviceWidth = painter->device()->width(); + const int deviceHeight = painter->device()->height(); + + const QTransform transform = painter->deviceTransform(); + + const GLfloat wfactor = 2.0 / deviceWidth; + const GLfloat hfactor = -2.0 / deviceHeight; + + const GLfloat positionMatrix[4][4] = + { + { + /*(0,0)*/ GLfloat(wfactor * transform.m11() - transform.m13()), + /*(0,1)*/ GLfloat(hfactor * transform.m12() + transform.m13()), + /*(0,2)*/ 0.0, + /*(0,3)*/ GLfloat(transform.m13()) + }, { + /*(1,0)*/ GLfloat(wfactor * transform.m21() - transform.m23()), + /*(1,1)*/ GLfloat(hfactor * transform.m22() + transform.m23()), + /*(1,2)*/ 0.0, + /*(1,3)*/ GLfloat(transform.m23()) + }, { + /*(2,0)*/ 0.0, + /*(2,1)*/ 0.0, + /*(2,2)*/ -1.0, + /*(2,3)*/ 0.0 + }, { + /*(3,0)*/ GLfloat(wfactor * transform.dx() - transform.m33()), + /*(3,1)*/ GLfloat(hfactor * transform.dy() + transform.m33()), + /*(3,2)*/ 0.0, + /*(3,3)*/ GLfloat(transform.m33()) + } + }; + + m_program.bind(); + + m_program.enableAttributeArray("vertexCoordArray"); + m_program.enableAttributeArray("textureCoordArray"); + m_program.setAttributeArray("vertexCoordArray", vertexCoordArray, 2); + m_program.setAttributeArray("textureCoordArray", textureCoordArray, 2); + m_program.setUniformValue("positionMatrix", positionMatrix); + + if (m_textureCount == 3) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureIds[0]); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_textureIds[1]); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_textureIds[2]); + glActiveTexture(GL_TEXTURE0); + + m_program.setUniformValue("texY", 0); + m_program.setUniformValue("texU", 1); + m_program.setUniformValue("texV", 2); + } else { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureIds[0]); + + m_program.setUniformValue("texRgb", 0); + } + m_program.setUniformValue("colorMatrix", m_colorMatrix); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + m_program.release(); +} diff --git a/elements/gstqtvideosink/painters/openglsurfacepainter.h b/elements/gstqtvideosink/painters/openglsurfacepainter.h new file mode 100644 index 0000000..c1e2dd0 --- /dev/null +++ b/elements/gstqtvideosink/painters/openglsurfacepainter.h @@ -0,0 +1,135 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com> + Copyright (C) 2011-2012 Collabora Ltd. <info@collabora.com> + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef OPENGLSURFACEPAINTER_H +#define OPENGLSURFACEPAINTER_H + +#ifndef GST_QT_VIDEO_SINK_NO_OPENGL + +#include "abstractsurfacepainter.h" +#include <QGLShaderProgram> + +#ifndef Q_WS_MAC +# ifndef APIENTRYP +# ifdef APIENTRY +# define APIENTRYP APIENTRY * +# else +# define APIENTRY +# define APIENTRYP * +# endif +# endif +#else +# define APIENTRY +# define APIENTRYP * +#endif + +class OpenGLSurfacePainter : public AbstractSurfacePainter +{ +public: + OpenGLSurfacePainter(); + + static QSet<GstVideoFormat> supportedPixelFormats(); + + virtual bool supportsFormat(GstVideoFormat format) const { + return supportedPixelFormats().contains(format); + } + + virtual void updateColors(int brightness, int contrast, int hue, int saturation); + virtual void paint(quint8 *data, const BufferFormat & frameFormat, const QRectF & clipRect, + QPainter *painter, const PaintAreas & areas); + +protected: + void initRgbTextureInfo(GLenum internalFormat, GLuint format, GLenum type, const QSize &size); + void initYuv420PTextureInfo(const QSize &size); + void initYv12TextureInfo(const QSize &size); + + virtual void paintImpl(const QPainter *painter, + const GLfloat *vertexCoordArray, + const GLfloat *textureCoordArray) = 0; + +#ifndef QT_OPENGL_ES + typedef void (APIENTRY *_glActiveTexture) (GLenum); + _glActiveTexture glActiveTexture; +#endif + + GLenum m_textureFormat; + GLuint m_textureInternalFormat; + GLenum m_textureType; + int m_textureCount; + GLuint m_textureIds[3]; + int m_textureWidths[3]; + int m_textureHeights[3]; + int m_textureOffsets[3]; + + QMatrix4x4 m_colorMatrix; + GstVideoColorMatrix m_videoColorMatrix; +}; + +#ifndef QT_OPENGL_ES + +class ArbFpSurfacePainter : public OpenGLSurfacePainter +{ +public: + ArbFpSurfacePainter(); + + virtual void init(const BufferFormat & format); + virtual void cleanup(); + +protected: + virtual void paintImpl(const QPainter *painter, + const GLfloat *vertexCoordArray, + const GLfloat *textureCoordArray); + +private: + typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *); + typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint); + typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *); + typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *); + typedef void (APIENTRY *_glProgramLocalParameter4fARB) ( + GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); + typedef void (APIENTRY *_glActiveTexture) (GLenum); + + _glProgramStringARB glProgramStringARB; + _glBindProgramARB glBindProgramARB; + _glDeleteProgramsARB glDeleteProgramsARB; + _glGenProgramsARB glGenProgramsARB; + _glProgramLocalParameter4fARB glProgramLocalParameter4fARB; + + GLuint m_programId; +}; + +#endif + +class GlslSurfacePainter : public OpenGLSurfacePainter +{ +public: + GlslSurfacePainter(); + + virtual void init(const BufferFormat & format); + virtual void cleanup(); + +protected: + virtual void paintImpl(const QPainter *painter, + const GLfloat *vertexCoordArray, + const GLfloat *textureCoordArray); + +private: + QGLShaderProgram m_program; +}; + +#endif // GST_QT_VIDEO_SINK_NO_OPENGL + +#endif // OPENGLSURFACEPAINTER_H |