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 <GLES3/gl3.h>
6 #include <stdint.h>
7
8 #include "base/bind.h"
9 #include "base/macros.h"
10 #include "base/memory/aligned_memory.h"
11 #include "base/sys_byteorder.h"
12 #include "base/test/task_environment.h"
13 #include "cc/paint/paint_flags.h"
14 #include "cc/paint/skia_paint_canvas.h"
15 #include "components/viz/common/gpu/context_provider.h"
16 #include "components/viz/test/test_context_provider.h"
17 #include "components/viz/test/test_gpu_service_holder.h"
18 #include "components/viz/test/test_in_process_context_provider.h"
19 #include "gpu/GLES2/gl2extchromium.h"
20 #include "gpu/command_buffer/client/gles2_interface_stub.h"
21 #include "gpu/command_buffer/common/capabilities.h"
22 #include "gpu/command_buffer/common/shared_image_usage.h"
23 #include "gpu/config/gpu_feature_info.h"
24 #include "media/base/timestamp_constants.h"
25 #include "media/base/video_frame.h"
26 #include "media/base/video_util.h"
27 #include "media/renderers/paint_canvas_video_renderer.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/libyuv/include/libyuv/convert.h"
30 #include "third_party/libyuv/include/libyuv/scale.h"
31 #include "third_party/skia/include/core/SkImage.h"
32 #include "third_party/skia/include/core/SkRefCnt.h"
33 #include "third_party/skia/include/core/SkSurface.h"
34 #include "third_party/skia/include/gpu/GrContext.h"
35 #include "ui/gfx/geometry/rect_f.h"
36 #include "ui/gl/gl_implementation.h"
37 #include "ui/gl/test/gl_surface_test_support.h"
38
39 using media::VideoFrame;
40
41 namespace media {
42
43 static const int kWidth = 320;
44 static const int kHeight = 240;
45 static const gfx::RectF kNaturalRect(kWidth, kHeight);
46
47 // Generate frame pixels to provided |external_memory| and wrap it as frame.
CreateTestY16Frame(const gfx::Size & coded_size,const gfx::Rect & visible_rect,void * external_memory,base::TimeDelta timestamp)48 scoped_refptr<VideoFrame> CreateTestY16Frame(const gfx::Size& coded_size,
49 const gfx::Rect& visible_rect,
50 void* external_memory,
51 base::TimeDelta timestamp) {
52 const int offset_x = visible_rect.x();
53 const int offset_y = visible_rect.y();
54 const int stride = coded_size.width();
55 const size_t byte_size = stride * coded_size.height() * 2;
56
57 // In the visible rect, fill upper byte with [0-255] and lower with [255-0].
58 uint16_t* data = static_cast<uint16_t*>(external_memory);
59 for (int j = 0; j < visible_rect.height(); j++) {
60 for (int i = 0; i < visible_rect.width(); i++) {
61 const int value = i + j * visible_rect.width();
62 data[(stride * (j + offset_y)) + i + offset_x] =
63 ((value & 0xFF) << 8) | (~value & 0xFF);
64 }
65 }
66
67 return media::VideoFrame::WrapExternalData(
68 media::PIXEL_FORMAT_Y16, coded_size, visible_rect, visible_rect.size(),
69 static_cast<uint8_t*>(external_memory), byte_size, timestamp);
70 }
71
72 // Destroys a list of shared images after a sync token is passed. Also runs
73 // |callback|.
DestroySharedImages(scoped_refptr<viz::ContextProvider> context_provider,std::vector<gpu::Mailbox> mailboxes,base::OnceClosure callback,const gpu::SyncToken & sync_token)74 static void DestroySharedImages(
75 scoped_refptr<viz::ContextProvider> context_provider,
76 std::vector<gpu::Mailbox> mailboxes,
77 base::OnceClosure callback,
78 const gpu::SyncToken& sync_token) {
79 auto* sii = context_provider->SharedImageInterface();
80 for (const auto& mailbox : mailboxes)
81 sii->DestroySharedImage(sync_token, mailbox);
82 std::move(callback).Run();
83 }
84
85 // Creates a video frame from a set of shared images with a common texture
86 // target and sync token.
CreateSharedImageFrame(scoped_refptr<viz::ContextProvider> context_provider,VideoPixelFormat format,std::vector<gpu::Mailbox> mailboxes,const gpu::SyncToken & sync_token,GLenum texture_target,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,base::TimeDelta timestamp,base::OnceClosure destroyed_callback)87 static scoped_refptr<VideoFrame> CreateSharedImageFrame(
88 scoped_refptr<viz::ContextProvider> context_provider,
89 VideoPixelFormat format,
90 std::vector<gpu::Mailbox> mailboxes,
91 const gpu::SyncToken& sync_token,
92 GLenum texture_target,
93 const gfx::Size& coded_size,
94 const gfx::Rect& visible_rect,
95 const gfx::Size& natural_size,
96 base::TimeDelta timestamp,
97 base::OnceClosure destroyed_callback) {
98 gpu::MailboxHolder mailboxes_for_frame[VideoFrame::kMaxPlanes] = {};
99 size_t i = 0;
100 for (const auto& mailbox : mailboxes) {
101 mailboxes_for_frame[i++] =
102 gpu::MailboxHolder(mailbox, sync_token, texture_target);
103 }
104 auto callback =
105 base::BindOnce(&DestroySharedImages, std::move(context_provider),
106 std::move(mailboxes), std::move(destroyed_callback));
107 return VideoFrame::WrapNativeTextures(format, mailboxes_for_frame,
108 std::move(callback), coded_size,
109 visible_rect, natural_size, timestamp);
110 }
111
112 // Upload pixels to a shared image using GL.
UploadPixels(gpu::gles2::GLES2Interface * gl,const gpu::Mailbox & mailbox,const gfx::Size & size,GLenum format,GLenum type,const uint8_t * data)113 static void UploadPixels(gpu::gles2::GLES2Interface* gl,
114 const gpu::Mailbox& mailbox,
115 const gfx::Size& size,
116 GLenum format,
117 GLenum type,
118 const uint8_t* data) {
119 GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
120 gl->BeginSharedImageAccessDirectCHROMIUM(
121 texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
122 gl->BindTexture(GL_TEXTURE_2D, texture);
123 gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), format,
124 type, data);
125 gl->EndSharedImageAccessDirectCHROMIUM(texture);
126 gl->DeleteTextures(1, &texture);
127 }
128
129 // Creates a shared image backed frame in RGBA format, with colors on the shared
130 // image mapped as follow.
131 // Bk | R | G | Y
132 // ---+---+---+---
133 // Bl | M | C | W
CreateSharedImageRGBAFrame(scoped_refptr<viz::ContextProvider> context_provider,const gfx::Size & coded_size,const gfx::Rect & visible_rect,base::OnceClosure destroyed_callback)134 static scoped_refptr<VideoFrame> CreateSharedImageRGBAFrame(
135 scoped_refptr<viz::ContextProvider> context_provider,
136 const gfx::Size& coded_size,
137 const gfx::Rect& visible_rect,
138 base::OnceClosure destroyed_callback) {
139 DCHECK_EQ(coded_size.width() % 4, 0);
140 DCHECK_EQ(coded_size.height() % 2, 0);
141 size_t pixels_size = coded_size.GetArea() * 4;
142 auto pixels = std::make_unique<uint8_t[]>(pixels_size);
143 size_t i = 0;
144 for (size_t block_y = 0; block_y < 2u; ++block_y) {
145 for (int y = 0; y < coded_size.height() / 2; ++y) {
146 for (size_t block_x = 0; block_x < 4u; ++block_x) {
147 for (int x = 0; x < coded_size.width() / 4; ++x) {
148 pixels[i++] = 0xffu * (block_x % 2); // R
149 pixels[i++] = 0xffu * (block_x / 2); // G
150 pixels[i++] = 0xffu * block_y; // B
151 pixels[i++] = 0xffu; // A
152 }
153 }
154 }
155 }
156 DCHECK_EQ(i, pixels_size);
157
158 auto* sii = context_provider->SharedImageInterface();
159 gpu::Mailbox mailbox =
160 sii->CreateSharedImage(viz::ResourceFormat::RGBA_8888, coded_size,
161 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
162 auto* gl = context_provider->ContextGL();
163 gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
164 UploadPixels(gl, mailbox, coded_size, GL_RGBA, GL_UNSIGNED_BYTE,
165 pixels.get());
166 gpu::SyncToken sync_token;
167 gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
168
169 return CreateSharedImageFrame(
170 std::move(context_provider), VideoPixelFormat::PIXEL_FORMAT_ABGR,
171 {mailbox}, sync_token, GL_TEXTURE_2D, coded_size, visible_rect,
172 visible_rect.size(), base::TimeDelta::FromSeconds(1),
173 std::move(destroyed_callback));
174 }
175
176 static constexpr const uint8_t kYuvColors[8][3] = {
177 {0x00, 0x80, 0x80}, // Black
178 {0x4c, 0x54, 0xff}, // Red
179 {0x95, 0x2b, 0x15}, // Green
180 {0xe1, 0x00, 0x94}, // Yellow
181 {0x1d, 0xff, 0x6b}, // Blue
182 {0x69, 0xd3, 0xec}, // Magenta
183 {0xb3, 0xaa, 0x00}, // Cyan
184 {0xff, 0x80, 0x80}, // White
185 };
186
187 // Creates a shared image backed frame in I420 format, with colors mapped
188 // exactly like CreateSharedImageRGBAFrame above, noting that subsamples may get
189 // interpolated leading to inconsistent colors around the "seams".
CreateSharedImageI420Frame(scoped_refptr<viz::ContextProvider> context_provider,const gfx::Size & coded_size,const gfx::Rect & visible_rect,base::OnceClosure destroyed_callback)190 static scoped_refptr<VideoFrame> CreateSharedImageI420Frame(
191 scoped_refptr<viz::ContextProvider> context_provider,
192 const gfx::Size& coded_size,
193 const gfx::Rect& visible_rect,
194 base::OnceClosure destroyed_callback) {
195 DCHECK_EQ(coded_size.width() % 8, 0);
196 DCHECK_EQ(coded_size.height() % 4, 0);
197 gfx::Size uv_size(coded_size.width() / 2, coded_size.height() / 2);
198 size_t y_pixels_size = coded_size.GetArea();
199 size_t uv_pixels_size = uv_size.GetArea();
200 auto y_pixels = std::make_unique<uint8_t[]>(y_pixels_size);
201 auto u_pixels = std::make_unique<uint8_t[]>(uv_pixels_size);
202 auto v_pixels = std::make_unique<uint8_t[]>(uv_pixels_size);
203 size_t y_i = 0;
204 size_t uv_i = 0;
205 for (size_t block_y = 0; block_y < 2u; ++block_y) {
206 for (int y = 0; y < coded_size.height() / 2; ++y) {
207 for (size_t block_x = 0; block_x < 4u; ++block_x) {
208 size_t color_index = block_x + block_y * 4;
209 const uint8_t* yuv = kYuvColors[color_index];
210 for (int x = 0; x < coded_size.width() / 4; ++x) {
211 y_pixels[y_i++] = yuv[0];
212 if ((x % 2) && (y % 2)) {
213 u_pixels[uv_i] = yuv[1];
214 v_pixels[uv_i++] = yuv[2];
215 }
216 }
217 }
218 }
219 }
220 DCHECK_EQ(y_i, y_pixels_size);
221 DCHECK_EQ(uv_i, uv_pixels_size);
222
223 auto* sii = context_provider->SharedImageInterface();
224 gpu::Mailbox y_mailbox =
225 sii->CreateSharedImage(viz::ResourceFormat::LUMINANCE_8, coded_size,
226 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
227 gpu::Mailbox u_mailbox =
228 sii->CreateSharedImage(viz::ResourceFormat::LUMINANCE_8, uv_size,
229 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
230 gpu::Mailbox v_mailbox =
231 sii->CreateSharedImage(viz::ResourceFormat::LUMINANCE_8, uv_size,
232 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
233 auto* gl = context_provider->ContextGL();
234 gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
235 UploadPixels(gl, y_mailbox, coded_size, GL_LUMINANCE, GL_UNSIGNED_BYTE,
236 y_pixels.get());
237 UploadPixels(gl, u_mailbox, uv_size, GL_LUMINANCE, GL_UNSIGNED_BYTE,
238 u_pixels.get());
239 UploadPixels(gl, v_mailbox, uv_size, GL_LUMINANCE, GL_UNSIGNED_BYTE,
240 v_pixels.get());
241 gpu::SyncToken sync_token;
242 gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
243
244 return CreateSharedImageFrame(
245 std::move(context_provider), VideoPixelFormat::PIXEL_FORMAT_I420,
246 {y_mailbox, u_mailbox, v_mailbox}, sync_token, GL_TEXTURE_2D, coded_size,
247 visible_rect, visible_rect.size(), base::TimeDelta::FromSeconds(1),
248 std::move(destroyed_callback));
249 }
250
251 // Creates a shared image backed frame in NV12 format, with colors mapped
252 // exactly like CreateSharedImageRGBAFrame above.
253 // This will return nullptr if the necessary extension is not available for NV12
254 // support.
CreateSharedImageNV12Frame(scoped_refptr<viz::ContextProvider> context_provider,const gfx::Size & coded_size,const gfx::Rect & visible_rect,base::OnceClosure destroyed_callback)255 static scoped_refptr<VideoFrame> CreateSharedImageNV12Frame(
256 scoped_refptr<viz::ContextProvider> context_provider,
257 const gfx::Size& coded_size,
258 const gfx::Rect& visible_rect,
259 base::OnceClosure destroyed_callback) {
260 DCHECK_EQ(coded_size.width() % 8, 0);
261 DCHECK_EQ(coded_size.height() % 4, 0);
262 if (!context_provider->ContextCapabilities().texture_rg)
263 return {};
264 gfx::Size uv_size(coded_size.width() / 2, coded_size.height() / 2);
265 size_t y_pixels_size = coded_size.GetArea();
266 size_t uv_pixels_size = uv_size.GetArea() * 2;
267 auto y_pixels = std::make_unique<uint8_t[]>(y_pixels_size);
268 auto uv_pixels = std::make_unique<uint8_t[]>(uv_pixels_size);
269 size_t y_i = 0;
270 size_t uv_i = 0;
271 for (size_t block_y = 0; block_y < 2u; ++block_y) {
272 for (int y = 0; y < coded_size.height() / 2; ++y) {
273 for (size_t block_x = 0; block_x < 4u; ++block_x) {
274 size_t color_index = block_x + block_y * 4;
275 const uint8_t* yuv = kYuvColors[color_index];
276 for (int x = 0; x < coded_size.width() / 4; ++x) {
277 y_pixels[y_i++] = yuv[0];
278 if ((x % 2) && (y % 2)) {
279 uv_pixels[uv_i++] = yuv[1];
280 uv_pixels[uv_i++] = yuv[2];
281 }
282 }
283 }
284 }
285 }
286 DCHECK_EQ(y_i, y_pixels_size);
287 DCHECK_EQ(uv_i, uv_pixels_size);
288
289 auto* sii = context_provider->SharedImageInterface();
290 gpu::Mailbox y_mailbox =
291 sii->CreateSharedImage(viz::ResourceFormat::LUMINANCE_8, coded_size,
292 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
293 gpu::Mailbox uv_mailbox =
294 sii->CreateSharedImage(viz::ResourceFormat::RG_88, uv_size,
295 gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
296 auto* gl = context_provider->ContextGL();
297 gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
298 UploadPixels(gl, y_mailbox, coded_size, GL_LUMINANCE, GL_UNSIGNED_BYTE,
299 y_pixels.get());
300 UploadPixels(gl, uv_mailbox, uv_size, GL_RG, GL_UNSIGNED_BYTE,
301 uv_pixels.get());
302 gpu::SyncToken sync_token;
303 gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
304
305 return CreateSharedImageFrame(
306 std::move(context_provider), VideoPixelFormat::PIXEL_FORMAT_NV12,
307 {y_mailbox, uv_mailbox}, sync_token, GL_TEXTURE_2D, coded_size,
308 visible_rect, visible_rect.size(), base::TimeDelta::FromSeconds(1),
309 std::move(destroyed_callback));
310 }
311
312 // Readback the contents of a RGBA texture into an array of RGBA values.
ReadbackTexture(gpu::gles2::GLES2Interface * gl,GLuint texture,const gfx::Size & size)313 static std::unique_ptr<uint8_t[]> ReadbackTexture(
314 gpu::gles2::GLES2Interface* gl,
315 GLuint texture,
316 const gfx::Size& size) {
317 size_t pixel_count = size.width() * size.height();
318 GLuint fbo = 0;
319 gl->GenFramebuffers(1, &fbo);
320 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
321 gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
322 texture, 0);
323 auto pixels = std::make_unique<uint8_t[]>(pixel_count * 4);
324 uint8_t* raw_pixels = pixels.get();
325 gl->ReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
326 raw_pixels);
327 gl->DeleteFramebuffers(1, &fbo);
328 return pixels;
329 }
330
331 // Returns a functor that retrieves a SkColor for a given pixel, from raw RGBA
332 // data.
ColorGetter(uint8_t * pixels,const gfx::Size & size)333 static auto ColorGetter(uint8_t* pixels, const gfx::Size& size) {
334 return [pixels, size](size_t x, size_t y) {
335 uint8_t* p = pixels + (size.width() * y + x) * 4;
336 return SkColorSetARGB(p[3], p[0], p[1], p[2]);
337 };
338 }
339
340 class PaintCanvasVideoRendererTest : public testing::Test {
341 public:
342 enum Color {
343 kNone,
344 kRed,
345 kGreen,
346 kBlue,
347 };
348
349 PaintCanvasVideoRendererTest();
350 ~PaintCanvasVideoRendererTest() override;
351
352 // Paints to |canvas| using |renderer_| without any frame data.
353 void PaintWithoutFrame(cc::PaintCanvas* canvas);
354
355 // Paints the |video_frame| to the |canvas| using |renderer_|, setting the
356 // color of |video_frame| to |color| first.
357 void Paint(scoped_refptr<VideoFrame> video_frame,
358 cc::PaintCanvas* canvas,
359 Color color);
360 void PaintRotated(scoped_refptr<VideoFrame> video_frame,
361 cc::PaintCanvas* canvas,
362 const gfx::RectF& dest_rect,
363 Color color,
364 SkBlendMode mode,
365 VideoTransformation video_transformation);
366
367 void Copy(scoped_refptr<VideoFrame> video_frame, cc::PaintCanvas* canvas);
368
369 // Getters for various frame sizes.
natural_frame()370 scoped_refptr<VideoFrame> natural_frame() { return natural_frame_; }
larger_frame()371 scoped_refptr<VideoFrame> larger_frame() { return larger_frame_; }
smaller_frame()372 scoped_refptr<VideoFrame> smaller_frame() { return smaller_frame_; }
cropped_frame()373 scoped_refptr<VideoFrame> cropped_frame() { return cropped_frame_; }
374
375 // Standard canvas.
target_canvas()376 cc::PaintCanvas* target_canvas() { return &target_canvas_; }
bitmap()377 SkBitmap* bitmap() { return &bitmap_; }
378
379 protected:
380 PaintCanvasVideoRenderer renderer_;
381
382 scoped_refptr<VideoFrame> natural_frame_;
383 scoped_refptr<VideoFrame> larger_frame_;
384 scoped_refptr<VideoFrame> smaller_frame_;
385 scoped_refptr<VideoFrame> cropped_frame_;
386
387 SkBitmap bitmap_;
388 cc::SkiaPaintCanvas target_canvas_;
389 base::test::TaskEnvironment task_environment_;
390
391 DISALLOW_COPY_AND_ASSIGN(PaintCanvasVideoRendererTest);
392 };
393
AllocBitmap(int width,int height)394 static SkBitmap AllocBitmap(int width, int height) {
395 SkBitmap bitmap;
396 bitmap.allocPixels(SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType));
397 bitmap.eraseColor(0);
398 return bitmap;
399 }
400
PaintCanvasVideoRendererTest()401 PaintCanvasVideoRendererTest::PaintCanvasVideoRendererTest()
402 : natural_frame_(VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight))),
403 larger_frame_(
404 VideoFrame::CreateBlackFrame(gfx::Size(kWidth * 2, kHeight * 2))),
405 smaller_frame_(
406 VideoFrame::CreateBlackFrame(gfx::Size(kWidth / 2, kHeight / 2))),
407 cropped_frame_(
408 VideoFrame::CreateFrame(PIXEL_FORMAT_I420,
409 gfx::Size(16, 16),
410 gfx::Rect(6, 6, 8, 6),
411 gfx::Size(8, 6),
412 base::TimeDelta::FromMilliseconds(4))),
413 bitmap_(AllocBitmap(kWidth, kHeight)),
414 target_canvas_(bitmap_) {
415 // Give each frame a unique timestamp.
416 natural_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(1));
417 larger_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(2));
418 smaller_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(3));
419
420 // Make sure the cropped video frame's aspect ratio matches the output device.
421 // Update cropped_frame_'s crop dimensions if this is not the case.
422 EXPECT_EQ(cropped_frame()->visible_rect().width() * kHeight,
423 cropped_frame()->visible_rect().height() * kWidth);
424
425 // Fill in the cropped frame's entire data with colors:
426 //
427 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
428 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
429 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
430 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
431 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
432 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
433 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
434 // Bl Bl Bl Bl Bl Bl Bl Bl R R R R R R R R
435 // G G G G G G G G B B B B B B B B
436 // G G G G G G G G B B B B B B B B
437 // G G G G G G G G B B B B B B B B
438 // G G G G G G G G B B B B B B B B
439 // G G G G G G G G B B B B B B B B
440 // G G G G G G G G B B B B B B B B
441 // G G G G G G G G B B B B B B B B
442 // G G G G G G G G B B B B B B B B
443 //
444 // The visible crop of the frame (as set by its visible_rect_) has contents:
445 //
446 // Bl Bl R R R R R R
447 // Bl Bl R R R R R R
448 // G G B B B B B B
449 // G G B B B B B B
450 // G G B B B B B B
451 // G G B B B B B B
452 //
453 // Each color region in the cropped frame is on a 2x2 block granularity, to
454 // avoid sharing UV samples between regions.
455
456 static const uint8_t cropped_y_plane[] = {
457 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
458 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
459 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
460 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
461 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
462 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
463 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
464 0, 0, 0, 0, 0, 0, 0, 0, 76, 76, 76, 76, 76, 76, 76, 76,
465 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
466 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
467 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
468 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
469 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
470 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
471 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
472 149, 149, 149, 149, 149, 149, 149, 149, 29, 29, 29, 29, 29, 29, 29, 29,
473 };
474
475 static const uint8_t cropped_u_plane[] = {
476 128, 128, 128, 128, 84, 84, 84, 84, 128, 128, 128, 128, 84,
477 84, 84, 84, 128, 128, 128, 128, 84, 84, 84, 84, 128, 128,
478 128, 128, 84, 84, 84, 84, 43, 43, 43, 43, 255, 255, 255,
479 255, 43, 43, 43, 43, 255, 255, 255, 255, 43, 43, 43, 43,
480 255, 255, 255, 255, 43, 43, 43, 43, 255, 255, 255, 255,
481 };
482 static const uint8_t cropped_v_plane[] = {
483 128, 128, 128, 128, 255, 255, 255, 255, 128, 128, 128, 128, 255,
484 255, 255, 255, 128, 128, 128, 128, 255, 255, 255, 255, 128, 128,
485 128, 128, 255, 255, 255, 255, 21, 21, 21, 21, 107, 107, 107,
486 107, 21, 21, 21, 21, 107, 107, 107, 107, 21, 21, 21, 21,
487 107, 107, 107, 107, 21, 21, 21, 21, 107, 107, 107, 107,
488 };
489
490 libyuv::I420Copy(cropped_y_plane, 16, cropped_u_plane, 8, cropped_v_plane, 8,
491 cropped_frame()->data(VideoFrame::kYPlane),
492 cropped_frame()->stride(VideoFrame::kYPlane),
493 cropped_frame()->data(VideoFrame::kUPlane),
494 cropped_frame()->stride(VideoFrame::kUPlane),
495 cropped_frame()->data(VideoFrame::kVPlane),
496 cropped_frame()->stride(VideoFrame::kVPlane), 16, 16);
497 }
498
499 PaintCanvasVideoRendererTest::~PaintCanvasVideoRendererTest() = default;
500
PaintWithoutFrame(cc::PaintCanvas * canvas)501 void PaintCanvasVideoRendererTest::PaintWithoutFrame(cc::PaintCanvas* canvas) {
502 cc::PaintFlags flags;
503 flags.setFilterQuality(kLow_SkFilterQuality);
504 renderer_.Paint(nullptr, canvas, kNaturalRect, flags, kNoTransformation,
505 nullptr);
506 }
507
Paint(scoped_refptr<VideoFrame> video_frame,cc::PaintCanvas * canvas,Color color)508 void PaintCanvasVideoRendererTest::Paint(scoped_refptr<VideoFrame> video_frame,
509 cc::PaintCanvas* canvas,
510 Color color) {
511 PaintRotated(std::move(video_frame), canvas, kNaturalRect, color,
512 SkBlendMode::kSrcOver, kNoTransformation);
513 }
514
PaintRotated(scoped_refptr<VideoFrame> video_frame,cc::PaintCanvas * canvas,const gfx::RectF & dest_rect,Color color,SkBlendMode mode,VideoTransformation video_transformation)515 void PaintCanvasVideoRendererTest::PaintRotated(
516 scoped_refptr<VideoFrame> video_frame,
517 cc::PaintCanvas* canvas,
518 const gfx::RectF& dest_rect,
519 Color color,
520 SkBlendMode mode,
521 VideoTransformation video_transformation) {
522 switch (color) {
523 case kNone:
524 break;
525 case kRed:
526 media::FillYUV(video_frame.get(), 76, 84, 255);
527 break;
528 case kGreen:
529 media::FillYUV(video_frame.get(), 149, 43, 21);
530 break;
531 case kBlue:
532 media::FillYUV(video_frame.get(), 29, 255, 107);
533 break;
534 }
535 cc::PaintFlags flags;
536 flags.setBlendMode(mode);
537 flags.setFilterQuality(kLow_SkFilterQuality);
538 renderer_.Paint(std::move(video_frame), canvas, dest_rect, flags,
539 video_transformation, nullptr);
540 }
541
Copy(scoped_refptr<VideoFrame> video_frame,cc::PaintCanvas * canvas)542 void PaintCanvasVideoRendererTest::Copy(scoped_refptr<VideoFrame> video_frame,
543 cc::PaintCanvas* canvas) {
544 renderer_.Copy(std::move(video_frame), canvas, nullptr);
545 }
546
TEST_F(PaintCanvasVideoRendererTest,NoFrame)547 TEST_F(PaintCanvasVideoRendererTest, NoFrame) {
548 // Test that black gets painted over canvas.
549 target_canvas()->clear(SK_ColorRED);
550 PaintWithoutFrame(target_canvas());
551 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
552 }
553
TEST_F(PaintCanvasVideoRendererTest,TransparentFrame)554 TEST_F(PaintCanvasVideoRendererTest, TransparentFrame) {
555 target_canvas()->clear(SK_ColorRED);
556 PaintRotated(
557 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
558 target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrcOver,
559 kNoTransformation);
560 EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), bitmap()->getColor(0, 0));
561 }
562
TEST_F(PaintCanvasVideoRendererTest,TransparentFrameSrcMode)563 TEST_F(PaintCanvasVideoRendererTest, TransparentFrameSrcMode) {
564 target_canvas()->clear(SK_ColorRED);
565 // SRC mode completely overwrites the buffer.
566 PaintRotated(
567 VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
568 target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrc,
569 kNoTransformation);
570 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
571 bitmap()->getColor(0, 0));
572 }
573
TEST_F(PaintCanvasVideoRendererTest,TransparentFrameSrcMode1x1)574 TEST_F(PaintCanvasVideoRendererTest, TransparentFrameSrcMode1x1) {
575 target_canvas()->clear(SK_ColorRED);
576 // SRC mode completely overwrites the buffer.
577 auto frame = VideoFrame::CreateTransparentFrame(gfx::Size(1, 1));
578 PaintRotated(frame.get(), target_canvas(), gfx::RectF(1, 1), kNone,
579 SkBlendMode::kSrc, kNoTransformation);
580 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
581 bitmap()->getColor(0, 0));
582 }
583
TEST_F(PaintCanvasVideoRendererTest,CopyTransparentFrame)584 TEST_F(PaintCanvasVideoRendererTest, CopyTransparentFrame) {
585 target_canvas()->clear(SK_ColorRED);
586 Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
587 target_canvas());
588 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
589 bitmap()->getColor(0, 0));
590 }
591
TEST_F(PaintCanvasVideoRendererTest,Natural)592 TEST_F(PaintCanvasVideoRendererTest, Natural) {
593 Paint(natural_frame(), target_canvas(), kRed);
594 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
595 }
596
TEST_F(PaintCanvasVideoRendererTest,Larger)597 TEST_F(PaintCanvasVideoRendererTest, Larger) {
598 Paint(natural_frame(), target_canvas(), kRed);
599 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
600
601 Paint(larger_frame(), target_canvas(), kBlue);
602 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
603 }
604
TEST_F(PaintCanvasVideoRendererTest,Smaller)605 TEST_F(PaintCanvasVideoRendererTest, Smaller) {
606 Paint(natural_frame(), target_canvas(), kRed);
607 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
608
609 Paint(smaller_frame(), target_canvas(), kBlue);
610 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
611 }
612
TEST_F(PaintCanvasVideoRendererTest,NoTimestamp)613 TEST_F(PaintCanvasVideoRendererTest, NoTimestamp) {
614 VideoFrame* video_frame = natural_frame().get();
615 video_frame->set_timestamp(media::kNoTimestamp);
616 Paint(video_frame, target_canvas(), kRed);
617 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
618 }
619
TEST_F(PaintCanvasVideoRendererTest,CroppedFrame)620 TEST_F(PaintCanvasVideoRendererTest, CroppedFrame) {
621 Paint(cropped_frame(), target_canvas(), kNone);
622 // Check the corners.
623 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
624 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
625 EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
626 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
627 // Check the interior along the border between color regions. Note that we're
628 // bilinearly upscaling, so we'll need to take care to pick sample points that
629 // are just outside the "zone of resampling".
630 EXPECT_EQ(SK_ColorBLACK,
631 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
632 EXPECT_EQ(SK_ColorRED,
633 bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
634 EXPECT_EQ(SK_ColorGREEN,
635 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
636 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
637 }
638
MaybeConvertABGRToARGB(uint32_t abgr)639 uint32_t MaybeConvertABGRToARGB(uint32_t abgr) {
640 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
641 SK_A32_SHIFT == 24
642 return abgr;
643 #else
644 return (base::ByteSwap(abgr & 0x00FFFFFF) >> 8) | (abgr & 0xFF000000);
645 #endif
646 }
647
TEST_F(PaintCanvasVideoRendererTest,CroppedFrameToRGBParallel)648 TEST_F(PaintCanvasVideoRendererTest, CroppedFrameToRGBParallel) {
649 // We need a test frame large enough to trigger parallel conversion. So we use
650 // cropped_frame() as a base and scale it up. Note: Visible rect and natural
651 // size must be even.
652 auto test_frame = VideoFrame::CreateFrame(
653 PIXEL_FORMAT_I420, gfx::Size(3840, 2160), gfx::Rect(1440, 810, 1920, 810),
654 gfx::Size(1920, 810), base::TimeDelta());
655
656 // Fill in the frame with the same data as the cropped frame.
657 libyuv::I420Scale(cropped_frame()->data(0), cropped_frame()->stride(0),
658 cropped_frame()->data(1), cropped_frame()->stride(1),
659 cropped_frame()->data(2), cropped_frame()->stride(2),
660 cropped_frame()->coded_size().width(),
661 cropped_frame()->coded_size().height(), test_frame->data(0),
662 test_frame->stride(0), test_frame->data(1),
663 test_frame->stride(1), test_frame->data(2),
664 test_frame->stride(2), test_frame->coded_size().width(),
665 test_frame->coded_size().height(), libyuv::kFilterNone);
666
667 const gfx::Size visible_size = test_frame->visible_rect().size();
668 const size_t row_bytes = visible_size.width() * sizeof(SkColor);
669 const size_t allocation_size = row_bytes * visible_size.height();
670
671 std::unique_ptr<uint8_t, base::AlignedFreeDeleter> memory(
672 static_cast<uint8_t*>(base::AlignedAlloc(
673 allocation_size, media::VideoFrame::kFrameAddressAlignment)));
674 memset(memory.get(), 0, allocation_size);
675
676 PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
677 test_frame.get(), memory.get(), row_bytes);
678
679 const uint32_t* rgb_pixels = reinterpret_cast<uint32_t*>(memory.get());
680
681 // Check the corners; this is sufficient to reveal https://crbug.com/1027442.
682 EXPECT_EQ(SK_ColorBLACK, rgb_pixels[0]);
683 EXPECT_EQ(MaybeConvertABGRToARGB(SK_ColorRED),
684 rgb_pixels[visible_size.width() - 1]);
685 EXPECT_EQ(SK_ColorGREEN,
686 rgb_pixels[visible_size.width() * (visible_size.height() - 1)]);
687 EXPECT_EQ(MaybeConvertABGRToARGB(SK_ColorBLUE),
688 rgb_pixels[(visible_size.width() - 1) * visible_size.height()]);
689 }
690
TEST_F(PaintCanvasVideoRendererTest,CroppedFrame_NoScaling)691 TEST_F(PaintCanvasVideoRendererTest, CroppedFrame_NoScaling) {
692 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
693 cc::SkiaPaintCanvas canvas(bitmap);
694 const gfx::Rect crop_rect = cropped_frame()->visible_rect();
695
696 // Force painting to a non-zero position on the destination bitmap, to check
697 // if the coordinates are calculated properly.
698 const int offset_x = 10;
699 const int offset_y = 15;
700 canvas.translate(offset_x, offset_y);
701
702 // Create a destination canvas with dimensions and scale which would not
703 // cause scaling.
704 canvas.scale(static_cast<SkScalar>(crop_rect.width()) / kWidth,
705 static_cast<SkScalar>(crop_rect.height()) / kHeight);
706
707 Paint(cropped_frame(), &canvas, kNone);
708
709 // Check the corners.
710 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(offset_x, offset_y));
711 EXPECT_EQ(SK_ColorRED,
712 bitmap.getColor(offset_x + crop_rect.width() - 1, offset_y));
713 EXPECT_EQ(SK_ColorGREEN,
714 bitmap.getColor(offset_x, offset_y + crop_rect.height() - 1));
715 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(offset_x + crop_rect.width() - 1,
716 offset_y + crop_rect.height() - 1));
717 }
718
TEST_F(PaintCanvasVideoRendererTest,Video_Rotation_90)719 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_90) {
720 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
721 cc::SkiaPaintCanvas canvas(bitmap);
722 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
723 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_90));
724 // Check the corners.
725 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));
726 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, 0));
727 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
728 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, kHeight - 1));
729 }
730
TEST_F(PaintCanvasVideoRendererTest,Video_Rotation_180)731 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_180) {
732 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
733 cc::SkiaPaintCanvas canvas(bitmap);
734 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
735 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_180));
736 // Check the corners.
737 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));
738 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, 0));
739 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
740 EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, kHeight - 1));
741 }
742
TEST_F(PaintCanvasVideoRendererTest,Video_Rotation_270)743 TEST_F(PaintCanvasVideoRendererTest, Video_Rotation_270) {
744 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
745 cc::SkiaPaintCanvas canvas(bitmap);
746 PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
747 SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_270));
748 // Check the corners.
749 EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, 0));
750 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, 0));
751 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
752 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(0, kHeight - 1));
753 }
754
TEST_F(PaintCanvasVideoRendererTest,Video_Translate)755 TEST_F(PaintCanvasVideoRendererTest, Video_Translate) {
756 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
757 cc::SkiaPaintCanvas canvas(bitmap);
758 canvas.clear(SK_ColorMAGENTA);
759
760 PaintRotated(cropped_frame(), &canvas,
761 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
762 kNone, SkBlendMode::kSrcOver, kNoTransformation);
763 // Check the corners of quadrant 2 and 4.
764 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
765 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
766 EXPECT_EQ(SK_ColorMAGENTA,
767 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
768 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
769 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight / 2));
770 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight / 2));
771 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight - 1));
772 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight - 1));
773 }
774
TEST_F(PaintCanvasVideoRendererTest,Video_Translate_Rotation_90)775 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_90) {
776 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
777 cc::SkiaPaintCanvas canvas(bitmap);
778 canvas.clear(SK_ColorMAGENTA);
779
780 PaintRotated(cropped_frame(), &canvas,
781 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
782 kNone, SkBlendMode::kSrcOver,
783 VideoTransformation(VIDEO_ROTATION_90));
784 // Check the corners of quadrant 2 and 4.
785 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
786 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
787 EXPECT_EQ(SK_ColorMAGENTA,
788 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
789 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
790 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight / 2));
791 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight / 2));
792 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
793 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight - 1));
794 }
795
TEST_F(PaintCanvasVideoRendererTest,Video_Translate_Rotation_180)796 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_180) {
797 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
798 cc::SkiaPaintCanvas canvas(bitmap);
799 canvas.clear(SK_ColorMAGENTA);
800
801 PaintRotated(cropped_frame(), &canvas,
802 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
803 kNone, SkBlendMode::kSrcOver,
804 VideoTransformation(VIDEO_ROTATION_180));
805 // Check the corners of quadrant 2 and 4.
806 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
807 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
808 EXPECT_EQ(SK_ColorMAGENTA,
809 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
810 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
811 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight / 2));
812 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight / 2));
813 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
814 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight - 1));
815 }
816
TEST_F(PaintCanvasVideoRendererTest,Video_Translate_Rotation_270)817 TEST_F(PaintCanvasVideoRendererTest, Video_Translate_Rotation_270) {
818 SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
819 cc::SkiaPaintCanvas canvas(bitmap);
820 canvas.clear(SK_ColorMAGENTA);
821
822 PaintRotated(cropped_frame(), &canvas,
823 gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
824 kNone, SkBlendMode::kSrcOver,
825 VideoTransformation(VIDEO_ROTATION_270));
826 // Check the corners of quadrant 2 and 4.
827 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
828 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
829 EXPECT_EQ(SK_ColorMAGENTA,
830 bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
831 EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
832 EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight / 2));
833 EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight / 2));
834 EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
835 EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight - 1));
836 }
837
TEST_F(PaintCanvasVideoRendererTest,HighBitDepth)838 TEST_F(PaintCanvasVideoRendererTest, HighBitDepth) {
839 struct params {
840 int bit_depth;
841 VideoPixelFormat format;
842 } kBitDepthAndFormats[] = {{9, PIXEL_FORMAT_YUV420P9},
843 {10, PIXEL_FORMAT_YUV420P10},
844 {12, PIXEL_FORMAT_YUV420P12}};
845 for (const auto param : kBitDepthAndFormats) {
846 // Copy cropped_frame into a highbit frame.
847 scoped_refptr<VideoFrame> frame(VideoFrame::CreateFrame(
848 param.format, cropped_frame()->coded_size(),
849 cropped_frame()->visible_rect(), cropped_frame()->natural_size(),
850 cropped_frame()->timestamp()));
851 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
852 ++plane) {
853 int width = cropped_frame()->row_bytes(plane);
854 uint16_t* dst = reinterpret_cast<uint16_t*>(frame->data(plane));
855 uint8_t* src = cropped_frame()->data(plane);
856 for (int row = 0; row < cropped_frame()->rows(plane); row++) {
857 for (int col = 0; col < width; col++) {
858 dst[col] = src[col] << (param.bit_depth - 8);
859 }
860 src += cropped_frame()->stride(plane);
861 dst += frame->stride(plane) / 2;
862 }
863 }
864
865 Paint(frame, target_canvas(), kNone);
866 // Check the corners.
867 EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
868 EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
869 EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
870 EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
871 // Check the interior along the border between color regions. Note that
872 // we're bilinearly upscaling, so we'll need to take care to pick sample
873 // points that are just outside the "zone of resampling".
874 EXPECT_EQ(SK_ColorBLACK,
875 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
876 EXPECT_EQ(SK_ColorRED,
877 bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
878 EXPECT_EQ(SK_ColorGREEN,
879 bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
880 EXPECT_EQ(SK_ColorBLUE,
881 bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
882 }
883 }
884
TEST_F(PaintCanvasVideoRendererTest,Y16)885 TEST_F(PaintCanvasVideoRendererTest, Y16) {
886 SkBitmap bitmap;
887 bitmap.allocPixels(SkImageInfo::MakeN32(16, 16, kPremul_SkAlphaType));
888
889 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
890 const int offset_x = 3;
891 const int offset_y = 5;
892 const int stride = bitmap.width() + offset_x;
893 const size_t byte_size = stride * (bitmap.height() + offset_y) * 2;
894 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
895 static_cast<unsigned char*>(base::AlignedAlloc(
896 byte_size, media::VideoFrame::kFrameAddressAlignment)));
897 const gfx::Rect rect(offset_x, offset_y, bitmap.width(), bitmap.height());
898 auto video_frame =
899 CreateTestY16Frame(gfx::Size(stride, offset_y + bitmap.height()), rect,
900 memory.get(), cropped_frame()->timestamp());
901
902 cc::SkiaPaintCanvas canvas(bitmap);
903 cc::PaintFlags flags;
904 flags.setFilterQuality(kNone_SkFilterQuality);
905 renderer_.Paint(std::move(video_frame), &canvas,
906 gfx::RectF(bitmap.width(), bitmap.height()), flags,
907 kNoTransformation, nullptr);
908 for (int j = 0; j < bitmap.height(); j++) {
909 for (int i = 0; i < bitmap.width(); i++) {
910 const int value = i + j * bitmap.width();
911 EXPECT_EQ(SkColorSetRGB(value, value, value), bitmap.getColor(i, j));
912 }
913 }
914 }
915
916 namespace {
917 class TestGLES2Interface : public gpu::gles2::GLES2InterfaceStub {
918 public:
GenTextures(GLsizei n,GLuint * textures)919 void GenTextures(GLsizei n, GLuint* textures) override {
920 DCHECK_EQ(1, n);
921 *textures = 1;
922 }
923
TexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLenum format,GLenum type,const void * pixels)924 void TexImage2D(GLenum target,
925 GLint level,
926 GLint internalformat,
927 GLsizei width,
928 GLsizei height,
929 GLint border,
930 GLenum format,
931 GLenum type,
932 const void* pixels) override {
933 if (!teximage2d_callback_.is_null()) {
934 teximage2d_callback_.Run(target, level, internalformat, width, height,
935 border, format, type, pixels);
936 }
937 }
938
TexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const void * pixels)939 void TexSubImage2D(GLenum target,
940 GLint level,
941 GLint xoffset,
942 GLint yoffset,
943 GLsizei width,
944 GLsizei height,
945 GLenum format,
946 GLenum type,
947 const void* pixels) override {
948 if (!texsubimage2d_callback_.is_null()) {
949 texsubimage2d_callback_.Run(target, level, xoffset, yoffset, width,
950 height, format, type, pixels);
951 }
952 }
953
954 base::RepeatingCallback<void(GLenum target,
955 GLint level,
956 GLint internalformat,
957 GLsizei width,
958 GLsizei height,
959 GLint border,
960 GLenum format,
961 GLenum type,
962 const void* pixels)>
963 teximage2d_callback_;
964
965 base::RepeatingCallback<void(GLenum target,
966 GLint level,
967 GLint xoffset,
968 GLint yoffset,
969 GLsizei width,
970 GLsizei height,
971 GLenum format,
972 GLenum type,
973 const void* pixels)>
974 texsubimage2d_callback_;
975 };
976
MailboxHoldersReleased(const gpu::SyncToken & sync_token)977 void MailboxHoldersReleased(const gpu::SyncToken& sync_token) {}
978 } // namespace
979
980 // Test that PaintCanvasVideoRenderer::Paint doesn't crash when GrContext is
981 // unable to wrap a video frame texture (eg due to being abandoned).
TEST_F(PaintCanvasVideoRendererTest,ContextLost)982 TEST_F(PaintCanvasVideoRendererTest, ContextLost) {
983 auto context_provider = viz::TestContextProvider::Create();
984 context_provider->BindToCurrentThread();
985 context_provider->GrContext()->abandonContext();
986
987 cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
988
989 gfx::Size size(kWidth, kHeight);
990 gpu::MailboxHolder holders[VideoFrame::kMaxPlanes] = {gpu::MailboxHolder(
991 gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_RECTANGLE_ARB)};
992 auto video_frame = VideoFrame::WrapNativeTextures(
993 PIXEL_FORMAT_NV12, holders, base::BindOnce(MailboxHoldersReleased), size,
994 gfx::Rect(size), size, kNoTimestamp);
995
996 cc::PaintFlags flags;
997 flags.setFilterQuality(kLow_SkFilterQuality);
998 renderer_.Paint(std::move(video_frame), &canvas, kNaturalRect, flags,
999 kNoTransformation, context_provider.get());
1000 }
1001
EmptyCallback(const gpu::SyncToken & sync_token)1002 void EmptyCallback(const gpu::SyncToken& sync_token) {}
1003
TEST_F(PaintCanvasVideoRendererTest,CorrectFrameSizeToVisibleRect)1004 TEST_F(PaintCanvasVideoRendererTest, CorrectFrameSizeToVisibleRect) {
1005 constexpr int fWidth{16}, fHeight{16};
1006 SkImageInfo imInfo =
1007 SkImageInfo::MakeN32(fWidth, fHeight, kOpaque_SkAlphaType);
1008
1009 cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
1010
1011 gfx::Size coded_size(fWidth, fHeight);
1012 gfx::Size visible_size(fWidth / 2, fHeight / 2);
1013
1014 uint8_t memory[fWidth * fHeight * 2] = {0};
1015
1016 auto video_frame = media::VideoFrame::WrapExternalData(
1017 media::PIXEL_FORMAT_Y16, coded_size, gfx::Rect(visible_size),
1018 visible_size, &memory[0], fWidth * fHeight * 2,
1019 base::TimeDelta::FromMilliseconds(4));
1020
1021 gfx::RectF visible_rect(visible_size.width(), visible_size.height());
1022 cc::PaintFlags flags;
1023 renderer_.Paint(std::move(video_frame), &canvas, visible_rect, flags,
1024 kNoTransformation, nullptr);
1025
1026 EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().width());
1027 EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().height());
1028 }
1029
TEST_F(PaintCanvasVideoRendererTest,TexImage2D_Y16_RGBA32F)1030 TEST_F(PaintCanvasVideoRendererTest, TexImage2D_Y16_RGBA32F) {
1031 // Create test frame.
1032 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
1033 const int offset_x = 3;
1034 const int offset_y = 5;
1035 const int width = 16;
1036 const int height = 16;
1037 const int stride = width + offset_x;
1038 const size_t byte_size = stride * (height + offset_y) * 2;
1039 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
1040 static_cast<unsigned char*>(base::AlignedAlloc(
1041 byte_size, media::VideoFrame::kFrameAddressAlignment)));
1042 const gfx::Rect rect(offset_x, offset_y, width, height);
1043 auto video_frame =
1044 CreateTestY16Frame(gfx::Size(stride, offset_y + height), rect,
1045 memory.get(), cropped_frame()->timestamp());
1046
1047 TestGLES2Interface gles2;
1048 // Bind the texImage2D callback to verify the uint16 to float32 conversion.
1049 gles2.teximage2d_callback_ =
1050 base::BindRepeating([](GLenum target, GLint level, GLint internalformat,
1051 GLsizei width, GLsizei height, GLint border,
1052 GLenum format, GLenum type, const void* pixels) {
1053 EXPECT_EQ(static_cast<unsigned>(GL_FLOAT), type);
1054 EXPECT_EQ(static_cast<unsigned>(GL_RGBA), format);
1055 EXPECT_EQ(GL_RGBA, internalformat);
1056 EXPECT_EQ(0, border);
1057 EXPECT_EQ(16, width);
1058 EXPECT_EQ(16, height);
1059 EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
1060 const float* data = static_cast<const float*>(pixels);
1061 for (int j = 0; j < height; j++) {
1062 for (int i = 0; i < width; i++) {
1063 const int value = i + (height - j - 1) * width; // flip_y is true.
1064 float expected_value =
1065 (((value & 0xFF) << 8) | (~value & 0xFF)) / 65535.f;
1066 EXPECT_EQ(expected_value, data[(i + j * width) * 4]);
1067 EXPECT_EQ(expected_value, data[(i + j * width) * 4 + 1]);
1068 EXPECT_EQ(expected_value, data[(i + j * width) * 4 + 2]);
1069 EXPECT_EQ(1.0f, data[(i + j * width) * 4 + 3]);
1070 }
1071 }
1072 });
1073 PaintCanvasVideoRenderer::TexImage2D(
1074 GL_TEXTURE_2D, 0, &gles2, gpu::Capabilities(), video_frame.get(), 0,
1075 GL_RGBA, GL_RGBA, GL_FLOAT, true /*flip_y*/, true);
1076 }
1077
TEST_F(PaintCanvasVideoRendererTest,TexSubImage2D_Y16_R32F)1078 TEST_F(PaintCanvasVideoRendererTest, TexSubImage2D_Y16_R32F) {
1079 // Create test frame.
1080 // |offset_x| and |offset_y| define visible rect's offset to coded rect.
1081 const int offset_x = 3;
1082 const int offset_y = 5;
1083 const int width = 16;
1084 const int height = 16;
1085 const int stride = width + offset_x;
1086 const size_t byte_size = stride * (height + offset_y) * 2;
1087 std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
1088 static_cast<unsigned char*>(base::AlignedAlloc(
1089 byte_size, media::VideoFrame::kFrameAddressAlignment)));
1090 const gfx::Rect rect(offset_x, offset_y, width, height);
1091 auto video_frame =
1092 CreateTestY16Frame(gfx::Size(stride, offset_y + height), rect,
1093 memory.get(), cropped_frame()->timestamp());
1094
1095 TestGLES2Interface gles2;
1096 // Bind the texImage2D callback to verify the uint16 to float32 conversion.
1097 gles2.texsubimage2d_callback_ =
1098 base::BindRepeating([](GLenum target, GLint level, GLint xoffset,
1099 GLint yoffset, GLsizei width, GLsizei height,
1100 GLenum format, GLenum type, const void* pixels) {
1101 EXPECT_EQ(static_cast<unsigned>(GL_FLOAT), type);
1102 EXPECT_EQ(static_cast<unsigned>(GL_RED), format);
1103 EXPECT_EQ(2, xoffset);
1104 EXPECT_EQ(1, yoffset);
1105 EXPECT_EQ(16, width);
1106 EXPECT_EQ(16, height);
1107 EXPECT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
1108 const float* data = static_cast<const float*>(pixels);
1109 for (int j = 0; j < height; j++) {
1110 for (int i = 0; i < width; i++) {
1111 const int value = i + j * width; // flip_y is false.
1112 float expected_value =
1113 (((value & 0xFF) << 8) | (~value & 0xFF)) / 65535.f;
1114 EXPECT_EQ(expected_value, data[(i + j * width)]);
1115 }
1116 }
1117 });
1118 PaintCanvasVideoRenderer::TexSubImage2D(
1119 GL_TEXTURE_2D, &gles2, video_frame.get(), 0, GL_RED, GL_FLOAT,
1120 2 /*xoffset*/, 1 /*yoffset*/, false /*flip_y*/, true);
1121 }
1122
1123 // Fixture for tests that require a GL context.
1124 class PaintCanvasVideoRendererWithGLTest : public PaintCanvasVideoRendererTest {
1125 public:
1126 using GetColorCallback = base::RepeatingCallback<SkColor(int, int)>;
1127
SetUp()1128 void SetUp() override {
1129 gl::GLSurfaceTestSupport::InitializeOneOff();
1130 enable_pixels_.emplace();
1131 media_context_ = base::MakeRefCounted<viz::TestInProcessContextProvider>(
1132 /*enable_gpu_rasterization=*/false,
1133 /*enable_oop_rasterization=*/false, /*support_locking=*/false);
1134 gpu::ContextResult result = media_context_->BindToCurrentThread();
1135 ASSERT_EQ(result, gpu::ContextResult::kSuccess);
1136
1137 destination_context_ =
1138 base::MakeRefCounted<viz::TestInProcessContextProvider>(
1139 /*enable_gpu_rasterization=*/false,
1140 /*enable_oop_rasterization=*/false, /*support_locking=*/false);
1141 result = destination_context_->BindToCurrentThread();
1142 ASSERT_EQ(result, gpu::ContextResult::kSuccess);
1143 }
1144
TearDown()1145 void TearDown() override {
1146 renderer_.ResetCache();
1147 destination_context_.reset();
1148 media_context_.reset();
1149 enable_pixels_.reset();
1150 viz::TestGpuServiceHolder::ResetInstance();
1151 gl::GLSurfaceTestSupport::ShutdownGL();
1152 }
1153
1154 // Uses CopyVideoFrameTexturesToGLTexture to copy |frame| into a GL texture,
1155 // reads back its contents, and runs |check_pixels| to validate it.
1156 template <class CheckPixels>
CopyVideoFrameTexturesAndCheckPixels(scoped_refptr<VideoFrame> frame,CheckPixels check_pixels)1157 void CopyVideoFrameTexturesAndCheckPixels(scoped_refptr<VideoFrame> frame,
1158 CheckPixels check_pixels) {
1159 auto* destination_gl = destination_context_->ContextGL();
1160 DCHECK(destination_gl);
1161 GLenum target = GL_TEXTURE_2D;
1162 GLuint texture = 0;
1163 destination_gl->GenTextures(1, &texture);
1164 destination_gl->BindTexture(target, texture);
1165
1166 renderer_.CopyVideoFrameTexturesToGLTexture(
1167 media_context_.get(), destination_gl, frame, target, texture, GL_RGBA,
1168 GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
1169 false /* flip_y */);
1170
1171 gfx::Size expected_size = frame->visible_rect().size();
1172
1173 std::unique_ptr<uint8_t[]> pixels =
1174 ReadbackTexture(destination_gl, texture, expected_size);
1175 destination_gl->DeleteTextures(1, &texture);
1176
1177 auto get_color = base::BindRepeating(
1178 [](uint8_t* pixels, const gfx::Size& size, int x, int y) {
1179 uint8_t* p = pixels + (size.width() * y + x) * 4;
1180 return SkColorSetARGB(p[3], p[0], p[1], p[2]);
1181 },
1182 pixels.get(), expected_size);
1183 check_pixels(get_color);
1184 }
1185
1186 // Uses Copy to paint |frame| into a bitmap-backed canvas, then
1187 // runs |check_pixels| to validate the contents of the canvas.
1188 template <class CheckPixels>
PaintVideoFrameAndCheckPixels(scoped_refptr<VideoFrame> frame,CheckPixels check_pixels)1189 void PaintVideoFrameAndCheckPixels(scoped_refptr<VideoFrame> frame,
1190 CheckPixels check_pixels) {
1191 gfx::Size expected_size = frame->visible_rect().size();
1192 SkBitmap bitmap =
1193 AllocBitmap(expected_size.width(), expected_size.height());
1194 cc::SkiaPaintCanvas canvas(bitmap);
1195 canvas.clear(SK_ColorGRAY);
1196 renderer_.Copy(frame, &canvas, media_context_.get());
1197
1198 auto get_color = base::BindRepeating(
1199 [](SkBitmap* bitmap, int x, int y) { return bitmap->getColor(x, y); },
1200 &bitmap);
1201 check_pixels(get_color);
1202 }
1203
1204 // Creates a cropped RGBA VideoFrame. |closure| is run once the shared images
1205 // backing the VideoFrame have been destroyed.
CreateTestRGBAFrame(base::OnceClosure closure)1206 scoped_refptr<VideoFrame> CreateTestRGBAFrame(base::OnceClosure closure) {
1207 return CreateSharedImageRGBAFrame(media_context_, gfx::Size(16, 8),
1208 gfx::Rect(3, 3, 12, 4),
1209 std::move(closure));
1210 }
1211
1212 // Checks that the contents of a texture/canvas match the expectations for the
1213 // cropped RGBA frame above. |get_color| is a callback that returns the actual
1214 // color at a given pixel location.
CheckRGBAFramePixels(GetColorCallback get_color)1215 static void CheckRGBAFramePixels(GetColorCallback get_color) {
1216 EXPECT_EQ(SK_ColorBLACK, get_color.Run(0, 0));
1217 EXPECT_EQ(SK_ColorRED, get_color.Run(1, 0));
1218 EXPECT_EQ(SK_ColorRED, get_color.Run(4, 0));
1219 EXPECT_EQ(SK_ColorGREEN, get_color.Run(5, 0));
1220 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(9, 0));
1221 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(11, 0));
1222 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 1));
1223 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 3));
1224 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(1, 1));
1225 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(4, 1));
1226 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(1, 3));
1227 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(4, 3));
1228 EXPECT_EQ(SK_ColorCYAN, get_color.Run(5, 1));
1229 EXPECT_EQ(SK_ColorCYAN, get_color.Run(5, 3));
1230 EXPECT_EQ(SK_ColorWHITE, get_color.Run(9, 1));
1231 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 1));
1232 EXPECT_EQ(SK_ColorWHITE, get_color.Run(9, 3));
1233 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 3));
1234 }
1235
1236 // Creates a cropped I420 VideoFrame. |closure| is run once the shared images
1237 // backing the VideoFrame have been destroyed.
CreateTestI420Frame(base::OnceClosure closure)1238 scoped_refptr<VideoFrame> CreateTestI420Frame(base::OnceClosure closure) {
1239 return CreateSharedImageI420Frame(media_context_, gfx::Size(16, 8),
1240 gfx::Rect(2, 2, 12, 4),
1241 std::move(closure));
1242 }
1243 // Creates a cropped I420 VideoFrame. |closure| is run once the shared images
1244 // backing the VideoFrame have been destroyed.
CreateTestI420FrameNotSubset(base::OnceClosure closure)1245 scoped_refptr<VideoFrame> CreateTestI420FrameNotSubset(
1246 base::OnceClosure closure) {
1247 return CreateSharedImageI420Frame(media_context_, gfx::Size(16, 8),
1248 gfx::Rect(0, 0, 16, 8),
1249 std::move(closure));
1250 }
1251
1252 // Checks that the contents of a texture/canvas match the expectations for the
1253 // cropped I420 frame above. |get_color| is a callback that returns the actual
1254 // color at a given pixel location.
CheckI420FramePixels(GetColorCallback get_color)1255 static void CheckI420FramePixels(GetColorCallback get_color) {
1256 // Avoid checking around the "seams" where subsamples may be interpolated.
1257 EXPECT_EQ(SK_ColorBLACK, get_color.Run(0, 0));
1258 EXPECT_EQ(SK_ColorRED, get_color.Run(3, 0));
1259 EXPECT_EQ(SK_ColorRED, get_color.Run(4, 0));
1260 EXPECT_EQ(SK_ColorGREEN, get_color.Run(7, 0));
1261 EXPECT_EQ(SK_ColorGREEN, get_color.Run(8, 0));
1262 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(11, 0));
1263 EXPECT_EQ(SK_ColorBLUE, get_color.Run(0, 3));
1264 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(3, 3));
1265 EXPECT_EQ(SK_ColorCYAN, get_color.Run(7, 3));
1266 EXPECT_EQ(SK_ColorWHITE, get_color.Run(11, 3));
1267 }
1268
1269 // Checks that the contents of a texture/canvas match the expectations for the
1270 // cropped I420 frame above. |get_color| is a callback that returns the actual
1271 // color at a given pixel location.
CheckI420FramePixelsNotSubset(GetColorCallback get_color)1272 static void CheckI420FramePixelsNotSubset(GetColorCallback get_color) {
1273 // Avoid checking around the "seams" where subsamples may be interpolated.
1274 EXPECT_EQ(SK_ColorBLACK, get_color.Run(2, 2));
1275 EXPECT_EQ(SK_ColorRED, get_color.Run(5, 2));
1276 EXPECT_EQ(SK_ColorRED, get_color.Run(6, 2));
1277 EXPECT_EQ(SK_ColorGREEN, get_color.Run(9, 2));
1278 EXPECT_EQ(SK_ColorGREEN, get_color.Run(10, 2));
1279 EXPECT_EQ(SK_ColorYELLOW, get_color.Run(13, 2));
1280 EXPECT_EQ(SK_ColorBLUE, get_color.Run(2, 5));
1281 EXPECT_EQ(SK_ColorMAGENTA, get_color.Run(5, 5));
1282 EXPECT_EQ(SK_ColorCYAN, get_color.Run(9, 5));
1283 EXPECT_EQ(SK_ColorWHITE, get_color.Run(13, 5));
1284 }
1285
1286 // Creates a cropped NV12 VideoFrame, or nullptr if the needed extension is
1287 // not available. |closure| is run once the shared images backing the
1288 // VideoFrame have been destroyed.
CreateTestNV12Frame(base::OnceClosure closure)1289 scoped_refptr<VideoFrame> CreateTestNV12Frame(base::OnceClosure closure) {
1290 return CreateSharedImageNV12Frame(media_context_, gfx::Size(16, 8),
1291 gfx::Rect(2, 2, 12, 4),
1292 std::move(closure));
1293 }
1294
1295 // Checks that the contents of a texture/canvas match the expectations for the
1296 // cropped NV12 frame above. |get_color| is a callback that returns the actual
1297 // color at a given pixel location. Note that the expectations are the same as
1298 // for the I420 frame.
CheckNV12FramePixels(GetColorCallback get_color)1299 static void CheckNV12FramePixels(GetColorCallback get_color) {
1300 CheckI420FramePixels(std::move(get_color));
1301 }
1302
1303 protected:
1304 base::Optional<gl::DisableNullDrawGLBindings> enable_pixels_;
1305 scoped_refptr<viz::TestInProcessContextProvider> media_context_;
1306 scoped_refptr<viz::TestInProcessContextProvider> destination_context_;
1307 };
1308
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameYUVDataToGLTexture)1309 TEST_F(PaintCanvasVideoRendererWithGLTest, CopyVideoFrameYUVDataToGLTexture) {
1310 auto* destination_gl = destination_context_->ContextGL();
1311 DCHECK(destination_gl);
1312 GLenum target = GL_TEXTURE_2D;
1313 GLuint texture = 0;
1314 destination_gl->GenTextures(1, &texture);
1315 destination_gl->BindTexture(target, texture);
1316
1317 renderer_.CopyVideoFrameYUVDataToGLTexture(
1318 media_context_.get(), destination_gl, *cropped_frame(), target, texture,
1319 GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
1320 false /* flip_y */);
1321
1322 gfx::Size expected_size = cropped_frame()->visible_rect().size();
1323
1324 std::unique_ptr<uint8_t[]> pixels =
1325 ReadbackTexture(destination_gl, texture, expected_size);
1326 auto get_color = ColorGetter(pixels.get(), expected_size);
1327
1328 // Avoid checking around the seams.
1329 EXPECT_EQ(SK_ColorBLACK, get_color(0, 0));
1330 EXPECT_EQ(SK_ColorRED, get_color(3, 0));
1331 EXPECT_EQ(SK_ColorRED, get_color(7, 0));
1332 EXPECT_EQ(SK_ColorGREEN, get_color(0, 3));
1333 EXPECT_EQ(SK_ColorGREEN, get_color(0, 5));
1334 EXPECT_EQ(SK_ColorBLUE, get_color(3, 3));
1335 EXPECT_EQ(SK_ColorBLUE, get_color(7, 5));
1336
1337 destination_gl->DeleteTextures(1, &texture);
1338 }
1339
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameYUVDataToGLTexture_FlipY)1340 TEST_F(PaintCanvasVideoRendererWithGLTest,
1341 CopyVideoFrameYUVDataToGLTexture_FlipY) {
1342 auto* destination_gl = destination_context_->ContextGL();
1343 DCHECK(destination_gl);
1344 GLenum target = GL_TEXTURE_2D;
1345 GLuint texture = 0;
1346 destination_gl->GenTextures(1, &texture);
1347 destination_gl->BindTexture(target, texture);
1348
1349 renderer_.CopyVideoFrameYUVDataToGLTexture(
1350 media_context_.get(), destination_gl, *cropped_frame(), target, texture,
1351 GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0, false /* premultiply_alpha */,
1352 true /* flip_y */);
1353
1354 gfx::Size expected_size = cropped_frame()->visible_rect().size();
1355
1356 std::unique_ptr<uint8_t[]> pixels =
1357 ReadbackTexture(destination_gl, texture, expected_size);
1358 auto get_color = ColorGetter(pixels.get(), expected_size);
1359
1360 // Avoid checking around the seams.
1361 EXPECT_EQ(SK_ColorBLACK, get_color(0, 5));
1362 EXPECT_EQ(SK_ColorRED, get_color(3, 5));
1363 EXPECT_EQ(SK_ColorRED, get_color(7, 5));
1364 EXPECT_EQ(SK_ColorGREEN, get_color(0, 2));
1365 EXPECT_EQ(SK_ColorGREEN, get_color(0, 0));
1366 EXPECT_EQ(SK_ColorBLUE, get_color(3, 2));
1367 EXPECT_EQ(SK_ColorBLUE, get_color(7, 0));
1368
1369 destination_gl->DeleteTextures(1, &texture);
1370 }
1371
1372 // Checks that we correctly copy a RGBA shared image VideoFrame when using
1373 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameTexturesToGLTextureRGBA)1374 TEST_F(PaintCanvasVideoRendererWithGLTest,
1375 CopyVideoFrameTexturesToGLTextureRGBA) {
1376 base::RunLoop run_loop;
1377 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1378
1379 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckRGBAFramePixels);
1380
1381 frame.reset();
1382 run_loop.Run();
1383 }
1384
1385 // Checks that we correctly copy a RGBA shared image VideoFrame that needs read
1386 // lock fences, when using CopyVideoFrameYUVDataToGLTexture, including correct
1387 // cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence)1388 TEST_F(PaintCanvasVideoRendererWithGLTest,
1389 CopyVideoFrameTexturesToGLTextureRGBA_ReadLockFence) {
1390 base::RunLoop run_loop;
1391 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1392 frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED,
1393 true);
1394
1395 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckRGBAFramePixels);
1396
1397 frame.reset();
1398 run_loop.Run();
1399 }
1400
1401 // Checks that we correctly paint a RGBA shared image VideoFrame, including
1402 // correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,PaintRGBA)1403 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintRGBA) {
1404 base::RunLoop run_loop;
1405 scoped_refptr<VideoFrame> frame = CreateTestRGBAFrame(run_loop.QuitClosure());
1406
1407 PaintVideoFrameAndCheckPixels(frame, &CheckRGBAFramePixels);
1408
1409 frame.reset();
1410 run_loop.Run();
1411 }
1412
1413 // Checks that we correctly copy an I420 shared image VideoFrame when using
1414 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameTexturesToGLTextureI420)1415 TEST_F(PaintCanvasVideoRendererWithGLTest,
1416 CopyVideoFrameTexturesToGLTextureI420) {
1417 base::RunLoop run_loop;
1418 scoped_refptr<VideoFrame> frame = CreateTestI420Frame(run_loop.QuitClosure());
1419
1420 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckI420FramePixels);
1421
1422 frame.reset();
1423 run_loop.Run();
1424 }
1425
1426 // Checks that we correctly paint a I420 shared image VideoFrame, including
1427 // correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,PaintI420)1428 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintI420) {
1429 base::RunLoop run_loop;
1430 scoped_refptr<VideoFrame> frame = CreateTestI420Frame(run_loop.QuitClosure());
1431
1432 PaintVideoFrameAndCheckPixels(frame, &CheckI420FramePixels);
1433
1434 frame.reset();
1435 run_loop.Run();
1436 }
1437
1438 // Checks that we correctly paint a I420 shared image VideoFrame, including
1439 // correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,PaintI420NotSubset)1440 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintI420NotSubset) {
1441 base::RunLoop run_loop;
1442 scoped_refptr<VideoFrame> frame =
1443 CreateTestI420FrameNotSubset(run_loop.QuitClosure());
1444
1445 PaintVideoFrameAndCheckPixels(frame, &CheckI420FramePixelsNotSubset);
1446
1447 frame.reset();
1448 run_loop.Run();
1449 }
1450
1451 // Checks that we correctly copy a NV12 shared image VideoFrame when using
1452 // CopyVideoFrameYUVDataToGLTexture, including correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,CopyVideoFrameTexturesToGLTextureNV12)1453 TEST_F(PaintCanvasVideoRendererWithGLTest,
1454 CopyVideoFrameTexturesToGLTextureNV12) {
1455 base::RunLoop run_loop;
1456 scoped_refptr<VideoFrame> frame = CreateTestNV12Frame(run_loop.QuitClosure());
1457 if (!frame) {
1458 LOG(ERROR) << "GL_EXT_texture_rg not supported, skipping NV12 test";
1459 return;
1460 }
1461
1462 CopyVideoFrameTexturesAndCheckPixels(frame, &CheckNV12FramePixels);
1463
1464 frame.reset();
1465 run_loop.Run();
1466 }
1467
1468 // Checks that we correctly paint a NV12 shared image VideoFrame, including
1469 // correct cropping.
TEST_F(PaintCanvasVideoRendererWithGLTest,PaintNV12)1470 TEST_F(PaintCanvasVideoRendererWithGLTest, PaintNV12) {
1471 base::RunLoop run_loop;
1472 scoped_refptr<VideoFrame> frame = CreateTestNV12Frame(run_loop.QuitClosure());
1473 if (!frame) {
1474 LOG(ERROR) << "GL_EXT_texture_rg not supported, skipping NV12 test";
1475 return;
1476 }
1477
1478 PaintVideoFrameAndCheckPixels(frame, &CheckNV12FramePixels);
1479
1480 frame.reset();
1481 run_loop.Run();
1482 }
1483
1484 } // namespace media
1485