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