Disable view source for Developer Tools.
[chromium-blink-merge.git] / media / filters / skcanvas_video_renderer.cc
blobec3e92fe754be1cb895f0f8f64a3ff289ef6dd94
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.
5 #include "media/filters/skcanvas_video_renderer.h"
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"
13 namespace media {
15 static bool IsEitherYV12OrYV16(media::VideoFrame::Format format) {
16 return format == media::VideoFrame::YV12 ||
17 format == media::VideoFrame::YV16 ||
18 format == media::VideoFrame::YV12J;
21 static bool IsEitherYV12OrYV16OrNative(media::VideoFrame::Format format) {
22 return IsEitherYV12OrYV16(format) ||
23 format == media::VideoFrame::NATIVE_TEXTURE;
26 static bool IsEitherYV12OrYV12AOrYV16(media::VideoFrame::Format format) {
27 return IsEitherYV12OrYV16(format) ||
28 format == media::VideoFrame::YV12A;
31 static bool IsEitherYV12OrYV12AOrYV16OrNative(
32 media::VideoFrame::Format format) {
33 return IsEitherYV12OrYV16OrNative(format) ||
34 format == media::VideoFrame::YV12A;
37 // CanFastPaint is a helper method to determine the conditions for fast
38 // painting. The conditions are:
39 // 1. No skew in canvas matrix.
40 // 2. No flipping nor mirroring.
41 // 3. Canvas has pixel format ARGB8888.
42 // 4. Canvas is opaque.
43 // 5. Frame format is YV12 or YV16.
45 // TODO(hclam): The fast paint method should support flipping and mirroring.
46 // Disable the flipping and mirroring checks once we have it.
47 static bool CanFastPaint(SkCanvas* canvas, uint8 alpha,
48 media::VideoFrame::Format format) {
49 if (alpha != 0xFF || !IsEitherYV12OrYV16(format))
50 return false;
52 const SkMatrix& total_matrix = canvas->getTotalMatrix();
53 // Perform the following checks here:
54 // 1. Check for skewing factors of the transformation matrix. They should be
55 // zero.
56 // 2. Check for mirroring and flipping. Make sure they are greater than zero.
57 if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
58 SkScalarNearlyZero(total_matrix.getSkewY()) &&
59 total_matrix.getScaleX() > 0 &&
60 total_matrix.getScaleY() > 0) {
61 SkBaseDevice* device = canvas->getDevice();
62 const SkBitmap::Config config = device->config();
64 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
65 return true;
69 return false;
72 // Fast paint does YUV => RGB, scaling, blitting all in one step into the
73 // canvas. It's not always safe and appropriate to perform fast paint.
74 // CanFastPaint() is used to determine the conditions.
75 static void FastPaint(
76 const scoped_refptr<media::VideoFrame>& video_frame,
77 SkCanvas* canvas,
78 const SkRect& dest_rect) {
79 DCHECK(IsEitherYV12OrYV16(video_frame->format())) << video_frame->format();
80 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
81 video_frame->stride(media::VideoFrame::kVPlane));
83 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
84 media::YUVType yuv_type = media::YV16;
85 int y_shift = 0;
86 if (video_frame->format() == media::VideoFrame::YV12 ||
87 video_frame->format() == media::VideoFrame::YV12A) {
88 yuv_type = media::YV12;
89 y_shift = 1;
92 if (video_frame->format() == media::VideoFrame::YV12J) {
93 yuv_type = media::YV12;
94 y_shift = 1;
97 // Transform the destination rectangle to local coordinates.
98 const SkMatrix& local_matrix = canvas->getTotalMatrix();
99 SkRect local_dest_rect;
100 local_matrix.mapRect(&local_dest_rect, dest_rect);
102 // After projecting the destination rectangle to local coordinates, round
103 // the projected rectangle to integer values, this will give us pixel values
104 // of the rectangle.
105 SkIRect local_dest_irect, local_dest_irect_saved;
106 local_dest_rect.round(&local_dest_irect);
107 local_dest_rect.round(&local_dest_irect_saved);
109 // No point painting if the destination rect doesn't intersect with the
110 // clip rect.
111 if (!local_dest_irect.intersect(canvas->getTotalClip().getBounds()))
112 return;
114 // At this point |local_dest_irect| contains the rect that we should draw
115 // to within the clipping rect.
117 // Calculate the address for the top left corner of destination rect in
118 // the canvas that we will draw to. The address is obtained by the base
119 // address of the canvas shifted by "left" and "top" of the rect.
120 uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
121 local_dest_irect.fTop * bitmap.rowBytes() +
122 local_dest_irect.fLeft * 4;
124 // Project the clip rect to the original video frame, obtains the
125 // dimensions of the projected clip rect, "left" and "top" of the rect.
126 // The math here are all integer math so we won't have rounding error and
127 // write outside of the canvas.
128 // We have the assumptions of dest_rect.width() and dest_rect.height()
129 // being non-zero, these are valid assumptions since finding intersection
130 // above rejects empty rectangle so we just do a DCHECK here.
131 DCHECK_NE(0, dest_rect.width());
132 DCHECK_NE(0, dest_rect.height());
133 size_t frame_clip_width = local_dest_irect.width() *
134 video_frame->visible_rect().width() / local_dest_irect_saved.width();
135 size_t frame_clip_height = local_dest_irect.height() *
136 video_frame->visible_rect().height() / local_dest_irect_saved.height();
138 // Project the "left" and "top" of the final destination rect to local
139 // coordinates of the video frame, use these values to find the offsets
140 // in the video frame to start reading.
141 size_t frame_clip_left =
142 video_frame->visible_rect().x() +
143 (local_dest_irect.fLeft - local_dest_irect_saved.fLeft) *
144 video_frame->visible_rect().width() / local_dest_irect_saved.width();
145 size_t frame_clip_top =
146 video_frame->visible_rect().y() +
147 (local_dest_irect.fTop - local_dest_irect_saved.fTop) *
148 video_frame->visible_rect().height() / local_dest_irect_saved.height();
150 // Use the "left" and "top" of the destination rect to locate the offset
151 // in Y, U and V planes.
152 size_t y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
153 frame_clip_top) + frame_clip_left;
155 // For format YV12, there is one U, V value per 2x2 block.
156 // For format YV16, there is one U, V value per 2x1 block.
157 size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
158 (frame_clip_top >> y_shift)) + (frame_clip_left >> 1);
159 uint8* frame_clip_y =
160 video_frame->data(media::VideoFrame::kYPlane) + y_offset;
161 uint8* frame_clip_u =
162 video_frame->data(media::VideoFrame::kUPlane) + uv_offset;
163 uint8* frame_clip_v =
164 video_frame->data(media::VideoFrame::kVPlane) + uv_offset;
166 // TODO(hclam): do rotation and mirroring here.
167 // TODO(fbarchard): switch filtering based on performance.
168 bitmap.lockPixels();
169 media::ScaleYUVToRGB32(frame_clip_y,
170 frame_clip_u,
171 frame_clip_v,
172 dest_rect_pointer,
173 frame_clip_width,
174 frame_clip_height,
175 local_dest_irect.width(),
176 local_dest_irect.height(),
177 video_frame->stride(media::VideoFrame::kYPlane),
178 video_frame->stride(media::VideoFrame::kUPlane),
179 bitmap.rowBytes(),
180 yuv_type,
181 media::ROTATE_0,
182 media::FILTER_BILINEAR);
183 bitmap.unlockPixels();
186 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
188 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
189 static void ConvertVideoFrameToBitmap(
190 const scoped_refptr<media::VideoFrame>& video_frame,
191 SkBitmap* bitmap) {
192 DCHECK(IsEitherYV12OrYV12AOrYV16OrNative(video_frame->format()))
193 << video_frame->format();
194 if (IsEitherYV12OrYV12AOrYV16(video_frame->format())) {
195 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
196 video_frame->stride(media::VideoFrame::kVPlane));
199 // Check if |bitmap| needs to be (re)allocated.
200 if (bitmap->isNull() ||
201 bitmap->width() != video_frame->visible_rect().width() ||
202 bitmap->height() != video_frame->visible_rect().height()) {
203 bitmap->setConfig(SkBitmap::kARGB_8888_Config,
204 video_frame->visible_rect().width(),
205 video_frame->visible_rect().height());
206 bitmap->allocPixels();
207 bitmap->setIsVolatile(true);
210 bitmap->lockPixels();
212 size_t y_offset = 0;
213 size_t uv_offset = 0;
214 if (IsEitherYV12OrYV12AOrYV16(video_frame->format())) {
215 int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1;
216 // Use the "left" and "top" of the destination rect to locate the offset
217 // in Y, U and V planes.
218 y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
219 video_frame->visible_rect().y()) +
220 video_frame->visible_rect().x();
221 // For format YV12, there is one U, V value per 2x2 block.
222 // For format YV16, there is one U, V value per 2x1 block.
223 uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
224 (video_frame->visible_rect().y() >> y_shift)) +
225 (video_frame->visible_rect().x() >> 1);
228 switch (video_frame->format()) {
229 case media::VideoFrame::YV12:
230 case media::VideoFrame::YV12J:
231 media::ConvertYUVToRGB32(
232 video_frame->data(media::VideoFrame::kYPlane) + y_offset,
233 video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
234 video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
235 static_cast<uint8*>(bitmap->getPixels()),
236 video_frame->visible_rect().width(),
237 video_frame->visible_rect().height(),
238 video_frame->stride(media::VideoFrame::kYPlane),
239 video_frame->stride(media::VideoFrame::kUPlane),
240 bitmap->rowBytes(),
241 media::YV12);
242 break;
244 case media::VideoFrame::YV16:
245 media::ConvertYUVToRGB32(
246 video_frame->data(media::VideoFrame::kYPlane) + y_offset,
247 video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
248 video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
249 static_cast<uint8*>(bitmap->getPixels()),
250 video_frame->visible_rect().width(),
251 video_frame->visible_rect().height(),
252 video_frame->stride(media::VideoFrame::kYPlane),
253 video_frame->stride(media::VideoFrame::kUPlane),
254 bitmap->rowBytes(),
255 media::YV16);
256 break;
258 case media::VideoFrame::YV12A:
259 media::ConvertYUVAToARGB(
260 video_frame->data(media::VideoFrame::kYPlane) + y_offset,
261 video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
262 video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
263 video_frame->data(media::VideoFrame::kAPlane),
264 static_cast<uint8*>(bitmap->getPixels()),
265 video_frame->visible_rect().width(),
266 video_frame->visible_rect().height(),
267 video_frame->stride(media::VideoFrame::kYPlane),
268 video_frame->stride(media::VideoFrame::kUPlane),
269 video_frame->stride(media::VideoFrame::kAPlane),
270 bitmap->rowBytes(),
271 media::YV12);
272 break;
274 case media::VideoFrame::NATIVE_TEXTURE:
275 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
276 video_frame->ReadPixelsFromNativeTexture(*bitmap);
277 break;
279 default:
280 NOTREACHED();
281 break;
283 bitmap->notifyPixelsChanged();
284 bitmap->unlockPixels();
287 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
288 : last_frame_timestamp_(media::kNoTimestamp()) {
291 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
293 void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
294 SkCanvas* canvas,
295 const gfx::RectF& dest_rect,
296 uint8 alpha) {
297 if (alpha == 0) {
298 return;
301 SkRect dest;
302 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
304 SkPaint paint;
305 paint.setAlpha(alpha);
307 // Paint black rectangle if there isn't a frame available or the
308 // frame has an unexpected format.
309 if (!video_frame ||
310 !IsEitherYV12OrYV12AOrYV16OrNative(video_frame->format())) {
311 canvas->drawRect(dest, paint);
312 return;
315 // Scale and convert to RGB in one step if we can.
316 if (CanFastPaint(canvas, alpha, video_frame->format())) {
317 FastPaint(video_frame, canvas, dest);
318 return;
321 // Check if we should convert and update |last_frame_|.
322 if (last_frame_.isNull() ||
323 video_frame->GetTimestamp() != last_frame_timestamp_) {
324 ConvertVideoFrameToBitmap(video_frame, &last_frame_);
325 last_frame_timestamp_ = video_frame->GetTimestamp();
328 // Do a slower paint using |last_frame_|.
329 paint.setFilterBitmap(true);
330 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
333 } // namespace media