1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "GLReadTexImageHelper.h"
8 
9 #include "gfx2DGlue.h"
10 #include "gfxColor.h"
11 #include "gfxTypes.h"
12 #include "GLContext.h"
13 #include "OGLShaderProgram.h"
14 #include "ScopedGLHelpers.h"
15 
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/Move.h"
18 
19 namespace mozilla {
20 namespace gl {
21 
22 using namespace mozilla::gfx;
23 
GLReadTexImageHelper(GLContext * gl)24 GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl)
25     : mGL(gl)
26 {
27     mPrograms[0] = 0;
28     mPrograms[1] = 0;
29     mPrograms[2] = 0;
30     mPrograms[3] = 0;
31 }
32 
~GLReadTexImageHelper()33 GLReadTexImageHelper::~GLReadTexImageHelper()
34 {
35     if (!mGL->MakeCurrent())
36         return;
37 
38     mGL->fDeleteProgram(mPrograms[0]);
39     mGL->fDeleteProgram(mPrograms[1]);
40     mGL->fDeleteProgram(mPrograms[2]);
41     mGL->fDeleteProgram(mPrograms[3]);
42 }
43 
44 static const GLchar
45 readTextureImageVS[] =
46     "attribute vec2 aVertex;\n"
47     "attribute vec2 aTexCoord;\n"
48     "varying vec2 vTexCoord;\n"
49     "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
50 
51 static const GLchar
52 readTextureImageFS_TEXTURE_2D[] =
53     "#ifdef GL_ES\n"
54     "precision mediump float;\n"
55     "#endif\n"
56     "varying vec2 vTexCoord;\n"
57     "uniform sampler2D uTexture;\n"
58     "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
59 
60 
61 static const GLchar
62 readTextureImageFS_TEXTURE_2D_BGRA[] =
63     "#ifdef GL_ES\n"
64     "precision mediump float;\n"
65     "#endif\n"
66     "varying vec2 vTexCoord;\n"
67     "uniform sampler2D uTexture;\n"
68     "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
69 
70 static const GLchar
71 readTextureImageFS_TEXTURE_EXTERNAL[] =
72     "#extension GL_OES_EGL_image_external : require\n"
73     "#ifdef GL_ES\n"
74     "precision mediump float;\n"
75     "#endif\n"
76     "varying vec2 vTexCoord;\n"
77     "uniform samplerExternalOES uTexture;\n"
78     "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
79 
80 static const GLchar
81 readTextureImageFS_TEXTURE_RECTANGLE[] =
82     "#extension GL_ARB_texture_rectangle\n"
83     "#ifdef GL_ES\n"
84     "precision mediump float;\n"
85     "#endif\n"
86     "varying vec2 vTexCoord;\n"
87     "uniform sampler2DRect uTexture;\n"
88     "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
89 
90 GLuint
TextureImageProgramFor(GLenum aTextureTarget,int aConfig)91 GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget,
92                                              int aConfig)
93 {
94     int variant = 0;
95     const GLchar* readTextureImageFS = nullptr;
96     if (aTextureTarget == LOCAL_GL_TEXTURE_2D) {
97         if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) {
98             // Need to swizzle R/B.
99             readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA;
100             variant = 1;
101         } else {
102             readTextureImageFS = readTextureImageFS_TEXTURE_2D;
103             variant = 0;
104         }
105     } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
106         readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL;
107         variant = 2;
108     } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
109         readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE;
110         variant = 3;
111     }
112 
113     /* This might be overkill, but assure that we don't access out-of-bounds */
114     MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms));
115     if (!mPrograms[variant]) {
116         GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
117         const GLchar* vsSourcePtr = &readTextureImageVS[0];
118         mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr);
119         mGL->fCompileShader(vs);
120 
121         GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
122         mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr);
123         mGL->fCompileShader(fs);
124 
125         GLuint program = mGL->fCreateProgram();
126         mGL->fAttachShader(program, vs);
127         mGL->fAttachShader(program, fs);
128         mGL->fBindAttribLocation(program, 0, "aVertex");
129         mGL->fBindAttribLocation(program, 1, "aTexCoord");
130         mGL->fLinkProgram(program);
131 
132         GLint success;
133         mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success);
134 
135         if (!success) {
136             mGL->fDeleteProgram(program);
137             program = 0;
138         }
139 
140         mGL->fDeleteShader(vs);
141         mGL->fDeleteShader(fs);
142 
143         mPrograms[variant] = program;
144     }
145 
146     return mPrograms[variant];
147 }
148 
149 bool
DidGLErrorOccur(const char * str)150 GLReadTexImageHelper::DidGLErrorOccur(const char* str)
151 {
152     GLenum error = mGL->fGetError();
153     if (error != LOCAL_GL_NO_ERROR) {
154         printf_stderr("GL ERROR: %s (0x%04x) %s\n",
155                       mGL->GLErrorToString(error), error, str);
156         return true;
157     }
158 
159     return false;
160 }
161 
162 bool
GetActualReadFormats(GLContext * gl,GLenum destFormat,GLenum destType,GLenum * out_readFormat,GLenum * out_readType)163 GetActualReadFormats(GLContext* gl,
164                      GLenum destFormat, GLenum destType,
165                      GLenum* out_readFormat, GLenum* out_readType)
166 {
167     MOZ_ASSERT(out_readFormat);
168     MOZ_ASSERT(out_readType);
169 
170     if (destFormat == LOCAL_GL_RGBA &&
171         destType == LOCAL_GL_UNSIGNED_BYTE)
172     {
173         *out_readFormat = destFormat;
174         *out_readType = destType;
175         return true;
176     }
177 
178     bool fallback = true;
179     if (gl->IsGLES()) {
180         GLenum auxFormat = 0;
181         GLenum auxType = 0;
182 
183         gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat);
184         gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType);
185 
186         if (destFormat == auxFormat &&
187             destType == auxType)
188         {
189             fallback = false;
190         }
191     } else {
192         switch (destFormat) {
193             case LOCAL_GL_RGB: {
194                 if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV)
195                     fallback = false;
196                 break;
197             }
198             case LOCAL_GL_BGRA: {
199                 if (destType == LOCAL_GL_UNSIGNED_BYTE ||
200                     destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV)
201                 {
202                     fallback = false;
203                 }
204                 break;
205             }
206         }
207     }
208 
209     if (fallback) {
210         *out_readFormat = LOCAL_GL_RGBA;
211         *out_readType = LOCAL_GL_UNSIGNED_BYTE;
212         return false;
213     } else {
214         *out_readFormat = destFormat;
215         *out_readType = destType;
216         return true;
217     }
218 }
219 
220 void
SwapRAndBComponents(DataSourceSurface * surf)221 SwapRAndBComponents(DataSourceSurface* surf)
222 {
223     DataSourceSurface::MappedSurface map;
224     if (!surf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
225         MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface.");
226         return;
227     }
228     MOZ_ASSERT(map.mStride >= 0);
229 
230     const size_t rowBytes = surf->GetSize().width*4;
231     const size_t rowHole = map.mStride - rowBytes;
232 
233     uint8_t* row = map.mData;
234     if (!row) {
235         MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from"
236                           " DataSourceSurface.");
237         surf->Unmap();
238         return;
239     }
240 
241     const size_t rows = surf->GetSize().height;
242     for (size_t i = 0; i < rows; i++) {
243         const uint8_t* rowEnd = row + rowBytes;
244 
245         while (row != rowEnd) {
246             Swap(row[0], row[2]);
247             row += 4;
248         }
249 
250         row += rowHole;
251     }
252 
253     surf->Unmap();
254 }
255 
256 static int
CalcRowStride(int width,int pixelSize,int alignment)257 CalcRowStride(int width, int pixelSize, int alignment)
258 {
259     MOZ_ASSERT(alignment);
260 
261     int rowStride = width * pixelSize;
262     if (rowStride % alignment) { // Extra at the end of the line?
263         int alignmentCount = rowStride / alignment;
264         rowStride = (alignmentCount+1) * alignment;
265     }
266     return rowStride;
267 }
268 
269 static int
GuessAlignment(int width,int pixelSize,int rowStride)270 GuessAlignment(int width, int pixelSize, int rowStride)
271 {
272     int alignment = 8; // Max GLES allows.
273     while (CalcRowStride(width, pixelSize, alignment) != rowStride) {
274         alignment /= 2;
275         if (!alignment) {
276             NS_WARNING("Bad alignment for GLES. Will use temp surf for readback.");
277             return 0;
278         }
279     }
280     return alignment;
281 }
282 
283 void
ReadPixelsIntoDataSurface(GLContext * gl,DataSourceSurface * dest)284 ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
285 {
286     gl->MakeCurrent();
287     MOZ_ASSERT(dest->GetSize().width != 0);
288     MOZ_ASSERT(dest->GetSize().height != 0);
289 
290     bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
291                     dest->GetFormat() == SurfaceFormat::R8G8B8A8;
292 
293     int destPixelSize;
294     GLenum destFormat;
295     GLenum destType;
296 
297     switch (dest->GetFormat()) {
298     case SurfaceFormat::B8G8R8A8:
299     case SurfaceFormat::B8G8R8X8:
300         // Needs host (little) endian ARGB.
301         destFormat = LOCAL_GL_BGRA;
302         destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
303         break;
304     case SurfaceFormat::R8G8B8A8:
305     case SurfaceFormat::R8G8B8X8:
306         // Needs host (little) endian ABGR.
307         destFormat = LOCAL_GL_RGBA;
308         destType = LOCAL_GL_UNSIGNED_BYTE;
309         break;
310     case SurfaceFormat::R5G6B5_UINT16:
311         destFormat = LOCAL_GL_RGB;
312         destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
313         break;
314     default:
315         MOZ_CRASH("GFX: Bad format, read pixels.");
316     }
317     destPixelSize = BytesPerPixel(dest->GetFormat());
318     MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
319 
320     GLenum readFormat = destFormat;
321     GLenum readType = destType;
322     bool needsTempSurf = !GetActualReadFormats(gl,
323                                                destFormat, destType,
324                                                &readFormat, &readType);
325 
326     RefPtr<DataSourceSurface> tempSurf;
327     DataSourceSurface* readSurf = dest;
328     int readAlignment = GuessAlignment(dest->GetSize().width,
329                                        destPixelSize,
330                                        dest->Stride());
331     if (!readAlignment) {
332         needsTempSurf = true;
333     }
334     if (needsTempSurf) {
335         if (GLContext::ShouldSpew()) {
336             NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
337         }
338         SurfaceFormat readFormatGFX;
339 
340         switch (readFormat) {
341             case LOCAL_GL_RGBA: {
342                 readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8
343                                          : SurfaceFormat::R8G8B8X8;
344                 break;
345             }
346             case LOCAL_GL_BGRA: {
347                 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
348                                          : SurfaceFormat::B8G8R8X8;
349                 break;
350             }
351             case LOCAL_GL_RGB: {
352                 MOZ_ASSERT(destPixelSize == 2);
353                 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
354                 readFormatGFX = SurfaceFormat::R5G6B5_UINT16;
355                 break;
356             }
357             default: {
358                 MOZ_CRASH("GFX: Bad read format, read format.");
359             }
360         }
361 
362         switch (readType) {
363             case LOCAL_GL_UNSIGNED_BYTE: {
364                 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
365                 readAlignment = 1;
366                 break;
367             }
368             case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
369                 MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
370                 readAlignment = 4;
371                 break;
372             }
373             case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
374                 MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
375                 readAlignment = 2;
376                 break;
377             }
378             default: {
379                 MOZ_CRASH("GFX: Bad read type, read type.");
380             }
381         }
382 
383         int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
384         tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
385                                                               readFormatGFX,
386                                                               stride);
387         if (NS_WARN_IF(!tempSurf)) {
388             return;
389         }
390 
391         readSurf = tempSurf;
392     }
393     MOZ_ASSERT(readAlignment);
394     MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
395 
396     GLsizei width = dest->GetSize().width;
397     GLsizei height = dest->GetSize().height;
398 
399     {
400         ScopedPackState safePackState(gl);
401         gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
402 
403         gl->fReadPixels(0, 0,
404                         width, height,
405                         readFormat, readType,
406                         readSurf->GetData());
407     }
408 
409     if (readSurf != dest) {
410         MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
411         MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
412         gfx::Factory::CopyDataSourceSurface(readSurf, dest);
413     }
414 }
415 
416 already_AddRefed<gfx::DataSourceSurface>
YInvertImageSurface(gfx::DataSourceSurface * aSurf)417 YInvertImageSurface(gfx::DataSourceSurface* aSurf)
418 {
419     RefPtr<DataSourceSurface> temp =
420       Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
421                                                  aSurf->GetFormat(),
422                                                  aSurf->Stride());
423     if (NS_WARN_IF(!temp)) {
424         return nullptr;
425     }
426 
427     DataSourceSurface::MappedSurface map;
428     if (!temp->Map(DataSourceSurface::MapType::WRITE, &map)) {
429         return nullptr;
430     }
431 
432     RefPtr<DrawTarget> dt =
433       Factory::CreateDrawTargetForData(BackendType::CAIRO,
434                                        map.mData,
435                                        temp->GetSize(),
436                                        map.mStride,
437                                        temp->GetFormat());
438     if (!dt) {
439         temp->Unmap();
440         return nullptr;
441     }
442 
443     dt->SetTransform(Matrix::Scaling(1.0, -1.0) *
444                      Matrix::Translation(0.0, aSurf->GetSize().height));
445     Rect rect(0, 0, aSurf->GetSize().width, aSurf->GetSize().height);
446     dt->DrawSurface(aSurf, rect, rect, DrawSurfaceOptions(),
447                     DrawOptions(1.0, CompositionOp::OP_SOURCE, AntialiasMode::NONE));
448     temp->Unmap();
449     return temp.forget();
450 }
451 
452 already_AddRefed<DataSourceSurface>
ReadBackSurface(GLContext * gl,GLuint aTexture,bool aYInvert,SurfaceFormat aFormat)453 ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat)
454 {
455     gl->MakeCurrent();
456     gl->GuaranteeResolve();
457     gl->fActiveTexture(LOCAL_GL_TEXTURE0);
458     gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
459 
460     IntSize size;
461     gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width);
462     gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height);
463 
464     RefPtr<DataSourceSurface> surf =
465       Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8,
466                                                  GetAlignedStride<4>(size.width, BytesPerPixel(SurfaceFormat::B8G8R8A8)));
467 
468     if (NS_WARN_IF(!surf)) {
469         return nullptr;
470     }
471 
472     uint32_t currentPackAlignment = 0;
473     gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)&currentPackAlignment);
474     if (currentPackAlignment != 4) {
475         gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
476     }
477 
478     gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData());
479 
480     if (currentPackAlignment != 4) {
481         gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
482     }
483 
484     if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) {
485         SwapRAndBComponents(surf);
486     }
487 
488     if (aYInvert) {
489         surf = YInvertImageSurface(surf);
490     }
491 
492     return surf.forget();
493 }
494 
495 #define CLEANUP_IF_GLERROR_OCCURRED(x)                                      \
496     if (DidGLErrorOccur(x)) {                                               \
497         return false;                                                       \
498     }
499 
500 already_AddRefed<DataSourceSurface>
ReadTexImage(GLuint aTextureId,GLenum aTextureTarget,const gfx::IntSize & aSize,int aConfig,bool aYInvert)501 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
502                                    GLenum aTextureTarget,
503                                    const gfx::IntSize& aSize,
504     /* ShaderConfigOGL.mFeature */ int aConfig,
505                                    bool aYInvert)
506 {
507     /* Allocate resulting image surface */
508     int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
509     RefPtr<DataSourceSurface> isurf =
510         Factory::CreateDataSourceSurfaceWithStride(aSize,
511                                                    SurfaceFormat::R8G8B8A8,
512                                                    stride);
513     if (NS_WARN_IF(!isurf)) {
514         return nullptr;
515     }
516 
517     if (!ReadTexImage(isurf, aTextureId, aTextureTarget, aSize, aConfig, aYInvert)) {
518         return nullptr;
519     }
520 
521     return isurf.forget();
522 }
523 
524 bool
ReadTexImage(DataSourceSurface * aDest,GLuint aTextureId,GLenum aTextureTarget,const gfx::IntSize & aSize,int aConfig,bool aYInvert)525 GLReadTexImageHelper::ReadTexImage(DataSourceSurface* aDest,
526                                    GLuint aTextureId,
527                                    GLenum aTextureTarget,
528                                    const gfx::IntSize& aSize,
529     /* ShaderConfigOGL.mFeature */ int aConfig,
530                                    bool aYInvert)
531 {
532     MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
533                aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
534                aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
535 
536     mGL->MakeCurrent();
537 
538     GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
539     GLuint rb, fb;
540 
541     do {
542         mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
543         mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
544         mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
545         mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit);
546         mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
547         switch (aTextureTarget) {
548         case LOCAL_GL_TEXTURE_2D:
549             mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex);
550             break;
551         case LOCAL_GL_TEXTURE_EXTERNAL:
552             mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex);
553             break;
554         case LOCAL_GL_TEXTURE_RECTANGLE:
555             mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex);
556             break;
557         default: /* Already checked above */
558             break;
559         }
560 
561         ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false);
562         ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false);
563         ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height);
564 
565         /* Setup renderbuffer */
566         mGL->fGenRenderbuffers(1, &rb);
567         mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
568 
569         GLenum rbInternalFormat =
570             mGL->IsGLES()
571                 ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4)
572                 : LOCAL_GL_RGBA;
573         mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height);
574         CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
575 
576         /* Setup framebuffer */
577         mGL->fGenFramebuffers(1, &fb);
578         mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
579         mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
580                                       LOCAL_GL_RENDERBUFFER, rb);
581         CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
582 
583         MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE);
584 
585         /* Setup vertex and fragment shader */
586         GLuint program = TextureImageProgramFor(aTextureTarget, aConfig);
587         MOZ_ASSERT(program);
588 
589         mGL->fUseProgram(program);
590         CLEANUP_IF_GLERROR_OCCURRED("when using program");
591         mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0);
592         CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
593 
594         /* Setup quad geometry */
595         mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
596 
597         float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f;
598         float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f;
599 
600         const float
601         vertexArray[4*2] = {
602             -1.0f, -1.0f,
603             1.0f, -1.0f,
604             -1.0f,  1.0f,
605             1.0f,  1.0f
606         };
607         ScopedVertexAttribPointer autoAttrib0(mGL, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, vertexArray);
608 
609         const float u0 = 0.0f;
610         const float u1 = w;
611         const float v0 = aYInvert ? h : 0.0f;
612         const float v1 = aYInvert ? 0.0f : h;
613         const float texCoordArray[8] = { u0, v0,
614                                          u1, v0,
615                                          u0, v1,
616                                          u1, v1 };
617         ScopedVertexAttribPointer autoAttrib1(mGL, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, texCoordArray);
618 
619         /* Bind the texture */
620         if (aTextureId) {
621             mGL->fBindTexture(aTextureTarget, aTextureId);
622             CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
623         }
624 
625         /* Draw quad */
626         mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f);
627         mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
628         CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer");
629 
630         mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
631         CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
632 
633         /* Read-back draw results */
634         ReadPixelsIntoDataSurface(mGL, aDest);
635         CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
636     } while (false);
637 
638     /* Restore GL state */
639     mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
640     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
641     mGL->fUseProgram(oldprog);
642 
643     // note that deleting 0 has no effect in any of these calls
644     mGL->fDeleteRenderbuffers(1, &rb);
645     mGL->fDeleteFramebuffers(1, &fb);
646 
647     if (aTextureId)
648         mGL->fBindTexture(aTextureTarget, oldTex);
649 
650     if (oldTexUnit != LOCAL_GL_TEXTURE0)
651         mGL->fActiveTexture(oldTexUnit);
652 
653     return true;
654 }
655 
656 #undef CLEANUP_IF_GLERROR_OCCURRED
657 
658 } // namespace gl
659 } // namespace mozilla
660