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