/* This file is part of the KDE project. Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 or 3 of the License. This library 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 library. If not, see . */ #include "videoframe.h" #include "quicktimevideoplayer.h" #import #import QT_BEGIN_NAMESPACE namespace Phonon { namespace QT7 { VideoFrame::VideoFrame() { initMembers(); } VideoFrame::VideoFrame(QuickTimeVideoPlayer *videoPlayer) { initMembers(); m_videoPlayer = videoPlayer; } VideoFrame::VideoFrame(const VideoFrame& frame) { copyMembers(frame); retain(); } void VideoFrame::operator=(const VideoFrame& frame) { if (this == &frame) return; release(); copyMembers(frame); retain(); } void VideoFrame::initMembers() { m_cachedCVTextureRef = 0; m_cachedCIImage = 0; m_cachedNSBitmap = 0; m_videoPlayer = 0; m_brightness = 0; m_contrast = 0; m_hue = 0; m_saturation = 0; m_opacity = 1; m_backgroundFrame = 0; } void VideoFrame::copyMembers(const VideoFrame& frame) { m_cachedCVTextureRef = frame.m_cachedCVTextureRef; m_cachedCIImage = frame.m_cachedCIImage; m_cachedQImage = frame.m_cachedQImage; m_cachedNSBitmap = frame.m_cachedNSBitmap; m_videoPlayer = frame.m_videoPlayer; m_brightness = frame.m_brightness; m_contrast = frame.m_contrast; m_hue = frame.m_hue; m_saturation = frame.m_saturation; m_opacity = frame.m_opacity; m_backgroundFrame = frame.m_backgroundFrame; } VideoFrame::~VideoFrame() { release(); } QuickTimeVideoPlayer *VideoFrame::videoPlayer() { return m_videoPlayer; } void VideoFrame::setBackgroundFrame(const VideoFrame &frame) { m_backgroundFrame = new VideoFrame(frame); } QRect VideoFrame::frameRect() const { return m_videoPlayer->videoRect(); } CVOpenGLTextureRef VideoFrame::cachedCVTexture() const { if (!m_cachedCVTextureRef && m_videoPlayer){ m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); (const_cast(this))->m_cachedCVTextureRef = m_videoPlayer->currentFrameAsCVTexture(); } return m_cachedCVTextureRef; } void *VideoFrame::cachedCIImage() const { if (!m_cachedCIImage && m_videoPlayer){ m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); (const_cast(this))->m_cachedCIImage = m_videoPlayer->currentFrameAsCIImage(); } return m_cachedCIImage; } GLuint VideoFrame::glTextureRef() const { return CVOpenGLTextureGetName(cachedCVTexture()); } void VideoFrame::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) { if (m_backgroundFrame) m_backgroundFrame->setColors(brightness, contrast, hue, saturation); if (m_brightness == brightness && m_contrast == contrast && m_hue == hue && m_saturation == saturation) return; m_brightness = brightness; m_contrast = contrast; m_hue = hue; m_saturation = saturation; invalidateImage(); } CGRect VideoFrame::QRectToCGRect(const QRect & qrect) { CGRect cgrect; cgrect.origin.x = qrect.x(); cgrect.origin.y = qrect.y() + qrect.height(); cgrect.size.width = qrect.width(); cgrect.size.height = -qrect.height(); return cgrect; } bool VideoFrame::hasColorAdjustments() { return (m_brightness || m_contrast || m_saturation || m_hue); } void VideoFrame::setBaseOpacity(qreal opacity) { m_opacity = opacity; } void VideoFrame::drawQImage(QPainter *p, const QRect &rect) const { if (!m_videoPlayer) return; #ifdef QUICKTIME_C_API_AVAILABLE if (m_cachedQImage.isNull()){ m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); (const_cast(this))->m_cachedQImage = m_videoPlayer->currentFrameAsQImage(); } #else // Since cocoa-64 doesn't give us OpenGL textures directly, the process of converting // CIImges into QImages takes time. We could still call m_videoPlayer->currentFrameAsQImage(), // but because of bitmap memory management issues, and the fact that we need to swap red and // blue, we can optimize the process a bit here since we are going to draw immidiatly: CIImage *img = (CIImage*)cachedCIImage(); if (!img) return; if (!m_cachedNSBitmap){ (const_cast(this))->m_cachedNSBitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; CGRect bounds = [img extent]; int w = bounds.size.width; int h = bounds.size.height; (const_cast(this))->m_cachedQImage = QImage([m_cachedNSBitmap bitmapData], w, h, QImage::Format_ARGB32); // Swap red and blue (same as QImage::rgbSwapped, but without copy) for (int i=0; i> 16) & 0xff) | (*p & 0xff00ff00); p++; } } } #endif p->drawImage(rect, m_cachedQImage); } void VideoFrame::drawCIImage(const QRect &rect, float opacity) const { drawCIImage(QRectToCGRect(rect), opacity); } void VideoFrame::drawCIImage(const CGRect &rect, float opacity) const { Q_UNUSED(opacity); CIImage *img = (CIImage *) cachedCIImage(); if (!img) return; CIContext* ciContext = [[NSGraphicsContext currentContext] CIContext]; [ciContext drawImage:img inRect:rect fromRect:[img extent]]; } void VideoFrame::drawCVTexture(const QRect &rect, float opacity) const { if (!m_videoPlayer) return; if (m_backgroundFrame) m_backgroundFrame->drawCVTexture(rect, opacity); CVOpenGLTextureRef texRef = cachedCVTexture(); if (!texRef) return; glPushMatrix(); glDisable(GL_CULL_FACE); GLenum target = CVOpenGLTextureGetTarget(texRef); glEnable(target); opacity *= m_opacity; if (opacity < 1){ glEnable(GL_BLEND); glColor4f(1, 1, 1, opacity); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glColor3f(1, 1, 1); glDisable(GL_BLEND); } glBindTexture(target, CVOpenGLTextureGetName(texRef)); glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; CVOpenGLTextureGetCleanTexCoords(texRef, lowerLeft, lowerRight, upperRight, upperLeft); glBegin(GL_QUADS); glTexCoord2f(lowerLeft[0], lowerLeft[1]); glVertex2i(rect.topLeft().x(), rect.topLeft().y()); glTexCoord2f(lowerRight[0], lowerRight[1]); glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); glTexCoord2f(upperRight[0], upperRight[1]); glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); glTexCoord2f(upperLeft[0], upperLeft[1]); glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); glEnd(); glPopMatrix(); } void VideoFrame::drawGLTexture(const QRect &rect, float opacity) const { if (!m_videoPlayer) return; if (m_backgroundFrame) m_backgroundFrame->drawGLTexture(rect, opacity); GLuint texture = m_videoPlayer->currentFrameAsGLTexture(); if (!texture) return; glPushMatrix(); glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_RECTANGLE_EXT); opacity *= m_opacity; if (opacity < 1){ glEnable(GL_BLEND); glColor4f(1, 1, 1, opacity); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glColor3f(1, 1, 1); glDisable(GL_BLEND); } glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture); glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); QRect videoRect = m_videoPlayer->videoRect(); GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; lowerLeft[0] = 0; lowerLeft[1] = videoRect.height(); lowerRight[0] = videoRect.width(); lowerRight[1] = videoRect.height(); upperRight[0] = videoRect.width(); upperRight[1] = 0; upperLeft[0] = 0; upperLeft[1] = 0; glBegin(GL_QUADS); glTexCoord2f(lowerLeft[0], lowerLeft[1]); glVertex2i(rect.topLeft().x(), rect.topLeft().y()); glTexCoord2f(lowerRight[0], lowerRight[1]); glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); glTexCoord2f(upperRight[0], upperRight[1]); glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); glTexCoord2f(upperLeft[0], upperLeft[1]); glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); glEnd(); glPopMatrix(); // FOR NOW. FREE THE TEXTURE: glDeleteTextures(1, &texture); } bool VideoFrame::isEmpty() { return (m_videoPlayer == 0); } void VideoFrame::invalidateImage() const { if (m_cachedCVTextureRef){ CVOpenGLTextureRelease(m_cachedCVTextureRef); (const_cast(this))->m_cachedCVTextureRef = 0; } if (m_cachedCIImage){ [(CIImage *) m_cachedCIImage release]; (const_cast(this))->m_cachedCIImage = 0; } if (m_cachedNSBitmap){ [m_cachedNSBitmap release]; (const_cast(this))->m_cachedNSBitmap = 0; } (const_cast(this))-> m_cachedQImage = QImage(); } void VideoFrame::retain() const { if (m_cachedCVTextureRef) CVOpenGLTextureRetain(m_cachedCVTextureRef); if (m_cachedCIImage) [(CIImage *) m_cachedCIImage retain]; if (m_backgroundFrame) m_backgroundFrame->retain(); if (m_cachedNSBitmap) [m_cachedNSBitmap retain]; } void VideoFrame::release() const { if (m_cachedCVTextureRef) CVOpenGLTextureRelease(m_cachedCVTextureRef); if (m_cachedCIImage) [(CIImage *) m_cachedCIImage release]; if (m_backgroundFrame) m_backgroundFrame->release(); if (m_cachedNSBitmap) [m_cachedNSBitmap release]; (const_cast(this))->m_backgroundFrame = 0; (const_cast(this))->m_cachedCVTextureRef = 0; (const_cast(this))->m_cachedCIImage = 0; (const_cast(this))->m_cachedNSBitmap = 0; } }} //namespace Phonon::QT7 QT_END_NAMESPACE