summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-19 00:49:55 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-19 00:49:55 +0000
commitabcfb02cc5b7605049855bf0a1c53e445f51be4b (patch)
tree800500ca33f9451119fd01ba49897d8d99a3f8f6 /chrome/renderer
parentccd418a089fdec26c9ef7354e9fa5203e1a37662 (diff)
downloadchromium_src-abcfb02cc5b7605049855bf0a1c53e445f51be4b.zip
chromium_src-abcfb02cc5b7605049855bf0a1c53e445f51be4b.tar.gz
chromium_src-abcfb02cc5b7605049855bf0a1c53e445f51be4b.tar.bz2
Fast paint for <video> by writing to SkCanvas directly
A direct path for rendering <video>, writes directly to bitmap in SkDevice in SkCanvas. There's several criteria for direct path: 1. No skew 2. SkDevice is ARGB8888 3. SkCanvas is opaque Review URL: http://codereview.chromium.org/115281 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16350 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/media/video_renderer_impl.cc187
-rw-r--r--chrome/renderer/media/video_renderer_impl.h23
2 files changed, 193 insertions, 17 deletions
diff --git a/chrome/renderer/media/video_renderer_impl.cc b/chrome/renderer/media/video_renderer_impl.cc
index 32ffb12..42f1f68 100644
--- a/chrome/renderer/media/video_renderer_impl.cc
+++ b/chrome/renderer/media/video_renderer_impl.cc
@@ -3,6 +3,7 @@
// LICENSE file.
#include "chrome/renderer/media/video_renderer_impl.h"
+#include "media/base/buffers.h"
#include "media/base/yuv_convert.h"
VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl* delegate)
@@ -43,6 +44,8 @@ bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) {
return false;
}
+void VideoRendererImpl::SetRect(const gfx::Rect& rect) {}
+
void VideoRendererImpl::OnFrameAvailable() {
delegate_->PostRepaintTask();
}
@@ -52,24 +55,53 @@ void VideoRendererImpl::Paint(skia::PlatformCanvas* canvas,
const gfx::Rect& dest_rect) {
scoped_refptr<media::VideoFrame> video_frame;
GetCurrentFrame(&video_frame);
- if (video_frame.get()) {
- CopyToCurrentFrame(video_frame);
+ if (video_frame) {
+ if (CanFastPaint(canvas, dest_rect)) {
+ FastPaint(video_frame, canvas, dest_rect);
+ } else {
+ SlowPaint(video_frame, canvas, dest_rect);
+ }
video_frame = NULL;
}
- SkMatrix matrix;
- matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()),
- static_cast<SkScalar>(dest_rect.y()));
- if (dest_rect.width() != video_size_.width() ||
- dest_rect.height() != video_size_.height()) {
- matrix.preScale(SkIntToScalar(dest_rect.width()) /
- SkIntToScalar(video_size_.width()),
- SkIntToScalar(dest_rect.height()) /
- SkIntToScalar(video_size_.height()));
+}
+
+// CanFastPaint is a helper method to determine the conditions for fast
+// painting. The conditions are:
+// 1. No skew in canvas matrix.
+// 2. Canvas has pixel format ARGB8888.
+// 3. Canvas is opaque.
+bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ const SkMatrix& total_matrix = canvas->getTotalMatrix();
+ if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
+ SkScalarNearlyZero(total_matrix.getSkewY())) {
+ // Get the properties of the SkDevice and the clip rect.
+ SkDevice* device = canvas->getDevice();
+
+ // Get the boundary of the device.
+ SkIRect device_rect;
+ device->getBounds(&device_rect);
+
+ // Get the pixel config of the device.
+ const SkBitmap::Config config = device->config();
+ // Get the total clip rect associated with the canvas.
+ const SkRegion& total_clip = canvas->getTotalClip();
+
+ SkIRect dest_irect;
+ TransformToSkIRect(canvas->getTotalMatrix(), dest_rect, &dest_irect);
+
+ if (config == SkBitmap::kARGB_8888_Config && device->isOpaque() &&
+ device_rect.contains(total_clip.getBounds())) {
+ return true;
+ }
}
- canvas->drawBitmapMatrix(bitmap_, matrix, NULL);
+ return false;
}
-void VideoRendererImpl::CopyToCurrentFrame(media::VideoFrame* video_frame) {
+void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ // 1. Convert YUV frame to RGB.
base::TimeDelta timestamp = video_frame->GetTimestamp();
if (video_frame != last_converted_frame_ ||
timestamp != last_converted_timestamp_) {
@@ -99,4 +131,133 @@ void VideoRendererImpl::CopyToCurrentFrame(media::VideoFrame* video_frame) {
NOTREACHED();
}
}
+
+ // 2. Paint the bitmap to canvas.
+ SkMatrix matrix;
+ matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()),
+ static_cast<SkScalar>(dest_rect.y()));
+ if (dest_rect.width() != video_size_.width() ||
+ dest_rect.height() != video_size_.height()) {
+ matrix.preScale(SkIntToScalar(dest_rect.width()) /
+ SkIntToScalar(video_size_.width()),
+ SkIntToScalar(dest_rect.height()) /
+ SkIntToScalar(video_size_.height()));
+ }
+ canvas->drawBitmapMatrix(bitmap_, matrix, NULL);
+}
+
+void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ media::VideoSurface frame_in;
+ if (video_frame->Lock(&frame_in)) {
+ // TODO(hclam): Support more video formats than just YV12.
+ DCHECK(frame_in.format == media::VideoSurface::YV12);
+ DCHECK(frame_in.strides[media::VideoSurface::kUPlane] ==
+ frame_in.strides[media::VideoSurface::kVPlane]);
+ DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes);
+ const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
+
+ // Create a rectangle backed by SkScalar.
+ SkRect scalar_dest_rect;
+ scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(),
+ dest_rect.right(), dest_rect.bottom());
+
+ // Transform the destination rectangle to local coordinates.
+ const SkMatrix& local_matrix = canvas->getTotalMatrix();
+ SkRect local_dest_rect;
+ local_matrix.mapRect(&local_dest_rect, scalar_dest_rect);
+
+ // After projecting the destination rectangle to local coordinates, round
+ // the projected rectangle to integer values, this will give us pixel values
+ // of the rectangle.
+ SkIRect local_dest_irect, local_dest_irect_saved;
+ local_dest_rect.round(&local_dest_irect);
+ local_dest_rect.round(&local_dest_irect_saved);
+
+ // Only does the paint if the destination rect intersects with the clip
+ // rect.
+ if (local_dest_irect.intersect(canvas->getTotalClip().getBounds())) {
+ // At this point |local_dest_irect| contains the rect that we should draw
+ // to within the clipping rect.
+
+ // Calculate the address for the top left corner of destination rect in
+ // the canvas that we will draw to. The address is obtained by the base
+ // address of the canvas shifted by "left" and "top" of the rect.
+ uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
+ local_dest_irect.fTop * bitmap.rowBytes() +
+ local_dest_irect.fLeft * 4;
+
+ // Project the clip rect to the original video frame, obtains the
+ // dimensions of the projected clip rect, "left" and "top" of the rect.
+ // The math here are all integer math so we won't have rounding error and
+ // write outside of the canvas.
+ // We have the assumptions of dest_rect.width() and dest_rect.height()
+ // being non-zero, these are valid assumptions since finding intersection
+ // above rejects empty rectangle so we just do a DCHECK here.
+ DCHECK_NE(0, dest_rect.width());
+ DCHECK_NE(0, dest_rect.height());
+ size_t frame_clip_width = local_dest_irect.width() *
+ frame_in.width / dest_rect.width();
+ size_t frame_clip_height = local_dest_irect.height() *
+ frame_in.height / dest_rect.height();
+
+ // Project the "left" and "top" of the final destination rect to local
+ // coordinates of the video frame, use these values to find the offsets
+ // in the video frame to start reading.
+ size_t frame_clip_left = (local_dest_irect.fLeft -
+ local_dest_irect_saved.fLeft) *
+ frame_in.width / dest_rect.width();
+ size_t frame_clip_top = (local_dest_irect.fTop -
+ local_dest_irect_saved.fTop) *
+ frame_in.height / dest_rect.height();
+
+ // Use the "left" and "top" of the destination rect to locate the offset
+ // in Y, U and V planes.
+ size_t y_offset = frame_in.strides[media::VideoSurface::kYPlane] *
+ frame_clip_top + frame_clip_left;
+ // Since the format is YV12, there is one U, V value per 2x2 block, thus
+ // the math here.
+ // TODO(hclam): handle formats other than YV12.
+ size_t uv_offset = (frame_in.strides[media::VideoSurface::kUPlane] *
+ frame_clip_top + frame_clip_left) / 2;
+ uint8* frame_clip_y = frame_in.data[media::VideoSurface::kYPlane] +
+ y_offset;
+ uint8* frame_clip_u = frame_in.data[media::VideoSurface::kUPlane] +
+ uv_offset;
+ uint8* frame_clip_v = frame_in.data[media::VideoSurface::kVPlane] +
+ uv_offset;
+ bitmap.lockPixels();
+ // TODO(hclam): do rotation and mirroring here.
+ media::ScaleYUVToRGB32(frame_clip_y,
+ frame_clip_u,
+ frame_clip_v,
+ dest_rect_pointer,
+ frame_clip_width,
+ frame_clip_height,
+ local_dest_irect.width(),
+ local_dest_irect.height(),
+ frame_in.strides[media::VideoSurface::kYPlane],
+ frame_in.strides[media::VideoSurface::kUPlane],
+ bitmap.rowBytes(),
+ media::YV12,
+ media::ROTATE_0);
+ bitmap.unlockPixels();
+ }
+ video_frame->Unlock();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void VideoRendererImpl::TransformToSkIRect(const SkMatrix& matrix,
+ const gfx::Rect& src_rect,
+ SkIRect* dest_rect) {
+ // Transform destination rect to local coordinates.
+ SkRect transformed_rect;
+ SkRect skia_dest_rect;
+ skia_dest_rect.iset(src_rect.x(), src_rect.y(),
+ src_rect.right(), src_rect.bottom());
+ matrix.mapRect(&transformed_rect, skia_dest_rect);
+ transformed_rect.round(dest_rect);
}
diff --git a/chrome/renderer/media/video_renderer_impl.h b/chrome/renderer/media/video_renderer_impl.h
index 2be93a0..bf91282 100644
--- a/chrome/renderer/media/video_renderer_impl.h
+++ b/chrome/renderer/media/video_renderer_impl.h
@@ -32,7 +32,7 @@ class VideoRendererImpl : public media::VideoThread {
// be used by future implementations to implement an improved color space +
// scale code on a separate thread. Since we always do the stretch on the
// same thread as the Paint method, we just ignore the call for now.
- virtual void SetRect(const gfx::Rect& rect) {}
+ virtual void SetRect(const gfx::Rect& rect);
// Paint the current front frame on the |canvas| stretching it to fit the
// |dest_rect|
@@ -64,9 +64,24 @@ class VideoRendererImpl : public media::VideoThread {
explicit VideoRendererImpl(WebMediaPlayerImpl* delegate);
virtual ~VideoRendererImpl() {}
- // Internal method used by the Paint method to convert the specified video
- // frame to RGB, placing the converted pixels in the |current_frame_| bitmap.
- void CopyToCurrentFrame(media::VideoFrame* video_frame);
+ // Determine the conditions to perform fast paint. Returns true if we can do
+ // fast paint otherwise false.
+ bool CanFastPaint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+
+ // Slow paint does a YUV => RGB, and scaled blit in two separate operations.
+ void SlowPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect);
+
+ // Fast paint does YUV => RGB, scaling, blitting all in one step into the
+ // canvas. It's not always safe and appropriate to perform fast paint.
+ // CanFastPaint() is used to determine the conditions.
+ void FastPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect);
+
+ void TransformToSkIRect(const SkMatrix& matrix, const gfx::Rect& src_rect,
+ SkIRect* dest_rect);
// Pointer to our parent object that is called to request repaints.
WebMediaPlayerImpl* delegate_;