1 // Copyright 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/common/gpu/media/rendering_helper.h" 6 7 #include "base/bind.h" 8 #include "base/mac/scoped_nsautorelease_pool.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/stringize_macros.h" 11 #include "base/synchronization/waitable_event.h" 12 #include "ui/gl/gl_context.h" 13 #include "ui/gl/gl_context_stub.h" 14 #include "ui/gl/gl_implementation.h" 15 #include "ui/gl/gl_surface.h" 16 17 #ifdef GL_VARIANT_GLX 18 typedef GLXWindow NativeWindowType; 19 struct ScopedPtrXFree { 20 void operator()(void* x) const { ::XFree(x); } 21 }; 22 #else // EGL 23 typedef EGLNativeWindowType NativeWindowType; 24 #endif 25 26 // Helper for Shader creation. 27 static void CreateShader(GLuint program, 28 GLenum type, 29 const char* source, 30 int size) { 31 GLuint shader = glCreateShader(type); 32 glShaderSource(shader, 1, &source, &size); 33 glCompileShader(shader); 34 int result = GL_FALSE; 35 glGetShaderiv(shader, GL_COMPILE_STATUS, &result); 36 if (!result) { 37 char log[4096]; 38 glGetShaderInfoLog(shader, arraysize(log), NULL, log); 39 LOG(FATAL) << log; 40 } 41 glAttachShader(program, shader); 42 glDeleteShader(shader); 43 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); 44 } 45 46 namespace { 47 48 // Lightweight GLContext stub implementation that returns a constructed 49 // extensions string. We use this to create a context that we can use to 50 // initialize GL extensions with, without actually creating a platform context. 51 class GLContextStubWithExtensions : public gfx::GLContextStub { 52 public: 53 GLContextStubWithExtensions() {} 54 virtual std::string GetExtensions() OVERRIDE; 55 56 void AddExtensionsString(const char* extensions); 57 58 protected: 59 virtual ~GLContextStubWithExtensions() {} 60 61 private: 62 std::string extensions_; 63 64 DISALLOW_COPY_AND_ASSIGN(GLContextStubWithExtensions); 65 }; 66 67 void GLContextStubWithExtensions::AddExtensionsString(const char* extensions) { 68 if (extensions == NULL) 69 return; 70 71 if (extensions_.size() != 0) 72 extensions_ += ' '; 73 extensions_ += extensions; 74 } 75 76 std::string GLContextStubWithExtensions::GetExtensions() { 77 return extensions_; 78 } 79 80 } // anonymous 81 82 namespace content { 83 84 RenderingHelperParams::RenderingHelperParams() {} 85 86 RenderingHelperParams::~RenderingHelperParams() {} 87 88 static const gfx::GLImplementation kGLImplementation = 89 #if defined(GL_VARIANT_GLX) 90 gfx::kGLImplementationDesktopGL; 91 #elif defined(GL_VARIANT_EGL) 92 gfx::kGLImplementationEGLGLES2; 93 #else 94 -1; 95 #error "Unknown GL implementation." 96 #endif 97 98 RenderingHelper::RenderingHelper() { 99 Clear(); 100 } 101 102 RenderingHelper::~RenderingHelper() { 103 CHECK_EQ(window_dimensions_.size(), 0U) << 104 "Must call UnInitialize before dtor."; 105 Clear(); 106 } 107 108 void RenderingHelper::MakeCurrent(int window_id) { 109 #if GL_VARIANT_GLX 110 if (window_id < 0) { 111 CHECK(glXMakeContextCurrent(x_display_, GLX_NONE, GLX_NONE, NULL)); 112 } else { 113 CHECK(glXMakeContextCurrent( 114 x_display_, x_windows_[window_id], x_windows_[window_id], gl_context_)); 115 } 116 #else // EGL 117 if (window_id < 0) { 118 CHECK(eglMakeCurrent(gl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, 119 EGL_NO_CONTEXT)) << eglGetError(); 120 } else { 121 CHECK(eglMakeCurrent(gl_display_, gl_surfaces_[window_id], 122 gl_surfaces_[window_id], gl_context_)) 123 << eglGetError(); 124 } 125 #endif 126 } 127 128 void RenderingHelper::Initialize(const RenderingHelperParams& params, 129 base::WaitableEvent* done) { 130 // Use window_dimensions_.size() != 0 as a proxy for the class having already 131 // been Initialize()'d, and UnInitialize() before continuing. 132 if (window_dimensions_.size()) { 133 base::WaitableEvent done(false, false); 134 UnInitialize(&done); 135 done.Wait(); 136 } 137 138 gfx::InitializeGLBindings(kGLImplementation); 139 scoped_refptr<GLContextStubWithExtensions> stub_context( 140 new GLContextStubWithExtensions()); 141 142 CHECK_GT(params.window_dimensions.size(), 0U); 143 CHECK_EQ(params.frame_dimensions.size(), params.window_dimensions.size()); 144 window_dimensions_ = params.window_dimensions; 145 frame_dimensions_ = params.frame_dimensions; 146 render_as_thumbnails_ = params.render_as_thumbnails; 147 message_loop_ = base::MessageLoop::current(); 148 CHECK_GT(params.num_windows, 0); 149 150 #if GL_VARIANT_GLX 151 x_display_ = base::MessagePumpForUI::GetDefaultXDisplay(); 152 CHECK(x_display_); 153 CHECK(glXQueryVersion(x_display_, NULL, NULL)); 154 const int fbconfig_attr[] = { 155 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 156 GLX_RENDER_TYPE, GLX_RGBA_BIT, 157 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, 158 GLX_BIND_TO_TEXTURE_RGB_EXT, GL_TRUE, 159 GLX_DOUBLEBUFFER, True, 160 GL_NONE, 161 }; 162 int num_fbconfigs; 163 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> glx_fb_configs( 164 glXChooseFBConfig(x_display_, DefaultScreen(x_display_), fbconfig_attr, 165 &num_fbconfigs)); 166 CHECK(glx_fb_configs.get()); 167 CHECK_GT(num_fbconfigs, 0); 168 x_visual_ = glXGetVisualFromFBConfig(x_display_, glx_fb_configs.get()[0]); 169 CHECK(x_visual_); 170 gl_context_ = glXCreateContext(x_display_, x_visual_, 0, true); 171 CHECK(gl_context_); 172 stub_context->AddExtensionsString( 173 reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))); 174 175 #else // EGL 176 EGLNativeDisplayType native_display; 177 178 #if defined(OS_WIN) 179 native_display = EGL_DEFAULT_DISPLAY; 180 #else 181 x_display_ = base::MessagePumpForUI::GetDefaultXDisplay(); 182 CHECK(x_display_); 183 native_display = x_display_; 184 #endif 185 186 gl_display_ = eglGetDisplay(native_display); 187 CHECK(gl_display_); 188 CHECK(eglInitialize(gl_display_, NULL, NULL)) << glGetError(); 189 190 static EGLint rgba8888[] = { 191 EGL_RED_SIZE, 8, 192 EGL_GREEN_SIZE, 8, 193 EGL_BLUE_SIZE, 8, 194 EGL_ALPHA_SIZE, 8, 195 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 196 EGL_NONE, 197 }; 198 EGLConfig egl_config; 199 int num_configs; 200 CHECK(eglChooseConfig(gl_display_, rgba8888, &egl_config, 1, &num_configs)) 201 << eglGetError(); 202 CHECK_GE(num_configs, 1); 203 static EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; 204 gl_context_ = eglCreateContext( 205 gl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); 206 CHECK_NE(gl_context_, EGL_NO_CONTEXT) << eglGetError(); 207 stub_context->AddExtensionsString( 208 reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))); 209 stub_context->AddExtensionsString( 210 eglQueryString(gl_display_, EGL_EXTENSIONS)); 211 #endif 212 213 // Per-window/surface X11 & EGL initialization. 214 for (int i = 0; i < params.num_windows; ++i) { 215 // Arrange X windows whimsically, with some padding. 216 int j = i % window_dimensions_.size(); 217 int width = window_dimensions_[j].width(); 218 int height = window_dimensions_[j].height(); 219 CHECK_GT(width, 0); 220 CHECK_GT(height, 0); 221 int top_left_x = (width + 20) * (i % 4); 222 int top_left_y = (height + 12) * (i % 3); 223 224 #if defined(OS_WIN) 225 NativeWindowType window = 226 CreateWindowEx(0, L"Static", L"VideoDecodeAcceleratorTest", 227 WS_OVERLAPPEDWINDOW | WS_VISIBLE, top_left_x, 228 top_left_y, width, height, NULL, NULL, NULL, 229 NULL); 230 CHECK(window != NULL); 231 windows_.push_back(window); 232 #else 233 int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); 234 235 #if defined(GL_VARIANT_GLX) 236 CHECK_EQ(depth, x_visual_->depth); 237 #endif 238 239 XSetWindowAttributes window_attributes; 240 window_attributes.background_pixel = 241 BlackPixel(x_display_, DefaultScreen(x_display_)); 242 window_attributes.override_redirect = true; 243 244 NativeWindowType window = XCreateWindow( 245 x_display_, DefaultRootWindow(x_display_), 246 top_left_x, top_left_y, width, height, 247 0 /* border width */, 248 depth, CopyFromParent /* class */, CopyFromParent /* visual */, 249 (CWBackPixel | CWOverrideRedirect), &window_attributes); 250 XStoreName(x_display_, window, "VideoDecodeAcceleratorTest"); 251 XSelectInput(x_display_, window, ExposureMask); 252 XMapWindow(x_display_, window); 253 x_windows_.push_back(window); 254 #endif 255 256 #if GL_VARIANT_EGL 257 EGLSurface egl_surface = 258 eglCreateWindowSurface(gl_display_, egl_config, window, NULL); 259 gl_surfaces_.push_back(egl_surface); 260 CHECK_NE(egl_surface, EGL_NO_SURFACE); 261 #endif 262 MakeCurrent(i); 263 } 264 265 // Must be done after a context is made current. 266 gfx::InitializeGLExtensionBindings(kGLImplementation, stub_context.get()); 267 268 if (render_as_thumbnails_) { 269 CHECK_EQ(window_dimensions_.size(), 1U); 270 271 GLint max_texture_size; 272 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); 273 CHECK_GE(max_texture_size, params.thumbnails_page_size.width()); 274 CHECK_GE(max_texture_size, params.thumbnails_page_size.height()); 275 276 thumbnails_fbo_size_ = params.thumbnails_page_size; 277 thumbnail_size_ = params.thumbnail_size; 278 279 glGenFramebuffersEXT(1, &thumbnails_fbo_id_); 280 glGenTextures(1, &thumbnails_texture_id_); 281 glBindTexture(GL_TEXTURE_2D, thumbnails_texture_id_); 282 glTexImage2D(GL_TEXTURE_2D, 283 0, 284 GL_RGB, 285 thumbnails_fbo_size_.width(), 286 thumbnails_fbo_size_.height(), 287 0, 288 GL_RGB, 289 GL_UNSIGNED_SHORT_5_6_5, 290 NULL); 291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 292 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 293 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 294 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 295 296 glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_); 297 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, 298 GL_COLOR_ATTACHMENT0, 299 GL_TEXTURE_2D, 300 thumbnails_texture_id_, 301 0); 302 303 GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); 304 CHECK(fb_status == GL_FRAMEBUFFER_COMPLETE) << fb_status; 305 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 306 glClear(GL_COLOR_BUFFER_BIT); 307 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); 308 } 309 310 // These vertices and texture coords. map (0,0) in the texture to the 311 // bottom left of the viewport. Since we get the video frames with the 312 // the top left at (0,0) we need to flip the texture y coordinate 313 // in the vertex shader for this to be rendered the right way up. 314 // In the case of thumbnail rendering we use the same vertex shader 315 // to render the FBO the screen, where we do not want this flipping. 316 static const float kVertices[] = 317 { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, }; 318 static const float kTextureCoords[] = { 0, 1, 0, 0, 1, 1, 1, 0, }; 319 static const char kVertexShader[] = STRINGIZE( 320 varying vec2 interp_tc; 321 attribute vec4 in_pos; 322 attribute vec2 in_tc; 323 uniform bool tex_flip; 324 void main() { 325 if (tex_flip) 326 interp_tc = vec2(in_tc.x, 1.0 - in_tc.y); 327 else 328 interp_tc = in_tc; 329 gl_Position = in_pos; 330 }); 331 332 #if GL_VARIANT_EGL 333 static const char kFragmentShader[] = 334 "#extension GL_OES_EGL_image_external : enable\n" 335 "precision mediump float;\n" 336 "varying vec2 interp_tc;\n" 337 "uniform sampler2D tex;\n" 338 "#ifdef GL_OES_EGL_image_external\n" 339 "uniform samplerExternalOES tex_external;\n" 340 "#endif\n" 341 "void main() {\n" 342 " vec4 color = texture2D(tex, interp_tc);\n" 343 "#ifdef GL_OES_EGL_image_external\n" 344 " color += texture2D(tex_external, interp_tc);\n" 345 "#endif\n" 346 " gl_FragColor = color;\n" 347 "}\n"; 348 #else 349 static const char kFragmentShader[] = STRINGIZE( 350 varying vec2 interp_tc; 351 uniform sampler2D tex; 352 void main() { 353 gl_FragColor = texture2D(tex, interp_tc); 354 }); 355 #endif 356 program_ = glCreateProgram(); 357 CreateShader( 358 program_, GL_VERTEX_SHADER, kVertexShader, arraysize(kVertexShader)); 359 CreateShader(program_, 360 GL_FRAGMENT_SHADER, 361 kFragmentShader, 362 arraysize(kFragmentShader)); 363 glLinkProgram(program_); 364 int result = GL_FALSE; 365 glGetProgramiv(program_, GL_LINK_STATUS, &result); 366 if (!result) { 367 char log[4096]; 368 glGetShaderInfoLog(program_, arraysize(log), NULL, log); 369 LOG(FATAL) << log; 370 } 371 glUseProgram(program_); 372 glDeleteProgram(program_); 373 374 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0); 375 glUniform1i(glGetUniformLocation(program_, "tex"), 0); 376 GLint tex_external = glGetUniformLocation(program_, "tex_external"); 377 if (tex_external != -1) { 378 glUniform1i(tex_external, 1); 379 } 380 int pos_location = glGetAttribLocation(program_, "in_pos"); 381 glEnableVertexAttribArray(pos_location); 382 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); 383 int tc_location = glGetAttribLocation(program_, "in_tc"); 384 glEnableVertexAttribArray(tc_location); 385 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords); 386 done->Signal(); 387 } 388 389 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { 390 CHECK_EQ(base::MessageLoop::current(), message_loop_); 391 if (render_as_thumbnails_) { 392 glDeleteTextures(1, &thumbnails_texture_id_); 393 glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_); 394 } 395 #if GL_VARIANT_GLX 396 397 glXDestroyContext(x_display_, gl_context_); 398 #else // EGL 399 MakeCurrent(-1); 400 CHECK(eglDestroyContext(gl_display_, gl_context_)); 401 for (size_t i = 0; i < gl_surfaces_.size(); ++i) 402 CHECK(eglDestroySurface(gl_display_, gl_surfaces_[i])); 403 CHECK(eglTerminate(gl_display_)); 404 #endif 405 gfx::ClearGLBindings(); 406 Clear(); 407 done->Signal(); 408 } 409 410 void RenderingHelper::CreateTexture(int window_id, 411 uint32 texture_target, 412 uint32* texture_id, 413 base::WaitableEvent* done) { 414 if (base::MessageLoop::current() != message_loop_) { 415 message_loop_->PostTask( 416 FROM_HERE, 417 base::Bind(&RenderingHelper::CreateTexture, base::Unretained(this), 418 window_id, texture_target, texture_id, done)); 419 return; 420 } 421 MakeCurrent(window_id); 422 glGenTextures(1, texture_id); 423 glBindTexture(texture_target, *texture_id); 424 int dimensions_id = window_id % frame_dimensions_.size(); 425 if (texture_target == GL_TEXTURE_2D) { 426 glTexImage2D(GL_TEXTURE_2D, 427 0, 428 GL_RGBA, 429 frame_dimensions_[dimensions_id].width(), 430 frame_dimensions_[dimensions_id].height(), 431 0, 432 GL_RGBA, 433 GL_UNSIGNED_BYTE, 434 NULL); 435 } 436 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 437 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 438 // OpenGLES2.0.25 section 3.8.2 requires CLAMP_TO_EDGE for NPOT textures. 439 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 440 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 441 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); 442 CHECK(texture_id_to_surface_index_.insert( 443 std::make_pair(*texture_id, window_id)).second); 444 done->Signal(); 445 } 446 447 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { 448 CHECK_EQ(base::MessageLoop::current(), message_loop_); 449 size_t window_id = texture_id_to_surface_index_[texture_id]; 450 MakeCurrent(window_id); 451 452 int dimensions_id = window_id % window_dimensions_.size(); 453 int width = window_dimensions_[dimensions_id].width(); 454 int height = window_dimensions_[dimensions_id].height(); 455 456 if (render_as_thumbnails_) { 457 glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_); 458 const int thumbnails_in_row = 459 thumbnails_fbo_size_.width() / thumbnail_size_.width(); 460 const int thumbnails_in_column = 461 thumbnails_fbo_size_.height() / thumbnail_size_.height(); 462 const int row = (frame_count_ / thumbnails_in_row) % thumbnails_in_column; 463 const int col = frame_count_ % thumbnails_in_row; 464 const int x = col * thumbnail_size_.width(); 465 const int y = row * thumbnail_size_.height(); 466 467 glViewport(x, y, thumbnail_size_.width(), thumbnail_size_.height()); 468 glScissor(x, y, thumbnail_size_.width(), thumbnail_size_.height()); 469 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0); 470 } else { 471 glViewport(0, 0, width, height); 472 glScissor(0, 0, width, height); 473 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); 474 } 475 476 // Unbound texture samplers default to (0, 0, 0, 1). Use this fact to switch 477 // between GL_TEXTURE_2D and GL_TEXTURE_EXTERNAL_OES as appopriate. 478 if (texture_target == GL_TEXTURE_2D) { 479 glActiveTexture(GL_TEXTURE0 + 0); 480 glBindTexture(GL_TEXTURE_2D, texture_id); 481 glActiveTexture(GL_TEXTURE0 + 1); 482 glBindTexture(texture_target, 0); 483 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) { 484 glActiveTexture(GL_TEXTURE0 + 0); 485 glBindTexture(GL_TEXTURE_2D, 0); 486 glActiveTexture(GL_TEXTURE0 + 1); 487 glBindTexture(texture_target, texture_id); 488 } 489 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 490 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); 491 492 ++frame_count_; 493 494 if (render_as_thumbnails_) { 495 // Copy from FBO to screen 496 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); 497 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); 498 glViewport(0, 0, width, height); 499 glScissor(0, 0, width, height); 500 glActiveTexture(GL_TEXTURE0 + 0); 501 glBindTexture(GL_TEXTURE_2D, thumbnails_texture_id_); 502 glActiveTexture(GL_TEXTURE0 + 1); 503 glBindTexture(texture_target, 0); 504 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 505 } 506 507 #if GL_VARIANT_GLX 508 glXSwapBuffers(x_display_, x_windows_[window_id]); 509 #else // EGL 510 eglSwapBuffers(gl_display_, gl_surfaces_[window_id]); 511 CHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); 512 #endif 513 } 514 515 void RenderingHelper::DeleteTexture(uint32 texture_id) { 516 glDeleteTextures(1, &texture_id); 517 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); 518 } 519 520 void* RenderingHelper::GetGLContext() { 521 return gl_context_; 522 } 523 524 void* RenderingHelper::GetGLDisplay() { 525 #if GL_VARIANT_GLX 526 return x_display_; 527 #else // EGL 528 return gl_display_; 529 #endif 530 } 531 532 void RenderingHelper::Clear() { 533 window_dimensions_.clear(); 534 frame_dimensions_.clear(); 535 texture_id_to_surface_index_.clear(); 536 message_loop_ = NULL; 537 gl_context_ = NULL; 538 #if GL_VARIANT_EGL 539 gl_display_ = EGL_NO_DISPLAY; 540 gl_surfaces_.clear(); 541 #endif 542 render_as_thumbnails_ = false; 543 frame_count_ = 0; 544 thumbnails_fbo_id_ = 0; 545 thumbnails_texture_id_ = 0; 546 547 #if defined(OS_WIN) 548 for (size_t i = 0; i < windows_.size(); ++i) { 549 DestroyWindow(windows_[i]); 550 } 551 windows_.clear(); 552 #else 553 // Destroy resources acquired in Initialize, in reverse-acquisition order. 554 for (size_t i = 0; i < x_windows_.size(); ++i) { 555 CHECK(XUnmapWindow(x_display_, x_windows_[i])); 556 CHECK(XDestroyWindow(x_display_, x_windows_[i])); 557 } 558 // Mimic newly created object. 559 x_display_ = NULL; 560 x_windows_.clear(); 561 #endif 562 } 563 564 void RenderingHelper::GetThumbnailsAsRGB(std::vector<unsigned char>* rgb, 565 bool* alpha_solid, 566 base::WaitableEvent* done) { 567 CHECK(render_as_thumbnails_); 568 569 const size_t num_pixels = thumbnails_fbo_size_.GetArea(); 570 std::vector<unsigned char> rgba; 571 rgba.resize(num_pixels * 4); 572 glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_); 573 glPixelStorei(GL_PACK_ALIGNMENT, 1); 574 // We can only count on GL_RGBA/GL_UNSIGNED_BYTE support. 575 glReadPixels(0, 576 0, 577 thumbnails_fbo_size_.width(), 578 thumbnails_fbo_size_.height(), 579 GL_RGBA, 580 GL_UNSIGNED_BYTE, 581 &rgba[0]); 582 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); 583 rgb->resize(num_pixels * 3); 584 // Drop the alpha channel, but check as we go that it is all 0xff. 585 bool solid = true; 586 unsigned char* rgb_ptr = &((*rgb)[0]); 587 unsigned char* rgba_ptr = &rgba[0]; 588 for (size_t i = 0; i < num_pixels; ++i) { 589 *rgb_ptr++ = *rgba_ptr++; 590 *rgb_ptr++ = *rgba_ptr++; 591 *rgb_ptr++ = *rgba_ptr++; 592 solid = solid && (*rgba_ptr == 0xff); 593 rgba_ptr++; 594 } 595 *alpha_solid = solid; 596 597 done->Signal(); 598 } 599 600 } // namespace content 601