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/base/video_frame.h"
6#include "media/base/video_util.h"
7#include "testing/gtest/include/gtest/gtest.h"
8#include "third_party/skia/include/core/SkCanvas.h"
9#include "media/filters/skcanvas_video_renderer.h"
10
11using media::VideoFrame;
12
13namespace media {
14
15static const int kWidth = 320;
16static const int kHeight = 240;
17static const gfx::Rect kNaturalRect(0, 0, kWidth, kHeight);
18
19// Helper for filling a |canvas| with a solid |color|.
20void FillCanvas(SkCanvas* canvas, SkColor color) {
21  canvas->clear(color);
22}
23
24// Helper for returning the color of a solid |canvas|.
25SkColor GetColorAt(SkCanvas* canvas, int x, int y) {
26  SkBitmap bitmap;
27  if (!bitmap.tryAllocN32Pixels(1, 1))
28    return 0;
29  if (!canvas->readPixels(&bitmap, x, y))
30    return 0;
31  return bitmap.getColor(0, 0);
32}
33
34SkColor GetColor(SkCanvas* canvas) {
35  return GetColorAt(canvas, 0, 0);
36}
37
38class SkCanvasVideoRendererTest : public testing::Test {
39 public:
40  enum Color {
41    kNone,
42    kRed,
43    kGreen,
44    kBlue,
45  };
46
47  SkCanvasVideoRendererTest();
48  virtual ~SkCanvasVideoRendererTest();
49
50  // Paints to |canvas| using |renderer_| without any frame data.
51  void PaintWithoutFrame(SkCanvas* canvas);
52
53  // Paints the |video_frame| to the |canvas| using |renderer_|, setting the
54  // color of |video_frame| to |color| first.
55  void Paint(const scoped_refptr<VideoFrame>& video_frame,
56             SkCanvas* canvas,
57             Color color);
58  void PaintRotated(const scoped_refptr<VideoFrame>& video_frame,
59                    SkCanvas* canvas,
60                    Color color,
61                    SkXfermode::Mode mode,
62                    VideoRotation video_rotation);
63
64  void Copy(const scoped_refptr<VideoFrame>& video_frame, SkCanvas* canvas);
65
66  // Getters for various frame sizes.
67  scoped_refptr<VideoFrame> natural_frame() { return natural_frame_; }
68  scoped_refptr<VideoFrame> larger_frame() { return larger_frame_; }
69  scoped_refptr<VideoFrame> smaller_frame() { return smaller_frame_; }
70  scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; }
71
72  // Standard canvas.
73  SkCanvas* target_canvas() { return &target_canvas_; }
74
75 private:
76  SkCanvasVideoRenderer renderer_;
77
78  scoped_refptr<VideoFrame> natural_frame_;
79  scoped_refptr<VideoFrame> larger_frame_;
80  scoped_refptr<VideoFrame> smaller_frame_;
81  scoped_refptr<VideoFrame> cropped_frame_;
82
83  SkCanvas target_canvas_;
84
85  DISALLOW_COPY_AND_ASSIGN(SkCanvasVideoRendererTest);
86};
87
88static SkBitmap AllocBitmap(int width, int height) {
89  SkBitmap bitmap;
90  bitmap.allocPixels(SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType));
91  bitmap.eraseColor(0);
92  return bitmap;
93}
94
95SkCanvasVideoRendererTest::SkCanvasVideoRendererTest()
96    : natural_frame_(VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight))),
97      larger_frame_(VideoFrame::CreateBlackFrame(
98          gfx::Size(kWidth * 2, kHeight * 2))),
99      smaller_frame_(VideoFrame::CreateBlackFrame(
100          gfx::Size(kWidth / 2, kHeight / 2))),
101      cropped_frame_(VideoFrame::CreateFrame(
102          VideoFrame::YV12,
103          gfx::Size(16, 16),
104          gfx::Rect(6, 6, 8, 6),
105          gfx::Size(8, 6),
106          base::TimeDelta::FromMilliseconds(4))),
107      target_canvas_(AllocBitmap(kWidth, kHeight)) {
108  // Give each frame a unique timestamp.
109  natural_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(1));
110  larger_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(2));
111  smaller_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(3));
112
113  // Make sure the cropped video frame's aspect ratio matches the output device.
114  // Update cropped_frame_'s crop dimensions if this is not the case.
115  EXPECT_EQ(cropped_frame()->visible_rect().width() * kHeight,
116            cropped_frame()->visible_rect().height() * kWidth);
117
118  // Fill in the cropped frame's entire data with colors:
119  //
120  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
121  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
122  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
123  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
124  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
125  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
126  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
127  //   Bl Bl Bl Bl Bl Bl Bl Bl R  R  R  R  R  R  R  R
128  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
129  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
130  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
131  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
132  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
133  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
134  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
135  //   G  G  G  G  G  G  G  G  B  B  B  B  B  B  B  B
136  //
137  // The visible crop of the frame (as set by its visible_rect_) has contents:
138  //
139  //   Bl Bl R  R  R  R  R  R
140  //   Bl Bl R  R  R  R  R  R
141  //   G  G  B  B  B  B  B  B
142  //   G  G  B  B  B  B  B  B
143  //   G  G  B  B  B  B  B  B
144  //   G  G  B  B  B  B  B  B
145  //
146  // Each color region in the cropped frame is on a 2x2 block granularity, to
147  // avoid sharing UV samples between regions.
148
149  static const uint8 cropped_y_plane[] = {
150      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
151      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
152      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
153      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
154      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
155      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
156      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
157      0,   0,   0,   0,   0,   0,   0,   0, 76, 76, 76, 76, 76, 76, 76, 76,
158    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
159    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
160    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
161    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
162    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
163    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
164    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
165    149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
166  };
167
168  static const uint8 cropped_u_plane[] = {
169    128, 128, 128, 128,  84,  84,  84,  84,
170    128, 128, 128, 128,  84,  84,  84,  84,
171    128, 128, 128, 128,  84,  84,  84,  84,
172    128, 128, 128, 128,  84,  84,  84,  84,
173     43,  43,  43,  43, 255, 255, 255, 255,
174     43,  43,  43,  43, 255, 255, 255, 255,
175     43,  43,  43,  43, 255, 255, 255, 255,
176     43,  43,  43,  43, 255, 255, 255, 255,
177  };
178  static const uint8 cropped_v_plane[] = {
179    128, 128, 128, 128, 255, 255, 255, 255,
180    128, 128, 128, 128, 255, 255, 255, 255,
181    128, 128, 128, 128, 255, 255, 255, 255,
182    128, 128, 128, 128, 255, 255, 255, 255,
183     21,  21,  21,  21, 107, 107, 107, 107,
184     21,  21,  21,  21, 107, 107, 107, 107,
185     21,  21,  21,  21, 107, 107, 107, 107,
186     21,  21,  21,  21, 107, 107, 107, 107,
187  };
188
189  media::CopyYPlane(cropped_y_plane, 16, 16, cropped_frame().get());
190  media::CopyUPlane(cropped_u_plane, 8, 8, cropped_frame().get());
191  media::CopyVPlane(cropped_v_plane, 8, 8, cropped_frame().get());
192}
193
194SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {}
195
196void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) {
197  renderer_.Paint(NULL,
198                  canvas,
199                  kNaturalRect,
200                  0xFF,
201                  SkXfermode::kSrcOver_Mode,
202                  VIDEO_ROTATION_0);
203}
204
205void SkCanvasVideoRendererTest::Paint(
206    const scoped_refptr<VideoFrame>& video_frame,
207    SkCanvas* canvas,
208    Color color) {
209  PaintRotated(
210      video_frame, canvas, color, SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0);
211}
212
213void SkCanvasVideoRendererTest::PaintRotated(
214    const scoped_refptr<VideoFrame>& video_frame,
215    SkCanvas* canvas,
216    Color color,
217    SkXfermode::Mode mode,
218    VideoRotation video_rotation) {
219  switch (color) {
220    case kNone:
221      break;
222    case kRed:
223      media::FillYUV(video_frame.get(), 76, 84, 255);
224      break;
225    case kGreen:
226      media::FillYUV(video_frame.get(), 149, 43, 21);
227      break;
228    case kBlue:
229      media::FillYUV(video_frame.get(), 29, 255, 107);
230      break;
231  }
232  renderer_.Paint(
233      video_frame, canvas, kNaturalRect, 0xFF, mode, video_rotation);
234}
235
236void SkCanvasVideoRendererTest::Copy(
237    const scoped_refptr<VideoFrame>& video_frame,
238    SkCanvas* canvas) {
239  renderer_.Copy(video_frame, canvas);
240}
241
242TEST_F(SkCanvasVideoRendererTest, NoFrame) {
243  // Test that black gets painted over canvas.
244  FillCanvas(target_canvas(), SK_ColorRED);
245  PaintWithoutFrame(target_canvas());
246  EXPECT_EQ(SK_ColorBLACK, GetColor(target_canvas()));
247}
248
249TEST_F(SkCanvasVideoRendererTest, TransparentFrame) {
250  FillCanvas(target_canvas(), SK_ColorRED);
251  PaintRotated(
252      VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
253      target_canvas(),
254      kNone,
255      SkXfermode::kSrcOver_Mode,
256      VIDEO_ROTATION_0);
257  EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas()));
258}
259
260TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) {
261  FillCanvas(target_canvas(), SK_ColorRED);
262  // SRC mode completely overwrites the buffer.
263  PaintRotated(
264      VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
265      target_canvas(),
266      kNone,
267      SkXfermode::kSrc_Mode,
268      VIDEO_ROTATION_0);
269  EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
270            GetColor(target_canvas()));
271}
272
273TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) {
274  FillCanvas(target_canvas(), SK_ColorRED);
275  Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
276       target_canvas());
277  EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
278            GetColor(target_canvas()));
279}
280
281TEST_F(SkCanvasVideoRendererTest, Natural) {
282  Paint(natural_frame(), target_canvas(), kRed);
283  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
284}
285
286TEST_F(SkCanvasVideoRendererTest, Larger) {
287  Paint(natural_frame(), target_canvas(), kRed);
288  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
289
290  Paint(larger_frame(), target_canvas(), kBlue);
291  EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
292}
293
294TEST_F(SkCanvasVideoRendererTest, Smaller) {
295  Paint(natural_frame(), target_canvas(), kRed);
296  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
297
298  Paint(smaller_frame(), target_canvas(), kBlue);
299  EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
300}
301
302TEST_F(SkCanvasVideoRendererTest, NoTimestamp) {
303  VideoFrame* video_frame = natural_frame().get();
304  video_frame->set_timestamp(media::kNoTimestamp());
305  Paint(video_frame, target_canvas(), kRed);
306  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
307}
308
309TEST_F(SkCanvasVideoRendererTest, SameVideoFrame) {
310  Paint(natural_frame(), target_canvas(), kRed);
311  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
312
313  // Slow paints can get cached, expect the old color value.
314  Paint(natural_frame(), target_canvas(), kBlue);
315  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
316}
317
318TEST_F(SkCanvasVideoRendererTest, CroppedFrame) {
319  Paint(cropped_frame(), target_canvas(), kNone);
320  // Check the corners.
321  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), 0, 0));
322  EXPECT_EQ(SK_ColorRED,   GetColorAt(target_canvas(), kWidth - 1, 0));
323  EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), 0, kHeight - 1));
324  EXPECT_EQ(SK_ColorBLUE,  GetColorAt(target_canvas(), kWidth - 1,
325                                                       kHeight - 1));
326  // Check the interior along the border between color regions.  Note that we're
327  // bilinearly upscaling, so we'll need to take care to pick sample points that
328  // are just outside the "zone of resampling".
329  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), kWidth  * 1 / 8 - 1,
330                                                       kHeight * 1 / 6 - 1));
331  EXPECT_EQ(SK_ColorRED,   GetColorAt(target_canvas(), kWidth  * 3 / 8,
332                                                       kHeight * 1 / 6 - 1));
333  EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), kWidth  * 1 / 8 - 1,
334                                                       kHeight * 3 / 6));
335  EXPECT_EQ(SK_ColorBLUE,  GetColorAt(target_canvas(), kWidth  * 3 / 8,
336                                                       kHeight * 3 / 6));
337}
338
339TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) {
340  SkCanvas canvas(AllocBitmap(kWidth, kHeight));
341  const gfx::Rect crop_rect = cropped_frame()->visible_rect();
342
343  // Force painting to a non-zero position on the destination bitmap, to check
344  // if the coordinates are calculated properly.
345  const int offset_x = 10;
346  const int offset_y = 15;
347  canvas.translate(offset_x, offset_y);
348
349  // Create a destination canvas with dimensions and scale which would not
350  // cause scaling.
351  canvas.scale(static_cast<SkScalar>(crop_rect.width()) / kWidth,
352               static_cast<SkScalar>(crop_rect.height()) / kHeight);
353
354  Paint(cropped_frame(), &canvas, kNone);
355
356  // Check the corners.
357  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, offset_x, offset_y));
358  EXPECT_EQ(SK_ColorRED,
359            GetColorAt(&canvas, offset_x + crop_rect.width() - 1, offset_y));
360  EXPECT_EQ(SK_ColorGREEN,
361            GetColorAt(&canvas, offset_x, offset_y + crop_rect.height() - 1));
362  EXPECT_EQ(SK_ColorBLUE,
363            GetColorAt(&canvas,
364                       offset_x + crop_rect.width() - 1,
365                       offset_y + crop_rect.height() - 1));
366}
367
368TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
369  SkCanvas canvas(AllocBitmap(kWidth, kHeight));
370  const gfx::Rect crop_rect = cropped_frame()->visible_rect();
371  PaintRotated(cropped_frame(),
372               &canvas,
373               kNone,
374               SkXfermode::kSrcOver_Mode,
375               VIDEO_ROTATION_90);
376  // Check the corners.
377  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0));
378  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0));
379  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
380  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, kHeight - 1));
381}
382
383TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
384  SkCanvas canvas(AllocBitmap(kWidth, kHeight));
385  const gfx::Rect crop_rect = cropped_frame()->visible_rect();
386  PaintRotated(cropped_frame(),
387               &canvas,
388               kNone,
389               SkXfermode::kSrcOver_Mode,
390               VIDEO_ROTATION_180);
391  // Check the corners.
392  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0));
393  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0));
394  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
395  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, kHeight - 1));
396}
397
398TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) {
399  SkCanvas canvas(AllocBitmap(kWidth, kHeight));
400  const gfx::Rect crop_rect = cropped_frame()->visible_rect();
401  PaintRotated(cropped_frame(),
402               &canvas,
403               kNone,
404               SkXfermode::kSrcOver_Mode,
405               VIDEO_ROTATION_270);
406  // Check the corners.
407  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0));
408  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0));
409  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
410  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, 0, kHeight - 1));
411}
412
413}  // namespace media
414