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*)¤tPackAlignment);
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