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