Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / gfx / gl / GLReadTexImageHelper.cpp
blob2b72cc139f75a185752384e0d4c1730e3113bda4
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/. */
7 #include "GLReadTexImageHelper.h"
9 #include "gfx2DGlue.h"
10 #include "gfxTypes.h"
11 #include "GLContext.h"
12 #include "OGLShaderProgram.h"
13 #include "ScopedGLHelpers.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/Move.h"
18 namespace mozilla {
19 namespace gl {
21 using namespace mozilla::gfx;
23 GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl)
24 : mGL(gl)
26 mPrograms[0] = 0;
27 mPrograms[1] = 0;
28 mPrograms[2] = 0;
29 mPrograms[3] = 0;
32 GLReadTexImageHelper::~GLReadTexImageHelper()
34 mGL->fDeleteProgram(mPrograms[0]);
35 mGL->fDeleteProgram(mPrograms[1]);
36 mGL->fDeleteProgram(mPrograms[2]);
37 mGL->fDeleteProgram(mPrograms[3]);
40 static const GLchar
41 readTextureImageVS[] =
42 "attribute vec2 aVertex;\n"
43 "attribute vec2 aTexCoord;\n"
44 "varying vec2 vTexCoord;\n"
45 "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
47 static const GLchar
48 readTextureImageFS_TEXTURE_2D[] =
49 "#ifdef GL_ES\n"
50 "precision mediump float;\n"
51 "#endif\n"
52 "varying vec2 vTexCoord;\n"
53 "uniform sampler2D uTexture;\n"
54 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
57 static const GLchar
58 readTextureImageFS_TEXTURE_2D_BGRA[] =
59 "#ifdef GL_ES\n"
60 "precision mediump float;\n"
61 "#endif\n"
62 "varying vec2 vTexCoord;\n"
63 "uniform sampler2D uTexture;\n"
64 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
66 static const GLchar
67 readTextureImageFS_TEXTURE_EXTERNAL[] =
68 "#extension GL_OES_EGL_image_external : require\n"
69 "#ifdef GL_ES\n"
70 "precision mediump float;\n"
71 "#endif\n"
72 "varying vec2 vTexCoord;\n"
73 "uniform samplerExternalOES uTexture;\n"
74 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
76 static const GLchar
77 readTextureImageFS_TEXTURE_RECTANGLE[] =
78 "#extension GL_ARB_texture_rectangle\n"
79 "#ifdef GL_ES\n"
80 "precision mediump float;\n"
81 "#endif\n"
82 "varying vec2 vTexCoord;\n"
83 "uniform sampler2DRect uTexture;\n"
84 "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
86 GLuint
87 GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget,
88 int aConfig)
90 int variant = 0;
91 const GLchar* readTextureImageFS = nullptr;
92 if (aTextureTarget == LOCAL_GL_TEXTURE_2D) {
93 if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) {
94 // Need to swizzle R/B.
95 readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA;
96 variant = 1;
97 } else {
98 readTextureImageFS = readTextureImageFS_TEXTURE_2D;
99 variant = 0;
101 } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
102 readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL;
103 variant = 2;
104 } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
105 readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE;
106 variant = 3;
109 /* This might be overkill, but assure that we don't access out-of-bounds */
110 MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms));
111 if (!mPrograms[variant]) {
112 GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
113 const GLchar* vsSourcePtr = &readTextureImageVS[0];
114 mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr);
115 mGL->fCompileShader(vs);
117 GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
118 mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr);
119 mGL->fCompileShader(fs);
121 GLuint program = mGL->fCreateProgram();
122 mGL->fAttachShader(program, vs);
123 mGL->fAttachShader(program, fs);
124 mGL->fBindAttribLocation(program, 0, "aVertex");
125 mGL->fBindAttribLocation(program, 1, "aTexCoord");
126 mGL->fLinkProgram(program);
128 GLint success;
129 mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success);
131 if (!success) {
132 mGL->fDeleteProgram(program);
133 program = 0;
136 mGL->fDeleteShader(vs);
137 mGL->fDeleteShader(fs);
139 mPrograms[variant] = program;
142 return mPrograms[variant];
145 bool
146 GLReadTexImageHelper::DidGLErrorOccur(const char* str)
148 GLenum error = mGL->fGetError();
149 if (error != LOCAL_GL_NO_ERROR) {
150 printf_stderr("GL ERROR: %s (0x%04x) %s\n",
151 mGL->GLErrorToString(error), error, str);
152 return true;
155 return false;
158 bool
159 GetActualReadFormats(GLContext* gl,
160 GLenum destFormat, GLenum destType,
161 GLenum* out_readFormat, GLenum* out_readType)
163 MOZ_ASSERT(out_readFormat);
164 MOZ_ASSERT(out_readType);
166 if (destFormat == LOCAL_GL_RGBA &&
167 destType == LOCAL_GL_UNSIGNED_BYTE)
169 *out_readFormat = destFormat;
170 *out_readType = destType;
171 return true;
174 bool fallback = true;
175 if (gl->IsGLES()) {
176 GLenum auxFormat = 0;
177 GLenum auxType = 0;
179 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat);
180 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType);
182 if (destFormat == auxFormat &&
183 destType == auxType)
185 fallback = false;
187 } else {
188 switch (destFormat) {
189 case LOCAL_GL_RGB: {
190 if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV)
191 fallback = false;
192 break;
194 case LOCAL_GL_BGRA: {
195 if (destType == LOCAL_GL_UNSIGNED_BYTE ||
196 destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV)
198 fallback = false;
200 break;
205 if (fallback) {
206 *out_readFormat = LOCAL_GL_RGBA;
207 *out_readType = LOCAL_GL_UNSIGNED_BYTE;
208 return false;
209 } else {
210 *out_readFormat = destFormat;
211 *out_readType = destType;
212 return true;
216 static void
217 SwapRAndBComponents(DataSourceSurface* surf)
219 DataSourceSurface::MappedSurface map;
220 MOZ_ALWAYS_TRUE( surf->Map(DataSourceSurface::MapType::READ_WRITE, &map) );
221 MOZ_ASSERT(map.mStride >= 0);
223 const size_t rowBytes = surf->GetSize().width*4;
224 const size_t rowHole = map.mStride - rowBytes;
226 uint8_t* row = map.mData;
227 if (!row) {
228 MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from"
229 " DataSourceSurface.");
230 surf->Unmap();
231 return;
234 const size_t rows = surf->GetSize().height;
235 for (size_t i = 0; i < rows; i++) {
236 const uint8_t* rowEnd = row + rowBytes;
238 while (row != rowEnd) {
239 Swap(row[0], row[2]);
240 row += 4;
243 row += rowHole;
246 surf->Unmap();
249 static uint16_t
250 PackRGB565(uint8_t r, uint8_t g, uint8_t b)
252 uint16_t pixel = ((r << 11) & 0xf800) |
253 ((g << 5) & 0x07e0) |
254 ((b ) & 0x001f);
256 return pixel;
259 static void
260 CopyDataSourceSurface(DataSourceSurface* aSource,
261 DataSourceSurface* aDest)
263 // Don't worry too much about speed.
264 MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
265 MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
266 aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
267 aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
268 aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
269 MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
270 aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
271 aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
272 aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
273 aDest->GetFormat() == SurfaceFormat::R5G6B5);
275 const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
276 aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
277 const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
278 aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
279 const bool needsSwap02 = isSrcBGR != isDestBGR;
281 const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
282 aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
283 const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
284 aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
285 const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
287 const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5;
289 DataSourceSurface::MappedSurface srcMap;
290 DataSourceSurface::MappedSurface destMap;
291 MOZ_ALWAYS_TRUE( aSource->Map(DataSourceSurface::MapType::READ, &srcMap) );
292 MOZ_ALWAYS_TRUE( aDest->Map(DataSourceSurface::MapType::WRITE, &destMap) );
293 MOZ_ASSERT(srcMap.mStride >= 0);
294 MOZ_ASSERT(destMap.mStride >= 0);
296 const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
297 const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
298 const size_t srcRowHole = srcMap.mStride - srcRowBytes;
300 const size_t destBPP = BytesPerPixel(aDest->GetFormat());
301 const size_t destRowBytes = aDest->GetSize().width * destBPP;
302 const size_t destRowHole = destMap.mStride - destRowBytes;
304 uint8_t* srcRow = srcMap.mData;
305 uint8_t* destRow = destMap.mData;
306 const size_t rows = aSource->GetSize().height;
307 for (size_t i = 0; i < rows; i++) {
308 const uint8_t* srcRowEnd = srcRow + srcRowBytes;
310 while (srcRow != srcRowEnd) {
311 uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
312 uint8_t d1 = srcRow[1];
313 uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
314 uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
316 if (needsConvertTo16Bits) {
317 *(uint16_t*)destRow = PackRGB565(d0, d1, d2);
318 } else {
319 destRow[0] = d0;
320 destRow[1] = d1;
321 destRow[2] = d2;
322 destRow[3] = d3;
324 srcRow += srcBPP;
325 destRow += destBPP;
328 srcRow += srcRowHole;
329 destRow += destRowHole;
332 aSource->Unmap();
333 aDest->Unmap();
336 static int
337 CalcRowStride(int width, int pixelSize, int alignment)
339 MOZ_ASSERT(alignment);
341 int rowStride = width * pixelSize;
342 if (rowStride % alignment) { // Extra at the end of the line?
343 int alignmentCount = rowStride / alignment;
344 rowStride = (alignmentCount+1) * alignment;
346 return rowStride;
349 static int
350 GuessAlignment(int width, int pixelSize, int rowStride)
352 int alignment = 8; // Max GLES allows.
353 while (CalcRowStride(width, pixelSize, alignment) != rowStride) {
354 alignment /= 2;
355 if (!alignment) {
356 NS_WARNING("Bad alignment for GLES. Will use temp surf for readback.");
357 return 0;
360 return alignment;
363 void
364 ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
366 gl->MakeCurrent();
367 MOZ_ASSERT(dest->GetSize().width != 0);
368 MOZ_ASSERT(dest->GetSize().height != 0);
370 bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
371 dest->GetFormat() == SurfaceFormat::R8G8B8A8;
373 int destPixelSize;
374 GLenum destFormat;
375 GLenum destType;
377 switch (dest->GetFormat()) {
378 case SurfaceFormat::B8G8R8A8:
379 case SurfaceFormat::B8G8R8X8:
380 // Needs host (little) endian ARGB.
381 destFormat = LOCAL_GL_BGRA;
382 destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
383 break;
384 case SurfaceFormat::R8G8B8A8:
385 case SurfaceFormat::R8G8B8X8:
386 // Needs host (little) endian ABGR.
387 destFormat = LOCAL_GL_RGBA;
388 destType = LOCAL_GL_UNSIGNED_BYTE;
389 break;
390 case SurfaceFormat::R5G6B5:
391 destFormat = LOCAL_GL_RGB;
392 destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
393 break;
394 default:
395 MOZ_CRASH("Bad format.");
397 destPixelSize = BytesPerPixel(dest->GetFormat());
398 MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
400 GLenum readFormat = destFormat;
401 GLenum readType = destType;
402 bool needsTempSurf = !GetActualReadFormats(gl,
403 destFormat, destType,
404 &readFormat, &readType);
406 RefPtr<DataSourceSurface> tempSurf;
407 DataSourceSurface* readSurf = dest;
408 int readAlignment = GuessAlignment(dest->GetSize().width,
409 destPixelSize,
410 dest->Stride());
411 if (!readAlignment) {
412 needsTempSurf = true;
414 if (needsTempSurf) {
415 if (gl->DebugMode()) {
416 NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
418 SurfaceFormat readFormatGFX;
420 switch (readFormat) {
421 case LOCAL_GL_RGBA: {
422 readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8
423 : SurfaceFormat::R8G8B8X8;
424 break;
426 case LOCAL_GL_BGRA: {
427 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
428 : SurfaceFormat::B8G8R8X8;
429 break;
431 case LOCAL_GL_RGB: {
432 MOZ_ASSERT(destPixelSize == 2);
433 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
434 readFormatGFX = SurfaceFormat::R5G6B5;
435 break;
437 default: {
438 MOZ_CRASH("Bad read format.");
442 switch (readType) {
443 case LOCAL_GL_UNSIGNED_BYTE: {
444 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
445 readAlignment = 1;
446 break;
448 case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
449 MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
450 readAlignment = 4;
451 break;
453 case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
454 MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
455 readAlignment = 2;
456 break;
458 default: {
459 MOZ_CRASH("Bad read type.");
463 int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
464 tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
465 readFormatGFX,
466 stride);
467 if (NS_WARN_IF(!tempSurf)) {
468 return;
471 readSurf = tempSurf;
473 MOZ_ASSERT(readAlignment);
474 MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
476 GLint currentPackAlignment = 0;
477 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &currentPackAlignment);
479 if (currentPackAlignment != readAlignment)
480 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
482 GLsizei width = dest->GetSize().width;
483 GLsizei height = dest->GetSize().height;
485 gl->fReadPixels(0, 0,
486 width, height,
487 readFormat, readType,
488 readSurf->GetData());
490 if (currentPackAlignment != readAlignment)
491 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
493 if (readSurf != dest) {
494 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
495 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
496 CopyDataSourceSurface(readSurf, dest);
499 // Check if GL is giving back 1.0 alpha for
500 // RGBA reads to RGBA images from no-alpha buffers.
501 #ifdef XP_MACOSX
502 if (gl->WorkAroundDriverBugs() &&
503 gl->Vendor() == gl::GLVendor::NVIDIA &&
504 hasAlpha &&
505 width && height)
507 GLint alphaBits = 0;
508 gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
509 if (!alphaBits) {
510 const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);
512 MOZ_ASSERT(dest->GetSize().width * destPixelSize == dest->Stride());
514 uint32_t* itr = (uint32_t*)dest->GetData();
515 uint32_t testPixel = *itr;
516 if ((testPixel & alphaMask) != alphaMask) {
517 // We need to set the alpha channel to 1.0 manually.
518 uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4.
520 for (; itr != itrEnd; itr++) {
521 *itr |= alphaMask;
526 #endif
529 static TemporaryRef<DataSourceSurface>
530 YInvertImageSurface(DataSourceSurface* aSurf)
532 RefPtr<DataSourceSurface> temp =
533 Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
534 aSurf->GetFormat(),
535 aSurf->Stride());
536 if (NS_WARN_IF(!temp)) {
537 return nullptr;
540 DataSourceSurface::MappedSurface map;
541 if (!temp->Map(DataSourceSurface::MapType::WRITE, &map)) {
542 return nullptr;
545 RefPtr<DrawTarget> dt =
546 Factory::CreateDrawTargetForData(BackendType::CAIRO,
547 map.mData,
548 temp->GetSize(),
549 map.mStride,
550 temp->GetFormat());
551 if (!dt) {
552 temp->Unmap();
553 return nullptr;
556 dt->SetTransform(Matrix::Translation(0.0, aSurf->GetSize().height) *
557 Matrix::Scaling(1.0, -1.0));
558 Rect rect(0, 0, aSurf->GetSize().width, aSurf->GetSize().height);
559 dt->DrawSurface(aSurf, rect, rect, DrawSurfaceOptions(),
560 DrawOptions(1.0, CompositionOp::OP_SOURCE, AntialiasMode::NONE));
561 temp->Unmap();
562 return temp.forget();
565 TemporaryRef<DataSourceSurface>
566 ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat)
568 gl->MakeCurrent();
569 gl->GuaranteeResolve();
570 gl->fActiveTexture(LOCAL_GL_TEXTURE0);
571 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
573 IntSize size;
574 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width);
575 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height);
577 RefPtr<DataSourceSurface> surf =
578 Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8,
579 GetAlignedStride<4>(size.width * BytesPerPixel(SurfaceFormat::B8G8R8A8)));
581 if (NS_WARN_IF(!surf)) {
582 return nullptr;
585 uint32_t currentPackAlignment = 0;
586 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)&currentPackAlignment);
587 if (currentPackAlignment != 4) {
588 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
591 gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData());
593 if (currentPackAlignment != 4) {
594 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
597 if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) {
598 SwapRAndBComponents(surf);
601 if (aYInvert) {
602 surf = YInvertImageSurface(surf);
605 return surf.forget();
608 #define CLEANUP_IF_GLERROR_OCCURRED(x) \
609 if (DidGLErrorOccur(x)) { \
610 isurf = nullptr; \
611 break; \
614 TemporaryRef<DataSourceSurface>
615 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
616 GLenum aTextureTarget,
617 const gfx::IntSize& aSize,
618 /* ShaderConfigOGL.mFeature */ int aConfig,
619 bool aYInvert)
621 MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
622 aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
623 aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
625 mGL->MakeCurrent();
627 /* Allocate resulting image surface */
628 int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
629 RefPtr<DataSourceSurface> isurf =
630 Factory::CreateDataSourceSurfaceWithStride(aSize,
631 SurfaceFormat::R8G8B8A8,
632 stride);
633 if (NS_WARN_IF(!isurf)) {
634 return nullptr;
637 GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
638 GLuint rb, fb;
640 do {
641 mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
642 mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
643 mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
644 mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit);
645 mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
646 switch (aTextureTarget) {
647 case LOCAL_GL_TEXTURE_2D:
648 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex);
649 break;
650 case LOCAL_GL_TEXTURE_EXTERNAL:
651 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex);
652 break;
653 case LOCAL_GL_TEXTURE_RECTANGLE:
654 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex);
655 break;
656 default: /* Already checked above */
657 break;
660 ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false);
661 ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false);
662 ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height);
664 /* Setup renderbuffer */
665 mGL->fGenRenderbuffers(1, &rb);
666 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
668 GLenum rbInternalFormat =
669 mGL->IsGLES()
670 ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4)
671 : LOCAL_GL_RGBA;
672 mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height);
673 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
675 /* Setup framebuffer */
676 mGL->fGenFramebuffers(1, &fb);
677 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
678 mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
679 LOCAL_GL_RENDERBUFFER, rb);
680 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
682 MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE);
684 /* Setup vertex and fragment shader */
685 GLuint program = TextureImageProgramFor(aTextureTarget, aConfig);
686 MOZ_ASSERT(program);
688 mGL->fUseProgram(program);
689 CLEANUP_IF_GLERROR_OCCURRED("when using program");
690 mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0);
691 CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
693 /* Setup quad geometry */
694 mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
696 float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f;
697 float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f;
699 const float
700 vertexArray[4*2] = {
701 -1.0f, -1.0f,
702 1.0f, -1.0f,
703 -1.0f, 1.0f,
704 1.0f, 1.0f
706 ScopedVertexAttribPointer autoAttrib0(mGL, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, vertexArray);
708 const float u0 = 0.0f;
709 const float u1 = w;
710 const float v0 = aYInvert ? h : 0.0f;
711 const float v1 = aYInvert ? 0.0f : h;
712 const float texCoordArray[8] = { u0, v0,
713 u1, v0,
714 u0, v1,
715 u1, v1 };
716 ScopedVertexAttribPointer autoAttrib1(mGL, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, texCoordArray);
718 /* Bind the texture */
719 if (aTextureId) {
720 mGL->fBindTexture(aTextureTarget, aTextureId);
721 CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
724 /* Draw quad */
725 mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f);
726 mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
727 CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer");
729 mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
730 CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
732 /* Read-back draw results */
733 ReadPixelsIntoDataSurface(mGL, isurf);
734 CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
735 } while (false);
737 /* Restore GL state */
738 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
739 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
740 mGL->fUseProgram(oldprog);
742 // note that deleting 0 has no effect in any of these calls
743 mGL->fDeleteRenderbuffers(1, &rb);
744 mGL->fDeleteFramebuffers(1, &fb);
746 if (aTextureId)
747 mGL->fBindTexture(aTextureTarget, oldTex);
749 if (oldTexUnit != LOCAL_GL_TEXTURE0)
750 mGL->fActiveTexture(oldTexUnit);
752 return isurf.forget();
755 #undef CLEANUP_IF_GLERROR_OCCURRED