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