OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 "content/browser/renderer_host/compositing_iosurface_transformer_mac.h" | |
6 | |
7 #include <OpenGL/CGLCurrent.h> | |
8 #include <OpenGL/CGLRenderers.h> | |
9 #include <OpenGL/CGLTypes.h> | |
10 #include <OpenGL/OpenGL.h> | |
11 #include <OpenGL/gl.h> | |
12 #include <OpenGL/glu.h> | |
13 | |
14 #include <algorithm> | |
15 #include <cstdlib> | |
16 #include <sstream> | |
17 #include <vector> | |
18 | |
19 #include "base/logging.h" | |
20 #include "base/memory/scoped_ptr.h" | |
21 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_ma
c.h" | |
22 #include "media/base/yuv_convert.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 #include "third_party/skia/include/core/SkBitmap.h" | |
25 #include "third_party/skia/include/core/SkCanvas.h" | |
26 #include "third_party/skia/include/core/SkColor.h" | |
27 #include "third_party/skia/include/core/SkRect.h" | |
28 #include "ui/gfx/rect.h" | |
29 | |
30 namespace content { | |
31 | |
32 #define EXPECT_NO_GL_ERROR(stmt) \ | |
33 do { \ | |
34 stmt; \ | |
35 const GLenum error_code = glGetError(); \ | |
36 EXPECT_TRUE(GL_NO_ERROR == error_code) \ | |
37 << "for error code " << error_code \ | |
38 << ": " << gluErrorString(error_code); \ | |
39 } while(0) | |
40 | |
41 namespace { | |
42 | |
43 const GLenum kGLTextureTarget = GL_TEXTURE_RECTANGLE_ARB; | |
44 | |
45 enum RendererRestriction { | |
46 RESTRICTION_NONE, | |
47 RESTRICTION_SOFTWARE_ONLY, | |
48 RESTRICTION_HARDWARE_ONLY | |
49 }; | |
50 | |
51 bool InitializeGLContext(CGLContextObj* context, | |
52 RendererRestriction restriction) { | |
53 std::vector<CGLPixelFormatAttribute> attribs; | |
54 // Select off-screen renderers only. | |
55 attribs.push_back(kCGLPFAOffScreen); | |
56 // By default, the library will prefer hardware-accelerated renderers, but | |
57 // falls back on the software ones if necessary. However, there are use cases | |
58 // where we want to force a restriction (e.g., benchmarking performance). | |
59 if (restriction == RESTRICTION_SOFTWARE_ONLY) { | |
60 attribs.push_back(kCGLPFARendererID); | |
61 attribs.push_back(static_cast<CGLPixelFormatAttribute>( | |
62 kCGLRendererGenericFloatID)); | |
63 } else if (restriction == RESTRICTION_HARDWARE_ONLY) { | |
64 attribs.push_back(kCGLPFAAccelerated); | |
65 } | |
66 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); | |
67 | |
68 CGLPixelFormatObj format; | |
69 GLint num_pixel_formats = 0; | |
70 bool success = true; | |
71 if (CGLChoosePixelFormat(&attribs.front(), &format, &num_pixel_formats) != | |
72 kCGLNoError) { | |
73 LOG(ERROR) << "Error choosing pixel format."; | |
74 success = false; | |
75 } | |
76 if (success && num_pixel_formats <= 0) { | |
77 LOG(ERROR) << "num_pixel_formats <= 0; actual value is " | |
78 << num_pixel_formats; | |
79 success = false; | |
80 } | |
81 if (success && CGLCreateContext(format, NULL, context) != kCGLNoError) { | |
82 LOG(ERROR) << "Error creating context."; | |
83 success = false; | |
84 } | |
85 CGLDestroyPixelFormat(format); | |
86 return success; | |
87 } | |
88 | |
89 // Returns a decent test pattern for testing all of: 1) orientation, 2) scaling, | |
90 // 3) color space conversion (e.g., 4 pixels --> one U or V pixel), and 4) | |
91 // texture alignment/processing. Example 32x32 bitmap: | |
92 // | |
93 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB | |
94 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB | |
95 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC | |
96 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC | |
97 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB | |
98 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB | |
99 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC | |
100 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC | |
101 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB | |
102 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB | |
103 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC | |
104 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC | |
105 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB | |
106 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB | |
107 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC | |
108 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC | |
109 // | |
110 // Key: G = Gray, R = Red, B = Blue, Y = Yellow, C = Cyan | |
111 SkBitmap GenerateTestPatternBitmap(const gfx::Size& size) { | |
112 SkBitmap bitmap; | |
113 CHECK(bitmap.allocN32Pixels(size.width(), size.height())); | |
114 bitmap.eraseColor(SK_ColorGRAY); | |
115 for (int y = 0; y < size.height(); ++y) { | |
116 uint32_t* p = bitmap.getAddr32(0, y); | |
117 for (int x = 0; x < size.width(); ++x, ++p) { | |
118 if ((x < (size.width() / 2)) && (y < (size.height() / 2))) | |
119 continue; // Leave upper-left quadrant gray. | |
120 *p = SkColorSetARGB(255, | |
121 x % 4 < 2 ? 255 : 0, | |
122 y % 4 < 2 ? 255 : 0, | |
123 x % 4 < 2 ? 0 : 255); | |
124 } | |
125 } | |
126 return bitmap; | |
127 } | |
128 | |
129 // Creates a new texture consisting of the given |bitmap|. | |
130 GLuint CreateTextureWithImage(const SkBitmap& bitmap) { | |
131 GLuint texture; | |
132 EXPECT_NO_GL_ERROR(glGenTextures(1, &texture)); | |
133 EXPECT_NO_GL_ERROR(glBindTexture(kGLTextureTarget, texture)); | |
134 { | |
135 SkAutoLockPixels lock_bitmap(bitmap); | |
136 EXPECT_NO_GL_ERROR(glTexImage2D( | |
137 kGLTextureTarget, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0, | |
138 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, bitmap.getPixels())); | |
139 } | |
140 glBindTexture(kGLTextureTarget, 0); | |
141 return texture; | |
142 } | |
143 | |
144 // Read back a texture from the GPU, returning the image data as an SkBitmap. | |
145 SkBitmap ReadBackTexture(GLuint texture, const gfx::Size& size, GLenum format) { | |
146 SkBitmap result; | |
147 CHECK(result.allocN32Pixels(size.width(), size.height())); | |
148 | |
149 GLuint frame_buffer; | |
150 EXPECT_NO_GL_ERROR(glGenFramebuffersEXT(1, &frame_buffer)); | |
151 EXPECT_NO_GL_ERROR( | |
152 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, frame_buffer)); | |
153 EXPECT_NO_GL_ERROR(glFramebufferTexture2DEXT( | |
154 GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, kGLTextureTarget, | |
155 texture, 0)); | |
156 DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) == | |
157 GL_FRAMEBUFFER_COMPLETE_EXT); | |
158 | |
159 { | |
160 SkAutoLockPixels lock_result(result); | |
161 EXPECT_NO_GL_ERROR(glReadPixels( | |
162 0, 0, size.width(), size.height(), format, GL_UNSIGNED_INT_8_8_8_8_REV, | |
163 result.getPixels())); | |
164 } | |
165 | |
166 EXPECT_NO_GL_ERROR(glDeleteFramebuffersEXT(1, &frame_buffer)); | |
167 | |
168 return result; | |
169 } | |
170 | |
171 // Returns the |src_rect| region of |src| scaled to |to_size| by drawing on a | |
172 // Skia canvas, and using bilinear filtering (just like a GPU would). | |
173 SkBitmap ScaleBitmapWithSkia(const SkBitmap& src, | |
174 const gfx::Rect& src_rect, | |
175 const gfx::Size& to_size) { | |
176 SkBitmap cropped_src; | |
177 if (src_rect == gfx::Rect(0, 0, src.width(), src.height())) { | |
178 cropped_src = src; | |
179 } else { | |
180 CHECK(src.extractSubset( | |
181 &cropped_src, | |
182 SkIRect::MakeXYWH(src_rect.x(), src_rect.y(), | |
183 src_rect.width(), src_rect.height()))); | |
184 } | |
185 | |
186 SkBitmap result; | |
187 CHECK(result.allocPixels( | |
188 cropped_src.info().makeWH(to_size.width(), to_size.height()))); | |
189 | |
190 SkCanvas canvas(result); | |
191 canvas.scale(static_cast<double>(result.width()) / cropped_src.width(), | |
192 static_cast<double>(result.height()) / cropped_src.height()); | |
193 SkPaint paint; | |
194 paint.setFilterLevel(SkPaint::kLow_FilterLevel); // Use bilinear filtering. | |
195 canvas.drawBitmap(cropped_src, 0, 0, &paint); | |
196 | |
197 return result; | |
198 } | |
199 | |
200 // The maximum value by which a pixel value may deviate from the expected value | |
201 // before considering it "significantly different." This is meant to account | |
202 // for the slight differences in filtering techniques used between the various | |
203 // GPUs and software implementations. | |
204 const int kDifferenceThreshold = 16; | |
205 | |
206 // Returns the number of pixels significantly different between |expected| and | |
207 // |actual|. | |
208 int ImageDifference(const SkBitmap& expected, const SkBitmap& actual) { | |
209 SkAutoLockPixels lock_expected(expected); | |
210 SkAutoLockPixels lock_actual(actual); | |
211 | |
212 // Sanity-check assumed image properties. | |
213 DCHECK_EQ(expected.width(), actual.width()); | |
214 DCHECK_EQ(expected.height(), actual.height()); | |
215 DCHECK_EQ(kN32_SkColorType, expected.colorType()); | |
216 DCHECK_EQ(kN32_SkColorType, actual.colorType()); | |
217 | |
218 // Compare both images. | |
219 int num_pixels_different = 0; | |
220 for (int y = 0; y < expected.height(); ++y) { | |
221 const uint32_t* p = expected.getAddr32(0, y); | |
222 const uint32_t* q = actual.getAddr32(0, y); | |
223 for (int x = 0; x < expected.width(); ++x, ++p, ++q) { | |
224 if (abs(static_cast<int>(SkColorGetR(*p)) - | |
225 static_cast<int>(SkColorGetR(*q))) > kDifferenceThreshold || | |
226 abs(static_cast<int>(SkColorGetG(*p)) - | |
227 static_cast<int>(SkColorGetG(*q))) > kDifferenceThreshold || | |
228 abs(static_cast<int>(SkColorGetB(*p)) - | |
229 static_cast<int>(SkColorGetB(*q))) > kDifferenceThreshold) { | |
230 ++num_pixels_different; | |
231 } | |
232 } | |
233 } | |
234 | |
235 return num_pixels_different; | |
236 } | |
237 | |
238 // Returns the number of pixels significantly different between |expected| and | |
239 // |actual|. It is understood that |actual| contains 4-byte quads, and so we | |
240 // may need to be ignoring a mod-4 number of pixels at the end of each of its | |
241 // rows. | |
242 int ImagePlaneDifference(const uint8* expected, const SkBitmap& actual, | |
243 const gfx::Size& dst_size) { | |
244 SkAutoLockPixels actual_lock(actual); | |
245 | |
246 int num_pixels_different = 0; | |
247 for (int y = 0; y < dst_size.height(); ++y) { | |
248 const uint8* p = expected + y * dst_size.width(); | |
249 const uint8* const p_end = p + dst_size.width(); | |
250 const uint8* q = | |
251 reinterpret_cast<uint8*>(actual.getPixels()) + y * actual.rowBytes(); | |
252 for (; p < p_end; ++p, ++q) { | |
253 if (abs(static_cast<int>(*p) - static_cast<int>(*q)) > | |
254 kDifferenceThreshold) { | |
255 ++num_pixels_different; | |
256 } | |
257 } | |
258 } | |
259 | |
260 return num_pixels_different; | |
261 } | |
262 | |
263 } // namespace | |
264 | |
265 // Note: All tests fixtures operate within an off-screen OpenGL context. | |
266 class CompositingIOSurfaceTransformerTest : public testing::Test { | |
267 public: | |
268 CompositingIOSurfaceTransformerTest() { | |
269 // TODO(miu): Try to use RESTRICTION_NONE to speed up the execution time of | |
270 // unit tests, once it's established that the trybots and buildbots behave | |
271 // well when using the GPU. | |
272 CHECK(InitializeGLContext(&context_, RESTRICTION_SOFTWARE_ONLY)); | |
273 CGLSetCurrentContext(context_); | |
274 shader_program_cache_.reset(new CompositingIOSurfaceShaderPrograms()); | |
275 transformer_.reset(new CompositingIOSurfaceTransformer( | |
276 kGLTextureTarget, false, shader_program_cache_.get())); | |
277 } | |
278 | |
279 virtual ~CompositingIOSurfaceTransformerTest() { | |
280 transformer_->ReleaseCachedGLObjects(); | |
281 shader_program_cache_->Reset(); | |
282 CGLSetCurrentContext(NULL); | |
283 CGLDestroyContext(context_); | |
284 } | |
285 | |
286 protected: | |
287 void RunResizeTest(const SkBitmap& src_bitmap, const gfx::Rect& src_rect, | |
288 const gfx::Size& dst_size) { | |
289 SCOPED_TRACE(::testing::Message() | |
290 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() | |
291 << ")x[" << src_rect.width() << 'x' << src_rect.height() | |
292 << "]; dst_size=[" << dst_size.width() << 'x' | |
293 << dst_size.height() << ']'); | |
294 | |
295 // Do the scale operation on the GPU. | |
296 const GLuint original_texture = CreateTextureWithImage(src_bitmap); | |
297 ASSERT_NE(0u, original_texture); | |
298 GLuint scaled_texture = 0u; | |
299 ASSERT_TRUE(transformer_->ResizeBilinear( | |
300 original_texture, src_rect, dst_size, &scaled_texture)); | |
301 EXPECT_NE(0u, scaled_texture); | |
302 CGLFlushDrawable(context_); // Account for some buggy driver impls. | |
303 const SkBitmap result_bitmap = | |
304 ReadBackTexture(scaled_texture, dst_size, GL_BGRA); | |
305 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); | |
306 | |
307 // Compare the image read back to the version produced by a known-working | |
308 // software implementation. Allow up to 2 lines of mismatch due to how | |
309 // implementations disagree on resolving the processing of edges. | |
310 const SkBitmap expected_bitmap = | |
311 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); | |
312 EXPECT_GE(std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, | |
313 ImageDifference(expected_bitmap, result_bitmap)); | |
314 } | |
315 | |
316 void RunTransformRGBToYV12Test( | |
317 const SkBitmap& src_bitmap, const gfx::Rect& src_rect, | |
318 const gfx::Size& dst_size) { | |
319 SCOPED_TRACE(::testing::Message() | |
320 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() | |
321 << ")x[" << src_rect.width() << 'x' << src_rect.height() | |
322 << "]; dst_size=[" << dst_size.width() << 'x' | |
323 << dst_size.height() << ']'); | |
324 | |
325 // Perform the RGB to YV12 conversion. | |
326 const GLuint original_texture = CreateTextureWithImage(src_bitmap); | |
327 ASSERT_NE(0u, original_texture); | |
328 GLuint texture_y = 0u; | |
329 GLuint texture_u = 0u; | |
330 GLuint texture_v = 0u; | |
331 gfx::Size packed_y_size; | |
332 gfx::Size packed_uv_size; | |
333 ASSERT_TRUE(transformer_->TransformRGBToYV12( | |
334 original_texture, src_rect, dst_size, | |
335 &texture_y, &texture_u, &texture_v, &packed_y_size, &packed_uv_size)); | |
336 EXPECT_NE(0u, texture_y); | |
337 EXPECT_NE(0u, texture_u); | |
338 EXPECT_NE(0u, texture_v); | |
339 EXPECT_FALSE(packed_y_size.IsEmpty()); | |
340 EXPECT_FALSE(packed_uv_size.IsEmpty()); | |
341 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); | |
342 | |
343 // Read-back the texture for each plane. | |
344 CGLFlushDrawable(context_); // Account for some buggy driver impls. | |
345 const GLenum format = shader_program_cache_->rgb_to_yv12_output_format(); | |
346 const SkBitmap result_y_bitmap = | |
347 ReadBackTexture(texture_y, packed_y_size, format); | |
348 const SkBitmap result_u_bitmap = | |
349 ReadBackTexture(texture_u, packed_uv_size, format); | |
350 const SkBitmap result_v_bitmap = | |
351 ReadBackTexture(texture_v, packed_uv_size, format); | |
352 | |
353 // Compare the Y, U, and V planes read-back to the version produced by a | |
354 // known-working software implementation. Allow up to 2 lines of mismatch | |
355 // due to how implementations disagree on resolving the processing of edges. | |
356 const SkBitmap expected_bitmap = | |
357 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); | |
358 const gfx::Size dst_uv_size( | |
359 (dst_size.width() + 1) / 2, (dst_size.height() + 1) / 2); | |
360 scoped_ptr<uint8[]> expected_y_plane( | |
361 new uint8[dst_size.width() * dst_size.height()]); | |
362 scoped_ptr<uint8[]> expected_u_plane( | |
363 new uint8[dst_uv_size.width() * dst_uv_size.height()]); | |
364 scoped_ptr<uint8[]> expected_v_plane( | |
365 new uint8[dst_uv_size.width() * dst_uv_size.height()]); | |
366 { | |
367 SkAutoLockPixels src_bitmap_lock(expected_bitmap); | |
368 media::ConvertRGB32ToYUV( | |
369 reinterpret_cast<const uint8*>(expected_bitmap.getPixels()), | |
370 expected_y_plane.get(), expected_u_plane.get(), | |
371 expected_v_plane.get(), | |
372 expected_bitmap.width(), expected_bitmap.height(), | |
373 expected_bitmap.rowBytes(), | |
374 dst_size.width(), (dst_size.width() + 1) / 2); | |
375 } | |
376 EXPECT_GE( | |
377 std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, | |
378 ImagePlaneDifference(expected_y_plane.get(), result_y_bitmap, dst_size)) | |
379 << " for RGB --> Y Plane"; | |
380 EXPECT_GE( | |
381 std::max(expected_bitmap.width(), expected_bitmap.height()), | |
382 ImagePlaneDifference(expected_u_plane.get(), result_u_bitmap, | |
383 dst_uv_size)) | |
384 << " for RGB --> U Plane"; | |
385 EXPECT_GE( | |
386 std::max(expected_bitmap.width(), expected_bitmap.height()), | |
387 ImagePlaneDifference(expected_v_plane.get(), result_v_bitmap, | |
388 dst_uv_size)) | |
389 << " for RGB --> V Plane"; | |
390 } | |
391 | |
392 CompositingIOSurfaceShaderPrograms* shader_program_cache() const { | |
393 return shader_program_cache_.get(); | |
394 } | |
395 | |
396 private: | |
397 CGLContextObj context_; | |
398 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache_; | |
399 scoped_ptr<CompositingIOSurfaceTransformer> transformer_; | |
400 | |
401 private: | |
402 DISALLOW_COPY_AND_ASSIGN(CompositingIOSurfaceTransformerTest); | |
403 }; | |
404 | |
405 TEST_F(CompositingIOSurfaceTransformerTest, ShaderProgramsCompileAndLink) { | |
406 // Attempt to use each program, binding its required uniform variables. | |
407 EXPECT_NO_GL_ERROR(shader_program_cache()->UseBlitProgram()); | |
408 EXPECT_NO_GL_ERROR(shader_program_cache()->UseSolidWhiteProgram()); | |
409 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(1, 1.0f)); | |
410 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(2, 1.0f)); | |
411 | |
412 EXPECT_NO_GL_ERROR(glUseProgram(0)); | |
413 } | |
414 | |
415 namespace { | |
416 | |
417 const struct TestParameters { | |
418 int src_width; | |
419 int src_height; | |
420 int scaled_width; | |
421 int scaled_height; | |
422 } kTestParameters[] = { | |
423 // Test 1:1 copies, but exposing varying pixel packing configurations. | |
424 { 64, 64, 64, 64 }, | |
425 { 63, 63, 63, 63 }, | |
426 { 62, 62, 62, 62 }, | |
427 { 61, 61, 61, 61 }, | |
428 { 60, 60, 60, 60 }, | |
429 { 59, 59, 59, 59 }, | |
430 { 58, 58, 58, 58 }, | |
431 { 57, 57, 57, 57 }, | |
432 { 56, 56, 56, 56 }, | |
433 | |
434 // Even-size, one or both dimensions upscaled. | |
435 { 32, 32, 64, 32 }, { 32, 32, 32, 64 }, { 32, 32, 64, 64 }, | |
436 // Even-size, one or both dimensions downscaled by 2X. | |
437 { 32, 32, 16, 32 }, { 32, 32, 32, 16 }, { 32, 32, 16, 16 }, | |
438 // Even-size, one or both dimensions downscaled by 1 pixel. | |
439 { 32, 32, 31, 32 }, { 32, 32, 32, 31 }, { 32, 32, 31, 31 }, | |
440 // Even-size, one or both dimensions downscaled by 2 pixels. | |
441 { 32, 32, 30, 32 }, { 32, 32, 32, 30 }, { 32, 32, 30, 30 }, | |
442 // Even-size, one or both dimensions downscaled by 3 pixels. | |
443 { 32, 32, 29, 32 }, { 32, 32, 32, 29 }, { 32, 32, 29, 29 }, | |
444 | |
445 // Odd-size, one or both dimensions upscaled. | |
446 { 33, 33, 66, 33 }, { 33, 33, 33, 66 }, { 33, 33, 66, 66 }, | |
447 // Odd-size, one or both dimensions downscaled by 2X. | |
448 { 33, 33, 16, 33 }, { 33, 33, 33, 16 }, { 33, 33, 16, 16 }, | |
449 // Odd-size, one or both dimensions downscaled by 1 pixel. | |
450 { 33, 33, 32, 33 }, { 33, 33, 33, 32 }, { 33, 33, 32, 32 }, | |
451 // Odd-size, one or both dimensions downscaled by 2 pixels. | |
452 { 33, 33, 31, 33 }, { 33, 33, 33, 31 }, { 33, 33, 31, 31 }, | |
453 // Odd-size, one or both dimensions downscaled by 3 pixels. | |
454 { 33, 33, 30, 33 }, { 33, 33, 33, 30 }, { 33, 33, 30, 30 }, | |
455 }; | |
456 | |
457 } // namespace | |
458 | |
459 TEST_F(CompositingIOSurfaceTransformerTest, ResizesTexturesCorrectly) { | |
460 for (size_t i = 0; i < arraysize(kTestParameters); ++i) { | |
461 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << i << ']'); | |
462 | |
463 const TestParameters& params = kTestParameters[i]; | |
464 const gfx::Size src_size(params.src_width, params.src_height); | |
465 const gfx::Size dst_size(params.scaled_width, params.scaled_height); | |
466 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); | |
467 | |
468 // Full texture resize test. | |
469 RunResizeTest(src_bitmap, gfx::Rect(src_size), dst_size); | |
470 // Subrect resize test: missing top row in source. | |
471 RunResizeTest(src_bitmap, | |
472 gfx::Rect(0, 1, params.src_width, params.src_height - 1), | |
473 dst_size); | |
474 // Subrect resize test: missing left column in source. | |
475 RunResizeTest(src_bitmap, | |
476 gfx::Rect(1, 0, params.src_width - 1, params.src_height), | |
477 dst_size); | |
478 // Subrect resize test: missing top+bottom rows, and left column in source. | |
479 RunResizeTest(src_bitmap, | |
480 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), | |
481 dst_size); | |
482 // Subrect resize test: missing top row, and left+right columns in source. | |
483 RunResizeTest(src_bitmap, | |
484 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), | |
485 dst_size); | |
486 } | |
487 } | |
488 | |
489 TEST_F(CompositingIOSurfaceTransformerTest, TransformsRGBToYV12) { | |
490 static const GLenum kOutputFormats[] = { GL_BGRA, GL_RGBA }; | |
491 | |
492 for (size_t i = 0; i < arraysize(kOutputFormats); ++i) { | |
493 SCOPED_TRACE(::testing::Message() << "kOutputFormats[" << i << ']'); | |
494 | |
495 shader_program_cache()->SetOutputFormatForTesting(kOutputFormats[i]); | |
496 | |
497 for (size_t j = 0; j < arraysize(kTestParameters); ++j) { | |
498 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << j << ']'); | |
499 | |
500 const TestParameters& params = kTestParameters[j]; | |
501 const gfx::Size src_size(params.src_width, params.src_height); | |
502 const gfx::Size dst_size(params.scaled_width, params.scaled_height); | |
503 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); | |
504 | |
505 // Full texture resize test. | |
506 RunTransformRGBToYV12Test(src_bitmap, gfx::Rect(src_size), dst_size); | |
507 // Subrect resize test: missing top row in source. | |
508 RunTransformRGBToYV12Test( | |
509 src_bitmap, gfx::Rect(0, 1, params.src_width, params.src_height - 1), | |
510 dst_size); | |
511 // Subrect resize test: missing left column in source. | |
512 RunTransformRGBToYV12Test( | |
513 src_bitmap, gfx::Rect(1, 0, params.src_width - 1, params.src_height), | |
514 dst_size); | |
515 // Subrect resize test: missing top+bottom rows, and left column in | |
516 // source. | |
517 RunTransformRGBToYV12Test( | |
518 src_bitmap, | |
519 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), | |
520 dst_size); | |
521 // Subrect resize test: missing top row, and left+right columns in source. | |
522 RunTransformRGBToYV12Test( | |
523 src_bitmap, | |
524 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), | |
525 dst_size); | |
526 } | |
527 } | |
528 } | |
529 | |
530 } // namespace content | |
OLD | NEW |