diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp new file mode 100644 index 0000000..81264b3 --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "VideoLayerChromium.h" + +#include "Extensions3DChromium.h" +#include "GraphicsContext3D.h" +#include "LayerRendererChromium.h" +#include "NotImplemented.h" +#include "RenderLayerBacking.h" +#include "VideoFrameChromium.h" +#include "VideoFrameProvider.h" + +namespace WebCore { + +// These values are magic numbers that are used in the transformation +// from YUV to RGB color values. +const float VideoLayerChromium::yuv2RGB[9] = { + 1.f, 1.f, 1.f, + 0.f, -.344f, 1.772f, + 1.403f, -.714f, 0.f, +}; + +VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) + : m_context(context) + , m_yuvShaderProgram(0) + , m_rgbaShaderProgram(0) + , m_yuvShaderMatrixLocation(0) + , m_yuvWidthScaleFactorLocation(0) + , m_rgbaShaderMatrixLocation(0) + , m_rgbaWidthScaleFactorLocation(0) + , m_ccMatrixLocation(0) + , m_yTextureLocation(0) + , m_uTextureLocation(0) + , m_vTextureLocation(0) + , m_rgbaTextureLocation(0) + , m_yuvAlphaLocation(0) + , m_rgbaAlphaLocation(0) + , m_initialized(false) +{ + // Frame textures are allocated based on stride width, not visible frame + // width, such that there is a guarantee that the frame rows line up + // properly and are not shifted by (stride - width) pixels. To hide the + // "padding" pixels between the edge of the visible frame width and the end + // of the stride, we give the shader a widthScaleFactor (<=1.0) of how much + // of the width of the texture should be shown when drawing the texture onto + // the vertices. + char vertexShaderString[] = + "precision mediump float; \n" + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform mat4 matrix; \n" + "varying vec2 v_texCoord; \n" + "uniform float widthScaleFactor; \n" + "void main() \n" + "{ \n" + " gl_Position = matrix * a_position; \n" + " v_texCoord = vec2(widthScaleFactor * a_texCoord.x, a_texCoord.y); \n" + "} \n"; + + char yuvFragmentShaderString[] = + "precision mediump float; \n" + "precision mediump int; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D y_texture; \n" + "uniform sampler2D u_texture; \n" + "uniform sampler2D v_texture; \n" + "uniform float alpha; \n" + "uniform mat3 cc_matrix; \n" + "void main() \n" + "{ \n" + " float y = texture2D(y_texture, v_texCoord).x; \n" + " float u = texture2D(u_texture, v_texCoord).r - .5; \n" + " float v = texture2D(v_texture, v_texCoord).r - .5; \n" + " vec3 rgb = cc_matrix * vec3(y, u, v); \n" + " gl_FragColor = vec4(rgb.x, rgb.y, rgb.z, 1.0) * alpha; \n" + "} \n"; + + char rgbaFragmentShaderString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D rgba_texture; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(rgba_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y)); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" + "} \n"; + + m_rgbaShaderProgram = createShaderProgram(m_context, vertexShaderString, rgbaFragmentShaderString); + if (!m_rgbaShaderProgram) { + LOG_ERROR("VideoLayerChromium: Failed to create rgba shader program"); + return; + } + + m_yuvShaderProgram = createShaderProgram(m_context, vertexShaderString, yuvFragmentShaderString); + if (!m_yuvShaderProgram) { + LOG_ERROR("VideoLayerChromium: Failed to create yuv shader program"); + return; + } + + m_yuvShaderMatrixLocation = m_context->getUniformLocation(m_yuvShaderProgram, "matrix"); + m_yuvWidthScaleFactorLocation = m_context->getUniformLocation(m_yuvShaderProgram, "widthScaleFactor"); + m_yTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "y_texture"); + m_uTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "u_texture"); + m_vTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "v_texture"); + m_ccMatrixLocation = m_context->getUniformLocation(m_yuvShaderProgram, "cc_matrix"); + m_yuvAlphaLocation = m_context->getUniformLocation(m_yuvShaderProgram, "alpha"); + + ASSERT(m_yuvShaderMatrixLocation != -1); + ASSERT(m_yuvWidthScaleFactorLocation != -1); + ASSERT(m_yTextureLocation != -1); + ASSERT(m_uTextureLocation != -1); + ASSERT(m_vTextureLocation != -1); + ASSERT(m_ccMatrixLocation != -1); + ASSERT(m_yuvAlphaLocation != -1); + + m_rgbaShaderMatrixLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "matrix"); + m_rgbaTextureLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "rgba_texture"); + m_rgbaWidthScaleFactorLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "widthScaleFactor"); + m_rgbaAlphaLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "alpha"); + + ASSERT(m_rgbaShaderMatrixLocation != -1); + ASSERT(m_rgbaTextureLocation != -1); + ASSERT(m_rgbaWidthScaleFactorLocation != -1); + ASSERT(m_rgbaAlphaLocation != -1); + + m_initialized = true; +} + +VideoLayerChromium::SharedValues::~SharedValues() +{ + if (m_yuvShaderProgram) + GLC(m_context, m_context->deleteProgram(m_yuvShaderProgram)); + if (m_rgbaShaderProgram) + GLC(m_context, m_context->deleteProgram(m_rgbaShaderProgram)); +} + +PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(GraphicsLayerChromium* owner, + VideoFrameProvider* provider) +{ + return adoptRef(new VideoLayerChromium(owner, provider)); +} + +VideoLayerChromium::VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider* provider) + : LayerChromium(owner) + , m_skipsDraw(true) + , m_frameFormat(VideoFrameChromium::Invalid) + , m_provider(provider) + , m_currentFrame(0) +{ + resetFrameParameters(); +} + +VideoLayerChromium::~VideoLayerChromium() +{ + cleanupResources(); +} + +void VideoLayerChromium::cleanupResources() +{ + LayerChromium::cleanupResources(); + releaseCurrentFrame(); + if (!layerRenderer()) + return; + + GraphicsContext3D* context = layerRendererContext(); + for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { + if (m_textures[plane]) + GLC(context, context->deleteTexture(m_textures[plane])); + } +} + +void VideoLayerChromium::updateContentsIfDirty() +{ + if (!m_contentsDirty) + return; + + RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); + if (!backing || backing->paintingGoesToWindow()) + return; + + ASSERT(drawsContent()); + + m_skipsDraw = false; + VideoFrameChromium* frame = m_provider->getCurrentFrame(); + if (!frame) { + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + m_frameFormat = frame->format(); + unsigned textureFormat = determineTextureFormat(frame); + if (textureFormat == GraphicsContext3D::INVALID_VALUE) { + // FIXME: Implement other paths. + notImplemented(); + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + if (frame->surfaceType() == VideoFrameChromium::TypeTexture) { + releaseCurrentFrame(); + saveCurrentFrame(frame); + m_dirtyRect.setSize(FloatSize()); + m_contentsDirty = false; + return; + } + + // Allocate textures for planes if they are not allocated already, or + // reallocate textures that are the wrong size for the frame. + GraphicsContext3D* context = layerRendererContext(); + bool texturesAllocated = allocateTexturesIfNeeded(context, frame, textureFormat); + if (!texturesAllocated) { + m_skipsDraw = true; + m_provider->putCurrentFrame(frame); + return; + } + + // Update texture planes. + for (unsigned plane = 0; plane < frame->planes(); plane++) { + ASSERT(frame->requiredTextureSize(plane) == m_textureSizes[plane]); + updateTexture(context, m_textures[plane], frame->requiredTextureSize(plane), textureFormat, frame->data(plane)); + } + + m_dirtyRect.setSize(FloatSize()); + m_contentsDirty = false; + + m_provider->putCurrentFrame(frame); +} + +unsigned VideoLayerChromium::determineTextureFormat(VideoFrameChromium* frame) +{ + switch (frame->format()) { + case VideoFrameChromium::YV12: + return GraphicsContext3D::LUMINANCE; + case VideoFrameChromium::RGBA: + return GraphicsContext3D::RGBA; + default: + break; + } + return GraphicsContext3D::INVALID_VALUE; +} + +bool VideoLayerChromium::allocateTexturesIfNeeded(GraphicsContext3D* context, VideoFrameChromium* frame, unsigned textureFormat) +{ + ASSERT(context); + ASSERT(frame); + + for (unsigned plane = 0; plane < frame->planes(); plane++) { + IntSize planeTextureSize = frame->requiredTextureSize(plane); + + // If the renderer cannot handle this large of a texture, return false. + // FIXME: Remove this test when tiled layers are implemented. + if (!layerRenderer()->checkTextureSize(planeTextureSize)) + return false; + + if (!m_textures[plane]) + m_textures[plane] = layerRenderer()->createLayerTexture(); + + if (!planeTextureSize.isZero() && planeTextureSize != m_textureSizes[plane]) { + allocateTexture(context, m_textures[plane], planeTextureSize, textureFormat); + m_textureSizes[plane] = planeTextureSize; + m_frameSizes[plane] = IntSize(frame->width(), frame->height()); + } + } + return true; +} + +void VideoLayerChromium::allocateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned textureFormat) +{ + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + GLC(context, context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, textureFormat, dimensions.width(), dimensions.height(), 0, textureFormat, GraphicsContext3D::UNSIGNED_BYTE)); +} + +void VideoLayerChromium::updateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned format, const void* data) +{ + ASSERT(context); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId)); + void* mem = static_cast<Extensions3DChromium*>(context->getExtensions())->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, dimensions.width(), dimensions.height(), format, GraphicsContext3D::UNSIGNED_BYTE, Extensions3DChromium::WRITE_ONLY); + if (mem) { + memcpy(mem, data, dimensions.width() * dimensions.height()); + GLC(context, static_cast<Extensions3DChromium*>(context->getExtensions())->unmapTexSubImage2DCHROMIUM(mem)); + } else { + // FIXME: We should have some sort of code to handle the case when + // mapTexSubImage2D fails. + m_skipsDraw = true; + } +} + +void VideoLayerChromium::draw() +{ + if (m_skipsDraw) + return; + + ASSERT(layerRenderer()); + const VideoLayerChromium::SharedValues* sv = layerRenderer()->videoLayerSharedValues(); + ASSERT(sv && sv->initialized()); + + switch (m_frameFormat) { + case VideoFrameChromium::YV12: + drawYUV(sv); + break; + case VideoFrameChromium::RGBA: + drawRGBA(sv); + break; + default: + // FIXME: Implement other paths. + notImplemented(); + break; + } + releaseCurrentFrame(); +} + +void VideoLayerChromium::releaseCurrentFrame() +{ + if (!m_currentFrame) + return; + + m_provider->putCurrentFrame(m_currentFrame); + m_currentFrame = 0; + resetFrameParameters(); +} + +void VideoLayerChromium::drawYUV(const SharedValues* sv) +{ + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE1)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::yPlane])); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE2)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::uPlane])); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE3)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::vPlane])); + + layerRenderer()->useShader(sv->yuvShaderProgram()); + unsigned frameWidth = m_frameSizes[VideoFrameChromium::yPlane].width(); + unsigned textureWidth = m_textureSizes[VideoFrameChromium::yPlane].width(); + float widthScaleFactor = static_cast<float>(frameWidth) / textureWidth; + GLC(context, context->uniform1f(sv->yuvWidthScaleFactorLocation(), widthScaleFactor)); + + GLC(context, context->uniform1i(sv->yTextureLocation(), 1)); + GLC(context, context->uniform1i(sv->uTextureLocation(), 2)); + GLC(context, context->uniform1i(sv->vTextureLocation(), 3)); + + GLC(context, context->uniformMatrix3fv(sv->ccMatrixLocation(), 0, const_cast<float*>(yuv2RGB), 1)); + + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->yuvShaderMatrixLocation(), sv->yuvAlphaLocation()); + + // Reset active texture back to texture 0. + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); +} + +void VideoLayerChromium::drawRGBA(const SharedValues* sv) +{ + GraphicsContext3D* context = layerRendererContext(); + GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); + GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textures[VideoFrameChromium::rgbPlane])); + + layerRenderer()->useShader(sv->rgbaShaderProgram()); + unsigned frameWidth = m_frameSizes[VideoFrameChromium::rgbPlane].width(); + unsigned textureWidth = m_textureSizes[VideoFrameChromium::rgbPlane].width(); + float widthScaleFactor = static_cast<float>(frameWidth) / textureWidth; + GLC(context, context->uniform1f(sv->rgbaWidthScaleFactorLocation(), widthScaleFactor)); + + GLC(context, context->uniform1i(sv->rgbaTextureLocation(), 0)); + + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), + bounds().width(), bounds().height(), drawOpacity(), + sv->rgbaShaderMatrixLocation(), sv->rgbaAlphaLocation()); +} + +void VideoLayerChromium::resetFrameParameters() +{ + for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { + m_textures[plane] = 0; + m_textureSizes[plane] = IntSize(); + m_frameSizes[plane] = IntSize(); + } +} + +void VideoLayerChromium::saveCurrentFrame(VideoFrameChromium* frame) +{ + ASSERT(!m_currentFrame); + m_currentFrame = frame; + for (unsigned plane = 0; plane < frame->planes(); plane++) { + m_textures[plane] = frame->texture(plane); + m_textureSizes[plane] = frame->requiredTextureSize(plane); + m_frameSizes[plane] = m_textureSizes[plane]; + } +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) |