/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #ifndef QT_NO_OPENGL #include #include #endif QT_BEGIN_NAMESPACE //----------------------------------------------------------------------------- // QEglImageVideoSurface //----------------------------------------------------------------------------- QEglImageVideoSurface::QEglImageVideoSurface(QObject *parent) : QAbstractVideoSurface(parent) , m_frameIsNew(false) , m_ready(false) { } QEglImageVideoSurface::~QEglImageVideoSurface() { if (isActive()) stop(); } QList QEglImageVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const { TRACE("QEglImageVideoSurface::supportedPixelFormats" << qtThisPtr() << "handleType" << handleType); return QList() << QVideoFrame::Format_RGB32 << QVideoFrame::Format_ARGB32; } bool QEglImageVideoSurface::start(const QVideoSurfaceFormat &format) { TRACE("QEglImageVideoSurface::start" << qtThisPtr()); bool result = false; if (isActive()) stop(); if (format.frameSize().isEmpty()) setError(UnsupportedFormatError); else doStart(format); if (error() == QAbstractVideoSurface::NoError) { m_format = format; m_ready = true; result = QAbstractVideoSurface::start(format); } else { QAbstractVideoSurface::stop(); } return result; } void QEglImageVideoSurface::stop() { TRACE("QEglImageVideoSurface::stop" << qtThisPtr()); if (isActive()) { doStop(); m_format = QVideoSurfaceFormat(); m_frame = QVideoFrame(); m_ready = false; QAbstractVideoSurface::stop(); } } bool QEglImageVideoSurface::present(const QVideoFrame &frame) { VERBOSE_TRACE("QEglImageVideoSurface::present" << qtThisPtr() << "active" << isActive() << "ready" << m_ready << "handleType" << frame.handleType() << "handle" << frame.handle().value() << "frame.isValid" << frame.isValid() << "format.pixelFormat" << m_format.pixelFormat() << "format.frameSize" << m_format.frameSize() << "frame.pixelFormat" << frame.pixelFormat() << "frame.Size" << frame.size()); bool result = false; m_frame = frame; m_frameIsNew = true; if (!m_ready) { if (!isActive()) setError(StoppedError); } else if (frame.isValid() && (frame.pixelFormat() != m_format.pixelFormat() || frame.size() != m_format.frameSize())) { setError(IncorrectFormatError); qWarning() << "QEglImageVideoSurface::present received frame of incorrect format, stopping the surface"; stop(); } else { m_ready = false; emit frameChanged(); result = true; } return result; } void QEglImageVideoSurface::viewportDestroyed() { TRACE("QEglImageVideoSurface::viewportDestroyed" << qtThisPtr()); onViewportDestroyed(); doStop(); } void QEglImageVideoSurface::setReady(bool ready) { if (m_ready != ready) { VERBOSE_TRACE("QEglImageVideoSurface::setReady" << qtThisPtr() << "ready" << ready << "frame.isValid" << m_frame.isValid()); m_ready = ready; } } void QEglImageVideoSurface::frameDisplayed() { m_frameIsNew = false; } void QEglImageVideoSurface::onViewportDestroyed() { // Default implementation does nothing } void QEglImageVideoSurface::doStart(const QVideoSurfaceFormat &format) { TRACE("QEglImageVideoSurface::doStart" << qtThisPtr()); Q_UNUSED(format) // Default implementation does nothing } //----------------------------------------------------------------------------- // QEglImageGLVideoSurface //----------------------------------------------------------------------------- #ifndef QT_NO_OPENGL static const char *qt_QEglImageGLVideoSurface_VertexShaderProgram = "attribute highp vec4 vertexCoordArray;\n" "attribute mediump vec2 textureCoordArray;\n" "uniform highp mat4 positionMatrix;\n" "varying mediump vec2 textureCoord;\n" "void main (void)\n" "{\n" " gl_Position = positionMatrix * vertexCoordArray;\n" " textureCoord = textureCoordArray;\n" "}"; static const char* qt_QEglImageGLVideoSurface_FragmentShaderProgram = "uniform sampler2D texRgb;\n" "varying mediump vec2 textureCoord;\n" "void main (void)\n" "{\n" " gl_FragColor = texture2D(texRgb, textureCoord);\n" "}"; QEglImageGLVideoSurface::QEglImageGLVideoSurface(QObject *parent) : QEglImageVideoSurface(parent) , m_context(const_cast(QGLContext::currentContext())) { TRACE("QEglImageGLVideoSurface::QEglImageGLVideoSurface" << qtThisPtr()); Q_ASSERT(m_context); } QEglImageGLVideoSurface::~QEglImageGLVideoSurface() { TRACE("QEglImageGLVideoSurface::~QEglImageGLVideoSurface" << qtThisPtr();) } void QEglImageGLVideoSurface::paint(QPainter *painter, const QRectF &sourceRect, const QRectF &targetRect) { VERBOSE_TRACE("QEglImageGLVideoSurface::paint" << qtThisPtr() << "sourceRect" << sourceRect << "targetRect" << targetRect << "isActive" << isActive() << "frameIsValid" << m_frame.isValid()); if (!isActive() || !m_frame.isValid()) { painter->fillRect(targetRect, QBrush(Qt::black)); } else { const QRectF viewport = m_format.viewport(); const QRectF source(viewport.x() + viewport.width() * sourceRect.x(), viewport.y() + viewport.height() * sourceRect.y(), viewport.width() * sourceRect.width(), viewport.height() * sourceRect.height()); const bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST); const bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST); painter->beginNativePainting(); if (stencilTestEnabled) glEnable(GL_STENCIL_TEST); if (scissorTestEnabled) glEnable(GL_SCISSOR_TEST); const int width = m_context->device()->width(); const int height = m_context->device()->height(); const GLfloat wfactor = 2.0 / width; const GLfloat hfactor = -2.0 / height; const QTransform transform = painter->deviceTransform(); 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()) } }; QVideoSurfaceFormat::Direction scanLineDirection = m_format.scanLineDirection(); const GLfloat vTop = targetRect.top(); const GLfloat vBottom = targetRect.bottom() + 1; const GLfloat vertexCoordArray[] = { GLfloat(targetRect.left()) , GLfloat(vBottom), GLfloat(targetRect.right() + 1), GLfloat(vBottom), GLfloat(targetRect.left()) , GLfloat(vTop), GLfloat(targetRect.right() + 1), GLfloat(vTop) }; const QSize frameSize = m_format.frameSize(); GLfloat txLeft = sourceRect.left(); GLfloat txRight = sourceRect.right(); GLfloat txTop = scanLineDirection == QVideoSurfaceFormat::TopToBottom ? sourceRect.top() : sourceRect.bottom(); GLfloat txBottom = scanLineDirection == QVideoSurfaceFormat::TopToBottom ? sourceRect.bottom() : sourceRect.top(); const GLfloat textureCoordArray[] = { txLeft , txBottom, txRight, txBottom, txLeft , txTop, txRight, txTop }; m_shaderProgram->bind(); m_shaderProgram->enableAttributeArray("vertexCoordArray"); m_shaderProgram->enableAttributeArray("textureCoordArray"); m_shaderProgram->setAttributeArray("vertexCoordArray", vertexCoordArray, 2); m_shaderProgram->setAttributeArray("textureCoordArray", textureCoordArray, 2); m_shaderProgram->setUniformValue("positionMatrix", positionMatrix); m_shaderProgram->setUniformValue("texRgb", 0); glEnable(GL_TEXTURE_2D); m_frame.map(QAbstractVideoBuffer::ReadOnly); bindTexture(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); unbindTexture(); m_frame.unmap(); frameDisplayed(); m_shaderProgram->release(); painter->endNativePainting(); } } void QEglImageGLVideoSurface::onViewportDestroyed() { m_context = 0; } void QEglImageGLVideoSurface::doStart(const QVideoSurfaceFormat &format) { TRACE("QEglImageGLVideoSurface::doStart" << qtThisPtr()); m_context->makeCurrent(); m_shaderProgram = new QGLShaderProgram(m_context, this); if (!m_shaderProgram->addShaderFromSourceCode(QGLShader::Vertex, qt_QEglImageGLVideoSurface_VertexShaderProgram)) { qWarning("QEglImageGLVideoSurface::doStart vertex shader compile error %s", qPrintable(m_shaderProgram->log())); setError(QAbstractVideoSurface::ResourceError); } else if (!m_shaderProgram->addShaderFromSourceCode(QGLShader::Fragment, qt_QEglImageGLVideoSurface_FragmentShaderProgram)) { qWarning("QEglImageGLVideoSurface::doStart vertex shader compile error %s", qPrintable(m_shaderProgram->log())); setError(QAbstractVideoSurface::ResourceError); } else { m_shaderProgram->bindAttributeLocation("textureCoordArray", 1); if (!m_shaderProgram->link()) { qWarning("QEglImageGLVideoSurface::doStart shader link error %s", qPrintable(m_shaderProgram->log())); m_shaderProgram->removeAllShaders(); setError(QAbstractVideoSurface::ResourceError); } } if (error() != QAbstractVideoSurface::NoError) { delete m_shaderProgram; m_shaderProgram = 0; } } void QEglImageGLVideoSurface::doStop() { if (m_context) { m_context->makeCurrent(); m_shaderProgram->removeAllShaders(); } delete m_shaderProgram; m_shaderProgram = 0; } void QEglImageGLVideoSurface::bindTexture() { const GLuint texture = m_frame.handle().value(); VERBOSE_TRACE("QEglImageGLVideoSurface::bindTexture" << qtThisPtr() << "texture" << texture); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } void QEglImageGLVideoSurface::unbindTexture() { VERBOSE_TRACE("QEglImageGLVideoSurface::unbindTexture" << qtThisPtr()); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); } #endif // !QT_NO_OPENGL //----------------------------------------------------------------------------- // QEglImageVGVideoSurface //----------------------------------------------------------------------------- #ifndef QT_NO_OPENVG QEglImageVGVideoSurface::QEglImageVGVideoSurface(QObject *parent) : QEglImageVideoSurface(parent) { TRACE("QEglImageVGVideoSurface::QEglImageVGVideoSurface" << qtThisPtr()); } QEglImageVGVideoSurface::~QEglImageVGVideoSurface() { TRACE("QEglImageVGVideoSurface::~QEglImageVGVideoSurface" << qtThisPtr()); } void QEglImageVGVideoSurface::paint(QPainter *painter, const QRectF &sourceRect, const QRectF &targetRect) { VERBOSE_TRACE("QEglImageVGVideoSurface::paint" << qtThisPtr() << "sourceRect" << sourceRect << "targetRect" << targetRect << "isActive" << isActive()); if (!isActive() || !m_frame.isValid()) { painter->fillRect(targetRect, QBrush(Qt::black)); } else { VERBOSE_TRACE("QEglImageVGVideoSurface::paint" << qtThisPtr()); m_frame.map(QAbstractVideoBuffer::ReadOnly); const VGImage image = m_frame.handle().value(); const int imageWidth = vgGetParameteri(image, VG_IMAGE_WIDTH); const int imageHeight = vgGetParameteri(image, VG_IMAGE_WIDTH); const QRectF subImageRect(sourceRect.left() * imageWidth, sourceRect.top() * imageHeight, sourceRect.width() * imageWidth, sourceRect.height() * imageHeight); const VGImage subImage = vgChildImage(image, subImageRect.left(), subImageRect.top(), subImageRect.width(), subImageRect.height()); const qreal scaleX = targetRect.width() / subImageRect.width(); const qreal scaleY = targetRect.height() / subImageRect.height(); const VGint scissor = vgGeti(VG_SCISSORING); painter->save(); if (QVideoSurfaceFormat::BottomToTop == m_format.scanLineDirection()) { painter->translate(targetRect.bottomLeft()); painter->scale(scaleX, -scaleY); } else { painter->translate(targetRect.topLeft()); painter->scale(scaleX, scaleY); } painter->beginNativePainting(); vgSeti(VG_SCISSORING, scissor); vgDrawImage(subImage); vgDestroyImage(subImage); m_frame.unmap(); frameDisplayed(); painter->endNativePainting(); painter->restore(); } } void QEglImageVGVideoSurface::doStop() { // TODO: release resources } #endif // !QT_NO_OPENVG QT_END_NAMESPACE