summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/chromium/VideoLayerChromium.cpp')
-rw-r--r--WebCore/platform/graphics/chromium/VideoLayerChromium.cpp399
1 files changed, 288 insertions, 111 deletions
diff --git a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp
index 0fb1bb4..26641a9 100644
--- a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp
+++ b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp
@@ -36,15 +36,134 @@
#include "GraphicsContext3D.h"
#include "LayerRendererChromium.h"
#include "RenderLayerBacking.h"
-#include "skia/ext/platform_canvas.h"
-
-#if PLATFORM(SKIA)
-#include "NativeImageSkia.h"
-#include "PlatformContextSkia.h"
-#endif
+#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)
{
@@ -52,14 +171,25 @@ PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(GraphicsLayerChromium*
}
VideoLayerChromium::VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider* provider)
- : ContentLayerChromium(owner)
-#if PLATFORM(SKIA)
- , m_canvas(0)
- , m_skiaContext(0)
-#endif
- , m_graphicsContext(0)
+ : LayerChromium(owner)
+ , m_skipsDraw(true)
+ , m_frameFormat(VideoFrameChromium::Invalid)
, m_provider(provider)
{
+ for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) {
+ m_textures[plane] = 0;
+ m_textureSizes[plane] = IntSize();
+ m_frameSizes[plane] = IntSize();
+ }
+}
+
+VideoLayerChromium::~VideoLayerChromium()
+{
+ GraphicsContext3D* context = layerRendererContext();
+ for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) {
+ if (m_textures[plane])
+ GLC(context, context->deleteTexture(m_textures[plane]));
+ }
}
void VideoLayerChromium::updateContents()
@@ -70,129 +200,176 @@ void VideoLayerChromium::updateContents()
ASSERT(drawsContent());
- IntRect dirtyRect(m_dirtyRect);
- IntSize requiredTextureSize;
-
-#if PLATFORM(SKIA)
- requiredTextureSize = m_bounds;
- IntRect boundsRect(IntPoint(0, 0), m_bounds);
-
- // If the texture needs to be reallocated, then we must redraw the entire
- // contents of the layer.
- if (requiredTextureSize != m_allocatedTextureSize)
- dirtyRect = boundsRect;
- else {
- // Clip the dirtyRect to the size of the layer to avoid drawing outside
- // the bounds of the backing texture.
- dirtyRect.intersect(boundsRect);
+ m_skipsDraw = false;
+ VideoFrameChromium* frame = m_provider->getCurrentFrame();
+ if (!frame) {
+ m_skipsDraw = true;
+ m_provider->putCurrentFrame(frame);
+ return;
}
- if (!m_canvas.get()
- || dirtyRect.width() != m_canvas->getDevice()->width()
- || dirtyRect.height() != m_canvas->getDevice()->height()) {
- m_canvas = new skia::PlatformCanvas(dirtyRect.width(), dirtyRect.height(), true);
- m_skiaContext = new PlatformContextSkia(m_canvas.get());
-
- // This is needed to get text to show up correctly.
- // FIXME: Does this take us down a very slow text rendering path?
- m_skiaContext->setDrawingToImageBuffer(true);
- m_graphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_skiaContext.get()));
+ 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;
}
- // Bring the canvas into the coordinate system of the paint rect.
- m_canvas->translate(static_cast<SkScalar>(-dirtyRect.x()), static_cast<SkScalar>(-dirtyRect.y()));
-
- // FIXME: Remove this test when tiled layers are implemented.
- m_skipsDraw = false;
- if (!layerRenderer()->checkTextureSize(requiredTextureSize)) {
+ // 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;
}
- unsigned textureId = m_contentsTexture;
- if (!textureId)
- textureId = layerRenderer()->createLayerTexture();
-
- // If the texture id or size changed since last time, then we need to tell GL
- // to re-allocate a texture.
- if (m_contentsTexture != textureId || requiredTextureSize != m_allocatedTextureSize)
- createTextureRect(requiredTextureSize, dirtyRect, textureId);
- else
- updateTextureRect(dirtyRect, textureId);
-#else
- // FIXME: Implement non-skia path
- notImplemented();
-#endif
+ // 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);
}
-void VideoLayerChromium::createTextureRect(const IntSize& requiredTextureSize, const IntRect& updateRect, unsigned textureId)
+unsigned VideoLayerChromium::determineTextureFormat(VideoFrameChromium* frame)
{
- // Paint into graphics context and get bitmap.
- m_owner->paintGraphicsLayerContents(*m_graphicsContext, updateRect);
- void* pixels = 0;
- IntSize bitmapSize = IntSize();
-#if PLATFORM(SKIA)
- const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false);
- const SkBitmap* skiaBitmap = &bitmap;
- ASSERT(skiaBitmap);
-
- SkAutoLockPixels lock(*skiaBitmap);
- SkBitmap::Config skiaConfig = skiaBitmap->config();
- // FIXME: Do we need to support more image configurations?
- if (skiaConfig == SkBitmap::kARGB_8888_Config) {
- pixels = skiaBitmap->getPixels();
- bitmapSize = IntSize(skiaBitmap->width(), skiaBitmap->height());
+ switch (frame->format()) {
+ case VideoFrameChromium::YV12:
+ return GraphicsContext3D::LUMINANCE;
+ case VideoFrameChromium::RGBA:
+ return GraphicsContext3D::RGBA;
+ default:
+ break;
}
-#else
- // FIXME: Implement non-skia path
- notImplemented();
-#endif
- if (!pixels)
- return;
+ return GraphicsContext3D::INVALID_VALUE;
+}
- GraphicsContext3D* context = layerRendererContext();
- context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId);
- ASSERT(bitmapSize == requiredTextureSize);
- context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, requiredTextureSize.width(), requiredTextureSize.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixels);
+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);
- m_contentsTexture = textureId;
- m_allocatedTextureSize = requiredTextureSize;
+ // 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;
- updateCompleted();
+ 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->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, textureFormat, dimensions.width(), dimensions.height(), 0, textureFormat, GraphicsContext3D::UNSIGNED_BYTE, 0));
}
-void VideoLayerChromium::updateTextureRect(const IntRect& updateRect, unsigned textureId)
+void VideoLayerChromium::updateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned format, const void* data)
{
-#if PLATFORM(SKIA)
- const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true);
- SkBitmap* skiaBitmap = const_cast<SkBitmap*>(&bitmap);
- ASSERT(skiaBitmap);
-
- SkAutoLockPixels lock(*skiaBitmap);
- SkBitmap::Config skiaConfig = skiaBitmap->config();
-
- if (skiaConfig == SkBitmap::kARGB_8888_Config) {
- GraphicsContext3D* context = layerRendererContext();
- context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId);
- ASSERT(context->supportsMapSubCHROMIUM());
- void* mem = context->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, GraphicsContext3D::WRITE_ONLY);
- skiaBitmap->setPixels(mem);
- m_owner->paintGraphicsLayerContents(*m_graphicsContext, updateRect);
- context->unmapTexSubImage2DCHROMIUM(mem);
+ ASSERT(context);
+ GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
+ void* mem = context->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, dimensions.width(), dimensions.height(), format, GraphicsContext3D::UNSIGNED_BYTE, GraphicsContext3D::WRITE_ONLY);
+ if (mem) {
+ memcpy(mem, data, dimensions.width() * dimensions.height());
+ GLC(context, context->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());
- updateCompleted();
-#else
- // FIXME: Implement non-skia path
- notImplemented();
-#endif
+ switch (m_frameFormat) {
+ case VideoFrameChromium::YV12:
+ drawYUV(sv);
+ break;
+ case VideoFrameChromium::RGBA:
+ drawRGBA(sv);
+ break;
+ default:
+ // FIXME: Implement other paths.
+ notImplemented();
+ break;
+ }
}
-void VideoLayerChromium::updateCompleted()
+void VideoLayerChromium::drawYUV(const SharedValues* sv)
{
- m_dirtyRect.setSize(FloatSize());
- m_contentsDirty = false;
+ 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());
}
+
+} // namespace WebCore
+
#endif // USE(ACCELERATED_COMPOSITING)