diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-19 00:49:55 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-19 00:49:55 +0000 |
commit | abcfb02cc5b7605049855bf0a1c53e445f51be4b (patch) | |
tree | 800500ca33f9451119fd01ba49897d8d99a3f8f6 /chrome/renderer | |
parent | ccd418a089fdec26c9ef7354e9fa5203e1a37662 (diff) | |
download | chromium_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.cc | 187 | ||||
-rw-r--r-- | chrome/renderer/media/video_renderer_impl.h | 23 |
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_; |