1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/functional/bind.h"
9 #include "base/logging.h"
10 #include "base/memory/aligned_memory.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/sys_byteorder.h"
13 #include "base/test/task_environment.h"
14 #include "build/build_config.h"
15 #include "cc/paint/paint_flags.h"
16 #include "cc/paint/skia_paint_canvas.h"
17 #include "components/viz/common/gpu/context_provider.h"
18 #include "components/viz/test/test_context_provider.h"
19 #include "components/viz/test/test_gpu_service_holder.h"
20 #include "components/viz/test/test_in_process_context_provider.h"
21 #include "gpu/GLES2/gl2extchromium.h"
22 #include "gpu/command_buffer/client/gles2_interface_stub.h"
23 #include "gpu/command_buffer/common/capabilities.h"
24 #include "gpu/command_buffer/common/shared_image_usage.h"
25 #include "gpu/config/gpu_feature_info.h"
26 #include "media/base/timestamp_constants.h"
27 #include "media/base/video_frame.h"
28 #include "media/base/video_util.h"
29 #include "media/renderers/paint_canvas_video_renderer.h"
30 #include "media/renderers/shared_image_video_frame_test_utils.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "third_party/libyuv/include/libyuv/convert.h"
33 #include "third_party/libyuv/include/libyuv/scale.h"
34 #include "third_party/skia/include/core/SkColorPriv.h"
35 #include "third_party/skia/include/core/SkImage.h"
36 #include "third_party/skia/include/core/SkRefCnt.h"
37 #include "third_party/skia/include/core/SkSurface.h"
38 #include "third_party/skia/include/gpu/GrDirectContext.h"
39 #include "ui/gfx/color_space.h"
40 #include "ui/gfx/geometry/rect_f.h"
41 #include "ui/gl/gl_implementation.h"
42 #include "ui/gl/test/gl_surface_test_support.h"
44 using media::VideoFrame;
48 static const int kWidth = 320;
49 static const int kHeight = 240;
50 static const gfx::RectF kNaturalRect(kWidth, kHeight);
52 // Generate frame pixels to provided |external_memory| and wrap it as frame.
53 scoped_refptr<VideoFrame> CreateTestY16Frame(const gfx::Size& coded_size,
54 const gfx::Rect& visible_rect,
55 void* external_memory,
56 base::TimeDelta timestamp) {
57 const int offset_x = visible_rect.x();
58 const int offset_y = visible_rect.y();
59 const int stride = coded_size.width();
60 const size_t byte_size = stride * coded_size.height() * 2;
62 // In the visible rect, fill upper byte with [0-255] and lower with [255-0].
63 uint16_t* data = static_cast<uint16_t*>(external_memory);
64 for (int j = 0; j < visible_rect.height(); j++) {
65 for (int i = 0; i < visible_rect.width(); i++) {
66 const int value = i + j * visible_rect.width();
67 data[(stride * (j + offset_y)) + i + offset_x] =
68 ((value & 0xFF) << 8) | (~value & 0xFF);
72 return media::VideoFrame::WrapExternalData(
73 media::PIXEL_FORMAT_Y16, coded_size, visible_rect, visible_rect.size(),
74 static_cast<uint8_t*>(external_memory), byte_size, timestamp);
77 // Readback the contents of a RGBA texture into an array of RGBA values.
78 static std::unique_ptr<uint8_t[]> ReadbackTexture(
79 gpu::gles2::GLES2Interface* gl,
81 const gfx::Size& size) {
82 size_t pixel_count = size.width() * size.height();
84 gl->GenFramebuffers(1, &fbo);
85 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
86 gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
88 auto pixels = std::make_unique<uint8_t[]>(pixel_count * 4);
89 uint8_t* raw_pixels = pixels.get();
90 gl->ReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
92 gl->DeleteFramebuffers(1, &fbo);
96 // Returns a functor that retrieves a SkColor for a given pixel, from raw RGBA
98 static auto ColorGetter(uint8_t* pixels, const gfx::Size& size) {
99 return [pixels, size](size_t x, size_t y) {
100 uint8_t* p = pixels + (size.width() * y + x) * 4;
101 return SkColorSetARGB(p[3], p[0], p[1], p[2]);
105 class PaintCanvasVideoRendererTest : public testing::Test {
114 PaintCanvasVideoRendererTest();
116 PaintCanvasVideoRendererTest(const PaintCanvasVideoRendererTest&) = delete;
117 PaintCanvasVideoRendererTest& operator=(const PaintCanvasVideoRendererTest&) =
120 ~PaintCanvasVideoRendererTest() override;
122 // Paints to |canvas| using |renderer_| without any frame data.
123 void PaintWithoutFrame(cc::PaintCanvas* canvas);
125 // Paints the |video_frame| to the |canvas| using |renderer_|, setting the
126 // color of |video_frame| to |color| first.
127 void Paint(scoped_refptr<VideoFrame> video_frame,
128 cc::PaintCanvas* canvas,
130 void PaintRotated(scoped_refptr<VideoFrame> video_frame,
131 cc::PaintCanvas* canvas,
132 const gfx::RectF& dest_rect,
135 VideoTransformation video_transformation);
137 void Copy(scoped_refptr<VideoFrame> video_frame, cc::PaintCanvas* canvas);
139 // Getters for various frame sizes.
140 scoped_refptr<VideoFrame> natural_frame() { return natural_frame_; }
141 scoped_refptr<VideoFrame> larger_frame() { return larger_frame_; }
142 scoped_refptr<VideoFrame> smaller_frame() { return smaller_frame_; }
143 scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; }
146 cc::PaintCanvas* target_canvas() { return &target_canvas_; }
147 SkBitmap* bitmap() { return &bitmap_; }
150 PaintCanvasVideoRenderer renderer_;
152 scoped_refptr<VideoFrame> natural_frame_;
153 scoped_refptr<VideoFrame> larger_frame_;
154 scoped_refptr<VideoFrame> smaller_frame_;
155 scoped_refptr<VideoFrame> cropped_frame_;
158 cc::SkiaPaintCanvas target_canvas_;
159 base::test::TaskEnvironment task_environment_;
162 static SkBitmap AllocBitmap(int width, int height) {
164 bitmap.allocPixels(SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType));
165 bitmap.eraseColor(0);
169 static scoped_refptr<VideoFrame> CreateCroppedFrame() {
170 scoped_refptr<VideoFrame> cropped_frame = VideoFrame::CreateFrame(
171 PIXEL_FORMAT_I420, gfx::Size(16, 16), gfx::Rect(6, 6, 8, 6),
172 gfx::Size(8, 6), base::Milliseconds(4));
173 // Make sure the cropped video frame's aspect ratio matches the output device.
174 // Update cropped_frame_'s crop dimensions if this is not the case.
175 EXPECT_EQ(cropped_frame->visible_rect().width() * kHeight,
176 cropped_frame->visible_rect().height() * kWidth);
178 // Fill in the cropped frame's entire data with colors:
180 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
181 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
182 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
183 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
184 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
185 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
186 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
187 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
188 // G G G G G G G G B B B B B B B B
189 // G G G G G G G G B B B B B B B B
190 // G G G G G G G G B B B B B B B B
191 // G G G G G G G G B B B B B B B B
192 // G G G G G G G G B B B B B B B B
193 // G G G G G G G G B B B B B B B B
194 // G G G G G G G G B B B B B B B B
195 // G G G G G G G G B B B B B B B B
197 // The visible crop of the frame (as set by its visible_rect_) has contents:
206 // Each color region in the cropped frame is on a 2x2 block granularity, to
207 // avoid sharing UV samples between regions.
209 static const uint8_t cropped_y_plane[] = {
210 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
211 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
212 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
213 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
214 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
215 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
216 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
217 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
218 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
219 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
220 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
221 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
222 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
223 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
224 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
225 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
228 static const uint8_t cropped_u_plane[] = {
229 128, 128, 128, 128, 84, 84, 84, 84, 128, 128, 128, 128, 84,
230 84, 84, 84, 128, 128, 128, 128, 84, 84, 84, 84, 128, 128,
231 128, 128, 84, 84, 84, 84, 43, 43, 43, 43, 255, 255, 255,
232 255, 43, 43, 43, 43, 255, 255, 255, 255, 43, 43, 43, 43,
233 255, 255, 255, 255, 43, 43, 43, 43, 255, 255, 255, 255,
235 static const uint8_t cropped_v_plane[] = {
236 128, 128, 128, 128, 255, 255, 255, 255, 128, 128, 128, 128, 255,
237 255, 255, 255, 128, 128, 128, 128, 255, 255, 255, 255, 128, 128,
238 128, 128, 255, 255, 255, 255, 21, 21, 21, 21, 107, 107, 107,
239 107, 21, 21, 21, 21, 107, 107, 107, 107, 21, 21, 21, 21,
240 107, 107, 107, 107, 21, 21, 21, 21, 107, 107, 107, 107,
243 libyuv::I420Copy(cropped_y_plane, 16, cropped_u_plane, 8, cropped_v_plane, 8,
244 cropped_frame->writable_data(VideoFrame::kYPlane),
245 cropped_frame->stride(VideoFrame::kYPlane),
246 cropped_frame->writable_data(VideoFrame::kUPlane),
247 cropped_frame->stride(VideoFrame::kUPlane),
248 cropped_frame->writable_data(VideoFrame::kVPlane),
249 cropped_frame->stride(VideoFrame::kVPlane), 16, 16);
251 return cropped_frame;
254 PaintCanvasVideoRendererTest::PaintCanvasVideoRendererTest()
255 : natural_frame_(VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight))),
257 VideoFrame::CreateBlackFrame(gfx::Size(kWidth * 2, kHeight * 2))),
259 VideoFrame::CreateBlackFrame(gfx::Size(kWidth / 2, kHeight / 2))),
260 cropped_frame_(CreateCroppedFrame()),
261 bitmap_(AllocBitmap(kWidth, kHeight)),
262 target_canvas_(bitmap_) {
263 // Give each frame a unique timestamp.
264 natural_frame_->set_timestamp(base::Milliseconds(1));
265 larger_frame_->set_timestamp(base::Milliseconds(2));
266 smaller_frame_->set_timestamp(base::Milliseconds(3));
269 PaintCanvasVideoRendererTest::~PaintCanvasVideoRendererTest() = default;
271 void PaintCanvasVideoRendererTest::PaintWithoutFrame(cc::PaintCanvas* canvas) {
272 cc::PaintFlags flags;
273 flags.setFilterQuality(cc::PaintFlags::FilterQuality::kLow);
274 renderer_.Paint(nullptr, canvas, kNaturalRect, flags, kNoTransformation,
278 void PaintCanvasVideoRendererTest::Paint(scoped_refptr<VideoFrame> video_frame,
279 cc::PaintCanvas* canvas,
281 PaintRotated(std::move(video_frame), canvas, kNaturalRect, color,
282 SkBlendMode::kSrcOver, kNoTransformation);
285 void PaintCanvasVideoRendererTest::PaintRotated(
286 scoped_refptr<VideoFrame> video_frame,
287 cc::PaintCanvas* canvas,
288 const gfx::RectF& dest_rect,
291 VideoTransformation video_transformation) {
296 media::FillYUV(video_frame.get(), 76, 84, 255);
299 media::FillYUV(video_frame.get(), 149, 43, 21);
302 media::FillYUV(video_frame.get(), 29, 255, 107);
305 cc::PaintFlags flags;
306 flags.setBlendMode(mode);
307 flags.setFilterQuality(cc::PaintFlags::FilterQuality::kLow);
308 renderer_.Paint(std::move(video_frame), canvas, dest_rect, flags,
309 video_transformation, nullptr);
312 void PaintCanvasVideoRendererTest::Copy(scoped_refptr<VideoFrame> video_frame,
313 cc::PaintCanvas* canvas) {
314 renderer_.Copy(std::move(video_frame), canvas, nullptr);
317 TEST_F(PaintCanvasVideoRendererTest, NoFrame) {
318 // Test that black gets painted over canvas.
319 target_canvas()->clear(SkColors::kRed);
320 PaintWithoutFrame(target_canvas());
321 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
324 TEST_F(PaintCanvasVideoRendererTest, TransparentFrame) {
325 target_canvas()->clear(SkColors::kRed);
327 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
328 target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrcOver,
330 EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), bitmap()->getColor(0, 0));
333 TEST_F(PaintCanvasVideoRendererTest, TransparentFrameSrcMode) {
334 target_canvas()->clear(SkColors::kRed);
335 // SRC mode completely overwrites the buffer.
337 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
338 target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrc,
340 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
341 bitmap()->getColor(0, 0));
344 TEST_F(PaintCanvasVideoRendererTest, TransparentFrameSrcMode1x1) {
345 target_canvas()->clear(SkColors::kRed);
346 // SRC mode completely overwrites the buffer.
347 auto frame = VideoFrame::CreateTransparentFrame(gfx::Size(1, 1));
348 PaintRotated(frame.get(), target_canvas(), gfx::RectF(1, 1), kNone,
349 SkBlendMode::kSrc, kNoTransformation);
350 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
351 bitmap()->getColor(0, 0));
354 TEST_F(PaintCanvasVideoRendererTest, CopyTransparentFrame) {
355 target_canvas()->clear(SkColors::kRed);
356 Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
358 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
359 bitmap()->getColor(0, 0));
362 TEST_F(PaintCanvasVideoRendererTest, Natural) {
363 Paint(natural_frame(), target_canvas(), kRed);
364 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
367 TEST_F(PaintCanvasVideoRendererTest, Larger) {
368 Paint(natural_frame(), target_canvas(), kRed);
369 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
371 Paint(larger_frame(), target_canvas(), kBlue);
372 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
375 TEST_F(PaintCanvasVideoRendererTest, Smaller) {
376 Paint(natural_frame(), target_canvas(), kRed);
377 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
379 Paint(smaller_frame(), target_canvas(), kBlue);
380 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
383 TEST_F(PaintCanvasVideoRendererTest, NoTimestamp) {
384 VideoFrame* video_frame = natural_frame().get();
385 video_frame->set_timestamp(media::kNoTimestamp);
386 Paint(video_frame, target_canvas(), kRed);
387 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
390 TEST_F(PaintCanvasVideoRendererTest, CroppedFrame) {
391 Paint(cropped_frame(), target_canvas(), kNone);
392 // Check the corners.
393 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
394 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
395 EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
396 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
397 // Check the interior along the border between color regions. Note that we're
398 // bilinearly upscaling, so we'll need to take care to pick sample points that
399 // are just outside the "zone of resampling".
400 EXPECT_EQ(SK_ColorBLACK,
401 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
402 EXPECT_EQ(SK_ColorRED,
403 bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
404 EXPECT_EQ(SK_ColorGREEN,
405 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
406 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
409 uint32_t MaybeConvertABGRToARGB(uint32_t abgr) {
410 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
414 return (base::ByteSwap(abgr & 0x00FFFFFF) >> 8) | (abgr & 0xFF000000);
418 TEST_F(PaintCanvasVideoRendererTest, CroppedFrameToRGBParallel) {
419 // We need a test frame large enough to trigger parallel conversion. So we use
420 // cropped_frame() as a base and scale it up. Note: Visible rect and natural
421 // size must be even.
422 auto test_frame = VideoFrame::CreateFrame(
423 PIXEL_FORMAT_I420, gfx::Size(3840, 2160), gfx::Rect(1440, 810, 1920, 810),
424 gfx::Size(1920, 810), base::TimeDelta());
426 // Fill in the frame with the same data as the cropped frame.
427 libyuv::I420Scale(cropped_frame()->data(0), cropped_frame()->stride(0),
428 cropped_frame()->data(1), cropped_frame()->stride(1),
429 cropped_frame()->data(2), cropped_frame()->stride(2),
430 cropped_frame()->coded_size().width(),
431 cropped_frame()->coded_size().height(),
432 test_frame->writable_data(0), test_frame->stride(0),
433 test_frame->writable_data(1), test_frame->stride(1),
434 test_frame->writable_data(2), test_frame->stride(2),
435 test_frame->coded_size().width(),
436 test_frame->coded_size().height(), libyuv::kFilterNone);
438 const gfx::Size visible_size = test_frame->visible_rect().size();
439 const size_t row_bytes = visible_size.width() * sizeof(SkColor);
440 const size_t allocation_size = row_bytes * visible_size.height();
442 std::unique_ptr<uint8_t, base::AlignedFreeDeleter> memory(
443 static_cast<uint8_t*>(base::AlignedAlloc(
444 allocation_size, media::VideoFrame::kFrameAddressAlignment)));
445 memset(memory.get(), 0, allocation_size);
447 PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
448 test_frame.get(), memory.get(), row_bytes);
450 const uint32_t* rgb_pixels = reinterpret_cast<uint32_t*>(memory.get());
452 // Check the corners; this is sufficient to reveal https://crbug.com/1027442.
453 EXPECT_EQ(SK_ColorBLACK, rgb_pixels[0]);
454 EXPECT_EQ(MaybeConvertABGRToARGB(SK_ColorRED),
455 rgb_pixels[visible_size.width() - 1]);
456 EXPECT_EQ(SK_ColorGREEN,
457 rgb_pixels[visible_size.width() * (visible_size.height() - 1)]);
458 EXPECT_EQ(MaybeConvertABGRToARGB(SK_ColorBLUE),
459 rgb_pixels[(visible_size.width() - 1) * visible_size.height()]);
462 TEST_F(PaintCanvasVideoRendererTest, CroppedFrame_NoScaling) {
463 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
464 cc::SkiaPaintCanvas canvas(bitmap);
465 const gfx::Rect crop_rect = cropped_frame()->visible_rect();
467 // Force painting to a non-zero position on the destination bitmap, to check
468 // if the coordinates are calculated properly.
469 const int offset_x = 10;
470 const int offset_y = 15;
471 canvas.translate(offset_x, offset_y);
473 // Create a destination canvas with dimensions and scale which would not
475 canvas.scale(static_cast<SkScalar>(crop_rect.width()) / kWidth,
476 static_cast<SkScalar>(crop_rect.height()) / kHeight);
478 Paint(cropped_frame(), &canvas, kNone);
480 // Check the corners.
481 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(offset_x, offset_y));
482 EXPECT_EQ(SK_ColorRED,
483 bitmap.getColor(offset_x + crop_rect.width() - 1, offset_y));
484 EXPECT_EQ(SK_ColorGREEN,
485 bitmap.getColor(offset_x, offset_y + crop_rect.height() - 1));
486 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(offset_x + crop_rect.width() - 1,
487 offset_y + crop_rect.height() - 1));
490 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_90) {
491 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
492 cc::SkiaPaintCanvas canvas(bitmap);
493 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
494 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_90));
495 // Check the corners.
496 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));
497 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, 0));
498 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
499 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, kHeight - 1));
502 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_180) {
503 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
504 cc::SkiaPaintCanvas canvas(bitmap);
505 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
506 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_180));
507 // Check the corners.
508 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));
509 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, 0));
510 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
511 EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, kHeight - 1));
514 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_270) {
515 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
516 cc::SkiaPaintCanvas canvas(bitmap);
517 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
518 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_270));
519 // Check the corners.
520 EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, 0));
521 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, 0));
522 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
523 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(0, kHeight - 1));
526 TEST_F(PaintCanvasVideoRendererTest, Video_Translate) {
527 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
528 cc::SkiaPaintCanvas canvas(bitmap);
529 canvas.clear(SkColors::kMagenta);
531 PaintRotated(cropped_frame(), &canvas,
532 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
533 kNone, SkBlendMode::kSrcOver, kNoTransformation);
534 // Check the corners of quadrant 2 and 4.
535 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
536 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
537 EXPECT_EQ(SK_ColorMAGENTA,
538 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
539 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
540 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight / 2));
541 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight / 2));
542 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight - 1));
543 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight - 1));
546 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_90) {
547 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
548 cc::SkiaPaintCanvas canvas(bitmap);
549 canvas.clear(SkColors::kMagenta);
551 PaintRotated(cropped_frame(), &canvas,
552 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
553 kNone, SkBlendMode::kSrcOver,
554 VideoTransformation(VIDEO_ROTATION_90));
555 // Check the corners of quadrant 2 and 4.
556 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
557 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
558 EXPECT_EQ(SK_ColorMAGENTA,
559 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
560 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
561 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight / 2));
562 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight / 2));
563 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
564 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight - 1));
567 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_180) {
568 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
569 cc::SkiaPaintCanvas canvas(bitmap);
570 canvas.clear(SkColors::kMagenta);
572 PaintRotated(cropped_frame(), &canvas,
573 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
574 kNone, SkBlendMode::kSrcOver,
575 VideoTransformation(VIDEO_ROTATION_180));
576 // Check the corners of quadrant 2 and 4.
577 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
578 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
579 EXPECT_EQ(SK_ColorMAGENTA,
580 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
581 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
582 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight / 2));
583 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight / 2));
584 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
585 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight - 1));
588 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_270) {
589 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
590 cc::SkiaPaintCanvas canvas(bitmap);
591 canvas.clear(SkColors::kMagenta);
593 PaintRotated(cropped_frame(), &canvas,
594 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
595 kNone, SkBlendMode::kSrcOver,
596 VideoTransformation(VIDEO_ROTATION_270));
597 // Check the corners of quadrant 2 and 4.
598 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
599 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
600 EXPECT_EQ(SK_ColorMAGENTA,
601 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
602 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
603 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight / 2));
604 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight / 2));
605 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
606 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight - 1));
609 TEST_F(PaintCanvasVideoRendererTest, HighBitDepth) {
612 VideoPixelFormat format;
613 } kBitDepthAndFormats[] = {{9, PIXEL_FORMAT_YUV420P9},
614 {10, PIXEL_FORMAT_YUV420P10},
615 {12, PIXEL_FORMAT_YUV420P12}};
616 for (const auto param : kBitDepthAndFormats) {
617 // Copy cropped_frame into a highbit frame.
618 scoped_refptr<VideoFrame> frame(VideoFrame::CreateFrame(
619 param.format, cropped_frame()->coded_size(),
620 cropped_frame()->visible_rect(), cropped_frame()->natural_size(),
621 cropped_frame()->timestamp()));
622 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
624 int width = cropped_frame()->row_bytes(plane);
625 uint16_t* dst = reinterpret_cast<uint16_t*>(frame->writable_data(plane));
626 const uint8_t* src = cropped_frame()->data(plane);
627 for (int row = 0; row < cropped_frame()->rows(plane); row++) {
628 for (int col = 0; col < width; col++) {
629 dst[col] = src[col] << (param.bit_depth - 8);
631 src += cropped_frame()->stride(plane);
632 dst += frame->stride(plane) / 2;
636 Paint(frame, target_canvas(), kNone);
637 // Check the corners.
638 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
639 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
640 EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
641 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
642 // Check the interior along the border between color regions. Note that
643 // we're bilinearly upscaling, so we'll need to take care to pick sample
644 // points that are just outside the "zone of resampling".
645 EXPECT_EQ(SK_ColorBLACK,
646 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
647 EXPECT_EQ(SK_ColorRED,
648 bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
649 EXPECT_EQ(SK_ColorGREEN,
650 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
651 EXPECT_EQ(SK_ColorBLUE,
652 bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
656 TEST_F(PaintCanvasVideoRendererTest, Y16) {
658 bitmap.allocPixels(SkImageInfo::MakeN32(16, 16, kPremul_SkAlphaType));
660 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
661 const int offset_x = 3;
662 const int offset_y = 5;
663 const int stride = bitmap.width() + offset_x;
664 const size_t byte_size = stride * (bitmap.height() + offset_y) * 2;
665 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
666 static_cast<unsigned char*>(base::AlignedAlloc(
667 byte_size, media::VideoFrame::kFrameAddressAlignment)));
668 const gfx::Rect rect(offset_x, offset_y, bitmap.width(), bitmap.height());
670 CreateTestY16Frame(gfx::Size(stride, offset_y + bitmap.height()), rect,
671 memory.get(), cropped_frame()->timestamp());
673 cc::SkiaPaintCanvas canvas(bitmap);
674 cc::PaintFlags flags;
675 flags.setFilterQuality(cc::PaintFlags::FilterQuality::kNone);
676 renderer_.Paint(std::move(video_frame), &canvas,
677 gfx::RectF(bitmap.width(), bitmap.height()), flags,
678 kNoTransformation, nullptr);
679 for (int j = 0; j < bitmap.height(); j++) {
680 for (int i = 0; i < bitmap.width(); i++) {
681 const int value = i + j * bitmap.width();
682 EXPECT_EQ(SkColorSetRGB(value, value, value), bitmap.getColor(i, j));
687 // A reproducer test case for crbug.com/1230409 if run with ASAN enabled.
688 TEST_F(PaintCanvasVideoRendererTest, Yuv420P12OddWidth) {
689 // Allocate the Y, U, V planes for a 3x3 12-bit YUV 4:2:0 image. Note that
690 // there are no padding bytes after each row.
691 constexpr int kImgWidth = 3;
692 constexpr int kImgHeight = 3;
693 constexpr int kUvWidth = (kImgWidth + 1) / 2;
694 constexpr int kUvHeight = (kImgHeight + 1) / 2;
695 std::unique_ptr<uint16_t[]> y_plane =
696 std::make_unique<uint16_t[]>(kImgWidth * kImgHeight);
697 std::unique_ptr<uint16_t[]> u_plane =
698 std::make_unique<uint16_t[]>(kUvWidth * kUvHeight);
699 std::unique_ptr<uint16_t[]> v_plane =
700 std::make_unique<uint16_t[]>(kUvWidth * kUvHeight);
701 // Set all pixels to white.
702 for (int i = 0; i < kImgHeight; ++i) {
703 for (int j = 0; j < kImgWidth; ++j) {
704 y_plane[i * kImgWidth + j] = 4095;
707 for (int i = 0; i < kUvHeight; ++i) {
708 for (int j = 0; j < kUvWidth; ++j) {
709 u_plane[i * kUvWidth + j] = 2048;
710 v_plane[i * kUvWidth + j] = 2048;
713 const int32_t y_stride = sizeof(uint16_t) * kImgWidth;
714 const int32_t uv_stride = sizeof(uint16_t) * kUvWidth;
715 uint8_t* const y_data = reinterpret_cast<uint8_t*>(y_plane.get());
716 uint8_t* const u_data = reinterpret_cast<uint8_t*>(u_plane.get());
717 uint8_t* const v_data = reinterpret_cast<uint8_t*>(v_plane.get());
719 auto size = gfx::Size(kImgWidth, kImgHeight);
720 scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalYuvData(
721 PIXEL_FORMAT_YUV420P12, size, gfx::Rect(size), size, y_stride, uv_stride,
722 uv_stride, y_data, u_data, v_data, base::TimeDelta());
724 std::unique_ptr<uint32_t[]> rgba =
725 std::make_unique<uint32_t[]>(kImgWidth * kImgHeight);
726 PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
727 frame.get(), rgba.get(), frame->visible_rect().width() * 4,
728 /*premultiply_alpha=*/true);
729 for (int i = 0; i < kImgHeight; ++i) {
730 for (int j = 0; j < kImgWidth; ++j) {
731 EXPECT_EQ(rgba[i * kImgWidth + j], 0xffffffff);
736 TEST_F(PaintCanvasVideoRendererTest, I420WithFilters) {
737 // Allocate the Y, U, V planes for a 4x4 8-bit YUV 4:2:0 image. Note that
738 // there are no padding bytes after each row.
739 constexpr int kImgWidth = 4;
740 constexpr int kImgHeight = 4;
741 constexpr int kUvWidth = (kImgWidth + 1) / 2;
742 constexpr int kUvHeight = (kImgHeight + 1) / 2;
743 std::unique_ptr<uint8_t[]> y_plane =
744 std::make_unique<uint8_t[]>(kImgWidth * kImgHeight);
745 std::unique_ptr<uint8_t[]> u_plane =
746 std::make_unique<uint8_t[]>(kUvWidth * kUvHeight);
747 std::unique_ptr<uint8_t[]> v_plane =
748 std::make_unique<uint8_t[]>(kUvWidth * kUvHeight);
749 // In the JPEG color space (K_R = 0.299, K_B = 0.114, full range), red
750 // (R = 255, G = 0, B = 0) is Y = 76, U = 85, V = 255.
752 // Set Y to 76 for all pixels.
753 memset(y_plane.get(), 76, kImgWidth * kImgHeight);
754 // Set U = 85 and V = 255 for the upperleft pixel. Then vary U and V with a
755 // linear, diagonal slope over the UV planes with a step size of 4 and -4,
758 // The full U plane is
763 // The subsampled U plane is
767 // The full V plane is
772 // The subsampled V plane is
775 for (int i = 0; i < kUvHeight; ++i) {
776 for (int j = 0; j < kUvWidth; ++j) {
777 u_plane[i * kUvWidth + j] = 89 + 8 * i + 8 * j;
778 v_plane[i * kUvWidth + j] = 251 - 8 * i - 8 * j;
782 auto size = gfx::Size(kImgWidth, kImgHeight);
783 scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalYuvData(
784 PIXEL_FORMAT_I420, size, gfx::Rect(size), size, kImgWidth, kUvWidth,
785 kUvWidth, y_plane.get(), u_plane.get(), v_plane.get(), base::TimeDelta());
786 frame->set_color_space(gfx::ColorSpace::CreateJpeg());
788 std::unique_ptr<uint32_t[]> rgba =
789 std::make_unique<uint32_t[]>(kImgWidth * kImgHeight);
791 // First convert with kFilterNone (nearest neighbor).
792 PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
793 frame.get(), rgba.get(), frame->visible_rect().width() * 4,
794 /*premultiply_alpha=*/true);
796 // The pixel at coordinates (1, 1) will have U = 89 and V = 251 if nearest
797 // neighbor is used. (The correct values are U = 93 and V = 247.)
800 uint32_t color = rgba[i * kImgWidth + j];
801 EXPECT_EQ(SkGetPackedA32(color), 255u);
802 EXPECT_EQ(SkGetPackedR32(color), 249u);
803 EXPECT_EQ(SkGetPackedG32(color), 1u);
804 EXPECT_EQ(SkGetPackedB32(color), 7u);
805 // The pixel at coordinates (2, 2) will have U = 105 and V = 235 if nearest
806 // neighbor is used. (The correct values are U = 101 and V = 239.)
809 color = rgba[i * kImgWidth + j];
810 EXPECT_EQ(SkGetPackedA32(color), 255u);
811 EXPECT_EQ(SkGetPackedR32(color), 226u);
812 EXPECT_EQ(SkGetPackedG32(color), 7u);
813 EXPECT_EQ(SkGetPackedB32(color), 35u);
815 // Then convert with kFilterBilinear (bilinear interpolation).
816 PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
817 frame.get(), rgba.get(), frame->visible_rect().width() * 4,
818 /*premultiply_alpha=*/true, PaintCanvasVideoRenderer::kFilterBilinear);
820 // The pixel at coordinates (1, 1) will have the correct values U = 93 and
821 // V = 247 if bilinear interpolation is used.
824 color = rgba[i * kImgWidth + j];
825 EXPECT_EQ(SkGetPackedA32(color), 255u);
826 EXPECT_EQ(SkGetPackedR32(color), 243u);
827 EXPECT_EQ(SkGetPackedG32(color), 2u);
828 EXPECT_EQ(SkGetPackedB32(color), 14u);
829 // The pixel at coordinates (2, 2) will have the correct values U = 101 and
830 // V = 239 if bilinear interpolation is used.
833 color = rgba[i * kImgWidth + j];
834 EXPECT_EQ(SkGetPackedA32(color), 255u);
835 EXPECT_EQ(SkGetPackedR32(color), 232u);
836 EXPECT_EQ(SkGetPackedG32(color), 5u);
837 EXPECT_EQ(SkGetPackedB32(color), 28u);
841 class TestGLES2Interface : public gpu::gles2::GLES2InterfaceStub {
843 void GenTextures(GLsizei n, GLuint* textures) override {
848 void TexImage2D(GLenum target,
850 GLint internalformat,
856 const void* pixels) override {
857 if (!teximage2d_callback_.is_null()) {
858 teximage2d_callback_.Run(target, level, internalformat, width, height,
859 border, format, type, pixels);
863 void TexSubImage2D(GLenum target,
871 const void* pixels) override {
872 if (!texsubimage2d_callback_.is_null()) {
873 texsubimage2d_callback_.Run(target, level, xoffset, yoffset, width,
874 height, format, type, pixels);
878 base::RepeatingCallback<void(GLenum target,
880 GLint internalformat,
887 teximage2d_callback_;
889 base::RepeatingCallback<void(GLenum target,
898 texsubimage2d_callback_;
901 void MailboxHoldersReleased(const gpu::SyncToken& sync_token) {}
904 // Test that PaintCanvasVideoRenderer::Paint doesn't crash when GrContext is
905 // unable to wrap a video frame texture (eg due to being abandoned).
906 TEST_F(PaintCanvasVideoRendererTest, ContextLost) {
907 auto context_provider = viz::TestContextProvider::Create();
908 context_provider->BindToCurrentSequence();
909 context_provider->GrContext()->abandonContext();
911 cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
913 gfx::Size size(kWidth, kHeight);
914 gpu::MailboxHolder holders[VideoFrame::kMaxPlanes] = {
915 gpu::MailboxHolder(gpu::Mailbox::GenerateForSharedImage(),
916 gpu::SyncToken(), GL_TEXTURE_RECTANGLE_ARB)};
917 auto video_frame = VideoFrame::WrapNativeTextures(
918 PIXEL_FORMAT_NV12, holders, base::BindOnce(MailboxHoldersReleased), size,
919 gfx::Rect(size), size, kNoTimestamp);
921 cc::PaintFlags flags;
922 flags.setFilterQuality(cc::PaintFlags::FilterQuality::kLow);
923 renderer_.Paint(std::move(video_frame), &canvas, kNaturalRect, flags,
924 kNoTransformation, context_provider.get());
927 void EmptyCallback(const gpu::SyncToken& sync_token) {}
929 TEST_F(PaintCanvasVideoRendererTest, CorrectFrameSizeToVisibleRect) {
930 constexpr int fWidth{16}, fHeight{16};
932 SkImageInfo::MakeN32(fWidth, fHeight, kOpaque_SkAlphaType);
934 cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
936 gfx::Size coded_size(fWidth, fHeight);
937 gfx::Size visible_size(fWidth / 2, fHeight / 2);
939 uint8_t memory[fWidth * fHeight * 2] = {0};
941 auto video_frame = media::VideoFrame::WrapExternalData(
942 media::PIXEL_FORMAT_Y16, coded_size, gfx::Rect(visible_size),
943 visible_size, &memory[0], fWidth * fHeight * 2, base::Milliseconds(4));
945 gfx::RectF visible_rect(visible_size.width(), visible_size.height());
946 cc::PaintFlags flags;
947 renderer_.Paint(std::move(video_frame), &canvas, visible_rect, flags,
948 kNoTransformation, nullptr);
950 EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().width());
951 EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().height());
954 TEST_F(PaintCanvasVideoRendererTest, TexImage2D_Y16_RGBA32F) {
955 // Create test frame.
956 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
957 const int offset_x = 3;
958 const int offset_y = 5;
959 const int width = 16;
960 const int height = 16;
961 const int stride = width + offset_x;
962 const size_t byte_size = stride * (height + offset_y) * 2;
963 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
964 static_cast<unsigned char*>(base::AlignedAlloc(
965 byte_size, media::VideoFrame::kFrameAddressAlignment)));
966 const gfx::Rect rect(offset_x, offset_y, width, height);
968 CreateTestY16Frame(gfx::Size(stride, offset_y + height), rect,
969 memory.get(), cropped_frame()->timestamp());
971 TestGLES2Interface gles2;
972 // Bind the texImage2D callback to verify the uint16 to float32 conversion.
973 gles2.teximage2d_callback_ =
974 base::BindRepeating([](GLenum target, GLint level, GLint internalformat,
975 GLsizei width, GLsizei height, GLint border,
976 GLenum format, GLenum type, const void* pixels) {
977 EXPECT_EQ(static_cast<unsigned>(GL_FLOAT), type);
978 EXPECT_EQ(static_cast<unsigned>(GL_RGBA), format);
979 EXPECT_EQ(GL_RGBA, internalformat);
980 EXPECT_EQ(0, border);
981 EXPECT_EQ(16, width);
982 EXPECT_EQ(16, height);
983 EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
984 const float* data = static_cast<const float*>(pixels);
985 for (int j = 0; j < height; j++) {
986 for (int i = 0; i < width; i++) {
987 const int value = i + (height - j - 1) * width; // flip_y is true.
988 float expected_value =
989 (((value & 0xFF) << 8) | (~value & 0xFF)) / 65535.f;
990 EXPECT_EQ(expected_value, data[(i + j * width) * 4]);
991 EXPECT_EQ(expected_value, data[(i + j * width) * 4 + 1]);
992 EXPECT_EQ(expected_value, data[(i + j * width) * 4 + 2]);
993 EXPECT_EQ(1.0f, data[(i + j * width) * 4 + 3]);
997 PaintCanvasVideoRenderer::TexImage2D(
998 GL_TEXTURE_2D, 0, &gles2, gpu::Capabilities(), video_frame.get(), 0,
999 GL_RGBA, GL_RGBA, GL_FLOAT, true /*flip_y*/, true);
1002 TEST_F(PaintCanvasVideoRendererTest, TexSubImage2D_Y16_R32F) {
1003 // Create test frame.
1004 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
1005 const int offset_x = 3;
1006 const int offset_y = 5;
1007 const int width = 16;
1008 const int height = 16;
1009 const int stride = width + offset_x;
1010 const size_t byte_size = stride * (height + offset_y) * 2;
1011 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
1012 static_cast<unsigned char*>(base::AlignedAlloc(
1013 byte_size, media::VideoFrame::kFrameAddressAlignment)));
1014 const gfx::Rect rect(offset_x, offset_y, width, height);
1016 CreateTestY16Frame(gfx::Size(stride, offset_y + height), rect,
1017 memory.get(), cropped_frame()->timestamp());
1019 TestGLES2Interface gles2;
1020 // Bind the texImage2D callback to verify the uint16 to float32 conversion.
1021 gles2.texsubimage2d_callback_ =
1022 base::BindRepeating([](GLenum target, GLint level, GLint xoffset,
1023 GLint yoffset, GLsizei width, GLsizei height,
1024 GLenum format, GLenum type, const void* pixels) {
1025 EXPECT_EQ(static_cast<unsigned>(GL_FLOAT), type);
1026 EXPECT_EQ(static_cast<unsigned>(GL_RED), format);
1027 EXPECT_EQ(2, xoffset);
1028 EXPECT_EQ(1, yoffset);
1029 EXPECT_EQ(16, width);
1030 EXPECT_EQ(16, height);
1031 EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
1032 const float* data = static_cast<const float*>(pixels);
1033 for (int j = 0; j < height; j++) {
1034 for (int i = 0; i < width; i++) {
1035 const int value = i + j * width; // flip_y is false.
1036 float expected_value =
1037 (((value & 0xFF) << 8) | (~value & 0xFF)) / 65535.f;
1038 EXPECT_EQ(expected_value, data[(i + j * width)]);
1042 PaintCanvasVideoRenderer::TexSubImage2D(
1043 GL_TEXTURE_2D, &gles2, video_frame.get(), 0, GL_RED, GL_FLOAT,
1044 2 /*xoffset*/, 1 /*yoffset*/, false /*flip_y*/, true);
1047 // Fixture for tests that require a GL context.
1048 class PaintCanvasVideoRendererWithGLTest : public testing::Test {
1050 using GetColorCallback = base::RepeatingCallback<SkColor(int, int)>;
1052 void SetUp() override {
1053 display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
1054 enable_pixels_.emplace();
1055 media_context_ = base::MakeRefCounted<viz::TestInProcessContextProvider>(
1056 viz::TestContextType::kGpuRaster, /*support_locking=*/false);
1057 gpu::ContextResult result = media_context_->BindToCurrentSequence();
1058 ASSERT_EQ(result, gpu::ContextResult::kSuccess);
1060 gles2_context_ = base::MakeRefCounted<viz::TestInProcessContextProvider>(
1061 viz::TestContextType::kGLES2, /*support_locking=*/false);
1062 result = gles2_context_->BindToCurrentSequence();
1063 ASSERT_EQ(result, gpu::ContextResult::kSuccess);
1065 destination_context_ =
1066 base::MakeRefCounted<viz::TestInProcessContextProvider>(
1067 viz::TestContextType::kGLES2, /*support_locking=*/false);
1068 result = destination_context_->BindToCurrentSequence();
1069 ASSERT_EQ(result, gpu::ContextResult::kSuccess);
1070 cropped_frame_ = CreateCroppedFrame();
1073 void TearDown() override {
1074 renderer_.ResetCache();
1075 destination_context_.reset();
1076 gles2_context_.reset();
1077 media_context_.reset();
1078 enable_pixels_.reset();
1079 viz::TestGpuServiceHolder::ResetInstance();
1080 gl::GLSurfaceTestSupport::ShutdownGL(display_);
1083 // Uses CopyVideoFrameTexturesToGLTexture to copy |frame| into a GL texture,
1084 // reads back its contents, and runs |check_pixels| to validate it.
1085 template <class CheckPixels>
1086 void CopyVideoFrameTexturesAndCheckPixels(scoped_refptr<VideoFrame> frame,
1087 CheckPixels check_pixels) {
1088 auto* destination_gl = destination_context_->ContextGL();
1089 DCHECK(destination_gl);
1090 GLenum target = GL_TEXTURE_2D;
1092 destination_gl->GenTextures(1, &texture);
1093 destination_gl->BindTexture(target, texture);
1095 renderer_.CopyVideoFrameTexturesToGLTexture(
1096 media_context_.get(), destination_gl,
1097 destination_context_->ContextCapabilities(), frame, target, texture,
1098 GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
1099 false /* flip_y */);
1101 gfx::Size expected_size = frame->visible_rect().size();
1103 std::unique_ptr<uint8_t[]> pixels =
1104 ReadbackTexture(destination_gl, texture, expected_size);
1105 destination_gl->DeleteTextures(1, &texture);
1107 auto get_color = base::BindRepeating(
1108 [](uint8_t* pixels, const gfx::Size& size, int x, int y) {
1109 uint8_t* p = pixels + (size.width() * y + x) * 4;
1110 return SkColorSetARGB(p[3], p[0], p[1], p[2]);
1112 pixels.get(), expected_size);
1113 check_pixels(get_color);
1116 // Uses Copy to paint |frame| into a bitmap-backed canvas, then
1117 // runs |check_pixels| to validate the contents of the canvas.
1118 template <class CheckPixels>
1119 void PaintVideoFrameAndCheckPixels(scoped_refptr<VideoFrame> frame,
1120 CheckPixels check_pixels) {
1121 gfx::Size expected_size = frame->visible_rect().size();
1123 AllocBitmap(expected_size.width(), expected_size.height());
1124 cc::SkiaPaintCanvas canvas(bitmap);
1125 canvas.clear(SkColors::kGray);
1126 renderer_.Copy(frame, &canvas, media_context_.get());
1128 auto get_color = base::BindRepeating(
1129 [](SkBitmap* bitmap, int x, int y) { return bitmap->getColor(x, y); },
1131 check_pixels(get_color);
1134 // Creates a cropped RGBA VideoFrame. |closure| is run once the shared images
1135 // backing the VideoFrame have been destroyed.
1136 scoped_refptr<VideoFrame> CreateTestRGBAFrame(base::OnceClosure closure) {
1137 return CreateSharedImageRGBAFrame(gles2_context_, gfx::Size(16, 8),
1138 gfx::Rect(3, 3, 12, 4),
1139 std::move(closure));
1142 // Checks that the contents of a texture/canvas match the expectations for the
1143 // cropped RGBA frame above. |get_color| is a callback that returns the actual
1144 // color at a given pixel location.
1145 static void CheckRGBAFramePixels(GetColorCallback get_color) {
1146 EXPECT_EQ(SK_ColorBLACK, get_color.Run(0, 0));
1147 EXPECT_EQ(SK_ColorRED, get_color.Run(1, 0));
1148 EXPECT_EQ(SK_ColorRED, get_color.Run(4, 0));
1149 EXPECT_EQ(SK_ColorGREEN, get_color.Run(5, 0));
1150 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(9, 0));
1151 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(11, 0));
1152 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 1));
1153 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 3));
1154 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(1, 1));
1155 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(4, 1));
1156 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(1, 3));
1157 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(4, 3));
1158 EXPECT_EQ(SK_ColorCYAN, get_color.Run(5, 1));
1159 EXPECT_EQ(SK_ColorCYAN, get_color.Run(5, 3));
1160 EXPECT_EQ(SK_ColorWHITE, get_color.Run(9, 1));
1161 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 1));
1162 EXPECT_EQ(SK_ColorWHITE, get_color.Run(9, 3));
1163 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 3));
1166 // Creates a cropped I420 VideoFrame. |closure| is run once the shared images
1167 // backing the VideoFrame have been destroyed.
1168 scoped_refptr<VideoFrame> CreateTestI420Frame(base::OnceClosure closure) {
1169 return CreateSharedImageI420Frame(gles2_context_, gfx::Size(16, 8),
1170 gfx::Rect(2, 2, 12, 4),
1171 std::move(closure));
1173 // Creates a cropped I420 VideoFrame. |closure| is run once the shared images
1174 // backing the VideoFrame have been destroyed.
1175 scoped_refptr<VideoFrame> CreateTestI420FrameNotSubset(
1176 base::OnceClosure closure) {
1177 return CreateSharedImageI420Frame(gles2_context_, gfx::Size(16, 8),
1178 gfx::Rect(0, 0, 16, 8),
1179 std::move(closure));
1182 // Checks that the contents of a texture/canvas match the expectations for the
1183 // cropped I420 frame above. |get_color| is a callback that returns the actual
1184 // color at a given pixel location.
1185 static void CheckI420FramePixels(GetColorCallback get_color) {
1186 // Avoid checking around the "seams" where subsamples may be interpolated.
1187 EXPECT_EQ(SK_ColorBLACK, get_color.Run(0, 0));
1188 EXPECT_EQ(SK_ColorRED, get_color.Run(3, 0));
1189 EXPECT_EQ(SK_ColorRED, get_color.Run(4, 0));
1190 EXPECT_EQ(SK_ColorGREEN, get_color.Run(7, 0));
1191 EXPECT_EQ(SK_ColorGREEN, get_color.Run(8, 0));
1192 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(11, 0));
1193 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 3));
1194 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(3, 3));
1195 EXPECT_EQ(SK_ColorCYAN, get_color.Run(7, 3));
1196 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 3));
1199 // Checks that the contents of a texture/canvas match the expectations for the
1200 // cropped I420 frame above. |get_color| is a callback that returns the actual
1201 // color at a given pixel location.
1202 static void CheckI420FramePixelsNotSubset(GetColorCallback get_color) {
1203 // Avoid checking around the "seams" where subsamples may be interpolated.
1204 EXPECT_EQ(SK_ColorBLACK, get_color.Run(2, 2));
1205 EXPECT_EQ(SK_ColorRED, get_color.Run(5, 2));
1206 EXPECT_EQ(SK_ColorRED, get_color.Run(6, 2));
1207 EXPECT_EQ(SK_ColorGREEN, get_color.Run(9, 2));
1208 EXPECT_EQ(SK_ColorGREEN, get_color.Run(10, 2));
1209 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(13, 2));
1210 EXPECT_EQ(SK_ColorBLUE, get_color.Run(2, 5));
1211 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(5, 5));
1212 EXPECT_EQ(SK_ColorCYAN, get_color.Run(9, 5));
1213 EXPECT_EQ(SK_ColorWHITE, get_color.Run(13, 5));
1216 // Creates a cropped NV12 VideoFrame, or nullptr if the needed extension is
1217 // not available. |closure| is run once the shared images backing the
1218 // VideoFrame have been destroyed.
1219 scoped_refptr<VideoFrame> CreateTestNV12Frame(base::OnceClosure closure) {
1220 return CreateSharedImageNV12Frame(gles2_context_, gfx::Size(16, 8),
1221 gfx::Rect(2, 2, 12, 4),
1222 std::move(closure));
1225 // Checks that the contents of a texture/canvas match the expectations for the
1226 // cropped NV12 frame above. |get_color| is a callback that returns the actual
1227 // color at a given pixel location. Note that the expectations are the same as
1228 // for the I420 frame.
1229 static void CheckNV12FramePixels(GetColorCallback get_color) {
1230 CheckI420FramePixels(std::move(get_color));
1233 scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; }
1236 absl::optional<gl::DisableNullDrawGLBindings> enable_pixels_;
1237 scoped_refptr<viz::TestInProcessContextProvider> media_context_;
1238 scoped_refptr<viz::TestInProcessContextProvider> gles2_context_;
1239 scoped_refptr<viz::TestInProcessContextProvider> destination_context_;
1241 PaintCanvasVideoRenderer renderer_;
1242 scoped_refptr<VideoFrame> cropped_frame_;
1243 base::test::TaskEnvironment task_environment_;
1244 raw_ptr<gl::GLDisplay> display_ = nullptr;
1247 TEST_F(PaintCanvasVideoRendererWithGLTest, CopyVideoFrameYUVDataToGLTexture) {
1248 auto* destination_gl = destination_context_->ContextGL();
1249 DCHECK(destination_gl);
1250 GLenum target = GL_TEXTURE_2D;
1252 destination_gl->GenTextures(1, &texture);
1253 destination_gl->BindTexture(target, texture);
1255 renderer_.CopyVideoFrameYUVDataToGLTexture(
1256 media_context_.get(), destination_gl,
1257 destination_context_->ContextCapabilities(), cropped_frame(), target,
1258 texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0,
1259 false /* premultiply_alpha */, false /* flip_y */);
1261 gfx::Size expected_size = cropped_frame()->visible_rect().size();
1263 std::unique_ptr<uint8_t[]> pixels =
1264 ReadbackTexture(destination_gl, texture, expected_size);
1265 auto get_color = ColorGetter(pixels.get(), expected_size);
1267 // Avoid checking around the seams.
1268 EXPECT_EQ(SK_ColorBLACK, get_color(0, 0));
1269 EXPECT_EQ(SK_ColorRED, get_color(3, 0));
1270 EXPECT_EQ(SK_ColorRED, get_color(7, 0));
1271 EXPECT_EQ(SK_ColorGREEN, get_color(0, 3));
1272 EXPECT_EQ(SK_ColorGREEN, get_color(0, 5));
1273 EXPECT_EQ(SK_ColorBLUE, get_color(3, 3));
1274 EXPECT_EQ(SK_ColorBLUE, get_color(7, 5));
1276 destination_gl->DeleteTextures(1, &texture);
1279 TEST_F(PaintCanvasVideoRendererWithGLTest,
1280 CopyVideoFrameYUVDataToGLTexture_FlipY) {
1281 auto* destination_gl = destination_context_->ContextGL();
1282 DCHECK(destination_gl);
1283 GLenum target = GL_TEXTURE_2D;
1285 destination_gl->GenTextures(1, &texture);
1286 destination_gl->BindTexture(target, texture);
1288 renderer_.CopyVideoFrameYUVDataToGLTexture(
1289 media_context_.get(), destination_gl,
1290 destination_context_->ContextCapabilities(), cropped_frame(), target,
1291 texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0,
1292 false /* premultiply_alpha */, true /* flip_y */);
1294 gfx::Size expected_size = cropped_frame()->visible_rect().size();
1296 std::unique_ptr<uint8_t[]> pixels =
1297 ReadbackTexture(destination_gl, texture, expected_size);
1298 auto get_color = ColorGetter(pixels.get(), expected_size);
1300 // Avoid checking around the seams.
1301 EXPECT_EQ(SK_ColorBLACK, get_color(0, 5));
1302 EXPECT_EQ(SK_ColorRED, get_color(3, 5));
1303 EXPECT_EQ(SK_ColorRED, get_color(7, 5));
1304 EXPECT_EQ(SK_ColorGREEN, get_color(0, 2));
1305 EXPECT_EQ(SK_ColorGREEN, get_color(0, 0));
1306 EXPECT_EQ(SK_ColorBLUE, get_color(3, 2));
1307 EXPECT_EQ(SK_ColorBLUE, get_color(7, 0));
1309 destination_gl->DeleteTextures(1, &texture);
1312 // Checks that we correctly copy a RGBA shared image VideoFrame when using
1313 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
1314 #if BUILDFLAG(IS_IOS) && BUILDFLAG(SKIA_USE_METAL)
1315 // TODO(crbug.com/1476625): R and B channels are currently inverted with
1316 // SkiaGraphite and metal.
1317 #define MAYBE_CopyVideoFrameTexturesToGLTextureRGBA \
1318 DISABLED_CopyVideoFrameTexturesToGLTextureRGBA
1320 #define MAYBE_CopyVideoFrameTexturesToGLTextureRGBA \
1321 CopyVideoFrameTexturesToGLTextureRGBA
1322 #endif // BUILDFLAG(IS_IOS) && BUILDFLAG(SKIA_USE_METAL)
1323 TEST_F(PaintCanvasVideoRendererWithGLTest,
1324 MAYBE_CopyVideoFrameTexturesToGLTextureRGBA) {
1325 base::RunLoop run_loop;
1326 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1328 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckRGBAFramePixels);
1334 // Checks that we correctly copy a RGBA shared image VideoFrame that needs read
1335 // lock fences, when using CopyVideoFrameYUVDataToGLTexture, including correct
1337 #if BUILDFLAG(IS_IOS) && BUILDFLAG(SKIA_USE_METAL)
1338 // TODO(crbug.com/1476625): R and B channels are currently inverted with
1339 // SkiaGraphite and metal.
1340 #define MAYBE_CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence \
1341 DISABLED_CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence
1343 #define MAYBE_CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence \
1344 CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence
1345 #endif // BUILDFLAG(IS_IOS) && BUILDFLAG(SKIA_USE_METAL)
1346 TEST_F(PaintCanvasVideoRendererWithGLTest,
1347 MAYBE_CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence) {
1348 base::RunLoop run_loop;
1349 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1350 frame->metadata().read_lock_fences_enabled = true;
1352 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckRGBAFramePixels);
1358 // Checks that we correctly paint a RGBA shared image VideoFrame, including
1359 // correct cropping.
1360 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintRGBA) {
1361 base::RunLoop run_loop;
1362 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1364 PaintVideoFrameAndCheckPixels(frame, &CheckRGBAFramePixels);
1370 // Checks that we correctly copy an I420 shared image VideoFrame when using
1371 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
1372 TEST_F(PaintCanvasVideoRendererWithGLTest,
1373 CopyVideoFrameTexturesToGLTextureI420) {
1374 base::RunLoop run_loop;
1375 scoped_refptr<VideoFrame> frame = CreateTestI420Frame(run_loop.QuitClosure());
1377 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckI420FramePixels);
1383 // Checks that we correctly paint a I420 shared image VideoFrame, including
1384 // correct cropping.
1385 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintI420) {
1386 base::RunLoop run_loop;
1387 scoped_refptr<VideoFrame> frame = CreateTestI420Frame(run_loop.QuitClosure());
1389 PaintVideoFrameAndCheckPixels(frame, &CheckI420FramePixels);
1395 // Checks that we correctly paint a I420 shared image VideoFrame, including
1396 // correct cropping.
1397 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintI420NotSubset) {
1398 base::RunLoop run_loop;
1399 scoped_refptr<VideoFrame> frame =
1400 CreateTestI420FrameNotSubset(run_loop.QuitClosure());
1402 PaintVideoFrameAndCheckPixels(frame, &CheckI420FramePixelsNotSubset);
1408 // Checks that we correctly copy a NV12 shared image VideoFrame when using
1409 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
1410 TEST_F(PaintCanvasVideoRendererWithGLTest,
1411 CopyVideoFrameTexturesToGLTextureNV12) {
1412 base::RunLoop run_loop;
1413 scoped_refptr<VideoFrame> frame = CreateTestNV12Frame(run_loop.QuitClosure());
1415 LOG(ERROR) << "GL_EXT_texture_rg not supported, skipping NV12 test";
1419 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckNV12FramePixels);
1425 // Checks that we correctly paint a NV12 shared image VideoFrame, including
1426 // correct cropping.
1427 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintNV12) {
1428 base::RunLoop run_loop;
1429 scoped_refptr<VideoFrame> frame = CreateTestNV12Frame(run_loop.QuitClosure());
1431 LOG(ERROR) << "GL_EXT_texture_rg not supported, skipping NV12 test";
1435 PaintVideoFrameAndCheckPixels(frame, &CheckNV12FramePixels);
1441 } // namespace media