Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: webkit/media/skcanvas_video_renderer.cc

Issue 11234071: Moved SkCanvasVideoRenderer to media/filters. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added MEDIA_EXPORT. Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "webkit/media/skcanvas_video_renderer.h"
6
7 #include "base/logging.h"
8 #include "media/base/video_frame.h"
9 #include "media/base/yuv_convert.h"
10 #include "third_party/skia/include/core/SkCanvas.h"
11 #include "third_party/skia/include/core/SkDevice.h"
12
13 namespace webkit_media {
14
15 static bool IsEitherYV12OrYV16(media::VideoFrame::Format format) {
16 return format == media::VideoFrame::YV12 || format == media::VideoFrame::YV16;
17 }
18
19 static bool IsEitherYV12OrYV16OrNative(media::VideoFrame::Format format) {
20 return IsEitherYV12OrYV16(format) ||
21 format == media::VideoFrame::NATIVE_TEXTURE;
22 }
23
24 // CanFastPaint is a helper method to determine the conditions for fast
25 // painting. The conditions are:
26 // 1. No skew in canvas matrix.
27 // 2. No flipping nor mirroring.
28 // 3. Canvas has pixel format ARGB8888.
29 // 4. Canvas is opaque.
30 // 5. Frame format is YV12 or YV16.
31 //
32 // TODO(hclam): The fast paint method should support flipping and mirroring.
33 // Disable the flipping and mirroring checks once we have it.
34 static bool CanFastPaint(SkCanvas* canvas, const gfx::Rect& dest_rect,
35 uint8_t alpha, media::VideoFrame::Format format) {
36 if (alpha != 0xFF || !IsEitherYV12OrYV16(format))
37 return false;
38
39 const SkMatrix& total_matrix = canvas->getTotalMatrix();
40 // Perform the following checks here:
41 // 1. Check for skewing factors of the transformation matrix. They should be
42 // zero.
43 // 2. Check for mirroring and flipping. Make sure they are greater than zero.
44 if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
45 SkScalarNearlyZero(total_matrix.getSkewY()) &&
46 total_matrix.getScaleX() > 0 &&
47 total_matrix.getScaleY() > 0) {
48 SkDevice* device = canvas->getDevice();
49 const SkBitmap::Config config = device->config();
50
51 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
52 return true;
53 }
54 }
55
56 return false;
57 }
58
59 // Fast paint does YUV => RGB, scaling, blitting all in one step into the
60 // canvas. It's not always safe and appropriate to perform fast paint.
61 // CanFastPaint() is used to determine the conditions.
62 static void FastPaint(
63 const scoped_refptr<media::VideoFrame>& video_frame,
64 SkCanvas* canvas,
65 const gfx::Rect& dest_rect) {
66 DCHECK(IsEitherYV12OrYV16(video_frame->format())) << video_frame->format();
67 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
68 video_frame->stride(media::VideoFrame::kVPlane));
69
70 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
71 media::YUVType yuv_type = (video_frame->format() == media::VideoFrame::YV12) ?
72 media::YV12 : media::YV16;
73 int y_shift = yuv_type; // 1 for YV12, 0 for YV16.
74
75 // Create a rectangle backed by SkScalar.
76 SkRect scalar_dest_rect;
77 scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(),
78 dest_rect.right(), dest_rect.bottom());
79
80 // Transform the destination rectangle to local coordinates.
81 const SkMatrix& local_matrix = canvas->getTotalMatrix();
82 SkRect local_dest_rect;
83 local_matrix.mapRect(&local_dest_rect, scalar_dest_rect);
84
85 // After projecting the destination rectangle to local coordinates, round
86 // the projected rectangle to integer values, this will give us pixel values
87 // of the rectangle.
88 SkIRect local_dest_irect, local_dest_irect_saved;
89 local_dest_rect.round(&local_dest_irect);
90 local_dest_rect.round(&local_dest_irect_saved);
91
92 // No point painting if the destination rect doesn't intersect with the
93 // clip rect.
94 if (!local_dest_irect.intersect(canvas->getTotalClip().getBounds()))
95 return;
96
97 // At this point |local_dest_irect| contains the rect that we should draw
98 // to within the clipping rect.
99
100 // Calculate the address for the top left corner of destination rect in
101 // the canvas that we will draw to. The address is obtained by the base
102 // address of the canvas shifted by "left" and "top" of the rect.
103 uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
104 local_dest_irect.fTop * bitmap.rowBytes() +
105 local_dest_irect.fLeft * 4;
106
107 // Project the clip rect to the original video frame, obtains the
108 // dimensions of the projected clip rect, "left" and "top" of the rect.
109 // The math here are all integer math so we won't have rounding error and
110 // write outside of the canvas.
111 // We have the assumptions of dest_rect.width() and dest_rect.height()
112 // being non-zero, these are valid assumptions since finding intersection
113 // above rejects empty rectangle so we just do a DCHECK here.
114 DCHECK_NE(0, dest_rect.width());
115 DCHECK_NE(0, dest_rect.height());
116 size_t frame_clip_width = local_dest_irect.width() *
117 video_frame->data_size().width() / local_dest_irect_saved.width();
118 size_t frame_clip_height = local_dest_irect.height() *
119 video_frame->data_size().height() / local_dest_irect_saved.height();
120
121 // Project the "left" and "top" of the final destination rect to local
122 // coordinates of the video frame, use these values to find the offsets
123 // in the video frame to start reading.
124 size_t frame_clip_left =
125 (local_dest_irect.fLeft - local_dest_irect_saved.fLeft) *
126 video_frame->data_size().width() / local_dest_irect_saved.width();
127 size_t frame_clip_top =
128 (local_dest_irect.fTop - local_dest_irect_saved.fTop) *
129 video_frame->data_size().height() / local_dest_irect_saved.height();
130
131 // Use the "left" and "top" of the destination rect to locate the offset
132 // in Y, U and V planes.
133 size_t y_offset = video_frame->stride(media::VideoFrame::kYPlane) *
134 frame_clip_top + frame_clip_left;
135
136 // For format YV12, there is one U, V value per 2x2 block.
137 // For format YV16, there is one u, V value per 2x1 block.
138 size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
139 (frame_clip_top >> y_shift)) + (frame_clip_left >> 1);
140 uint8* frame_clip_y =
141 video_frame->data(media::VideoFrame::kYPlane) + y_offset;
142 uint8* frame_clip_u =
143 video_frame->data(media::VideoFrame::kUPlane) + uv_offset;
144 uint8* frame_clip_v =
145 video_frame->data(media::VideoFrame::kVPlane) + uv_offset;
146
147 // TODO(hclam): do rotation and mirroring here.
148 // TODO(fbarchard): switch filtering based on performance.
149 bitmap.lockPixels();
150 media::ScaleYUVToRGB32(frame_clip_y,
151 frame_clip_u,
152 frame_clip_v,
153 dest_rect_pointer,
154 frame_clip_width,
155 frame_clip_height,
156 local_dest_irect.width(),
157 local_dest_irect.height(),
158 video_frame->stride(media::VideoFrame::kYPlane),
159 video_frame->stride(media::VideoFrame::kUPlane),
160 bitmap.rowBytes(),
161 yuv_type,
162 media::ROTATE_0,
163 media::FILTER_BILINEAR);
164 bitmap.unlockPixels();
165 }
166
167 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
168 //
169 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
170 static void ConvertVideoFrameToBitmap(
171 const scoped_refptr<media::VideoFrame>& video_frame,
172 SkBitmap* bitmap) {
173 DCHECK(IsEitherYV12OrYV16OrNative(video_frame->format()))
174 << video_frame->format();
175 if (IsEitherYV12OrYV16(video_frame->format())) {
176 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
177 video_frame->stride(media::VideoFrame::kVPlane));
178 }
179
180 // Check if |bitmap| needs to be (re)allocated.
181 if (bitmap->isNull() ||
182 bitmap->width() != video_frame->data_size().width() ||
183 bitmap->height() != video_frame->data_size().height()) {
184 bitmap->setConfig(SkBitmap::kARGB_8888_Config,
185 video_frame->data_size().width(),
186 video_frame->data_size().height());
187 bitmap->allocPixels();
188 bitmap->setIsVolatile(true);
189 }
190
191 bitmap->lockPixels();
192 if (IsEitherYV12OrYV16(video_frame->format())) {
193 media::YUVType yuv_type =
194 (video_frame->format() == media::VideoFrame::YV12) ?
195 media::YV12 : media::YV16;
196 media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane),
197 video_frame->data(media::VideoFrame::kUPlane),
198 video_frame->data(media::VideoFrame::kVPlane),
199 static_cast<uint8*>(bitmap->getPixels()),
200 video_frame->data_size().width(),
201 video_frame->data_size().height(),
202 video_frame->stride(media::VideoFrame::kYPlane),
203 video_frame->stride(media::VideoFrame::kUPlane),
204 bitmap->rowBytes(),
205 yuv_type);
206 } else {
207 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
208 video_frame->ReadPixelsFromNativeTexture(bitmap->getPixels());
209 }
210 bitmap->notifyPixelsChanged();
211 bitmap->unlockPixels();
212 }
213
214 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
215 : last_frame_timestamp_(media::kNoTimestamp()) {
216 }
217
218 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
219
220 void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
221 SkCanvas* canvas,
222 const gfx::Rect& dest_rect,
223 uint8_t alpha) {
224 if (alpha == 0) {
225 return;
226 }
227
228 SkRect dest;
229 dest.set(SkIntToScalar(dest_rect.x()), SkIntToScalar(dest_rect.y()),
230 SkIntToScalar(dest_rect.right()), SkIntToScalar(dest_rect.bottom()));
231
232 SkPaint paint;
233 paint.setAlpha(alpha);
234
235 // Paint black rectangle if there isn't a frame available or the
236 // frame has an unexpected format.
237 if (!video_frame || !IsEitherYV12OrYV16OrNative(video_frame->format())) {
238 canvas->drawRect(dest, paint);
239 return;
240 }
241
242 // Scale and convert to RGB in one step if we can.
243 if (CanFastPaint(canvas, dest_rect, alpha, video_frame->format())) {
244 FastPaint(video_frame, canvas, dest_rect);
245 return;
246 }
247
248 // Check if we should convert and update |last_frame_|.
249 if (last_frame_.isNull() ||
250 video_frame->GetTimestamp() != last_frame_timestamp_) {
251 ConvertVideoFrameToBitmap(video_frame, &last_frame_);
252 last_frame_timestamp_ = video_frame->GetTimestamp();
253 }
254
255 // Do a slower paint using |last_frame_|.
256 paint.setFilterBitmap(true);
257 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
258 }
259
260 } // namespace webkit_media
OLDNEW
« no previous file with comments | « webkit/media/skcanvas_video_renderer.h ('k') | webkit/media/skcanvas_video_renderer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698