Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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"
8 #include "GLContext.h"
9 #include "OGLShaderProgram.h"
10 #include "gfxTypes.h"
11 #include "gfxContext.h"
12 #include "gfxImageSurface.h"
13 #include "ScopedGLHelpers.h"
14 #include "mozilla/gfx/2D.h"
15 #include "gfx2DGlue.h"
17 using namespace mozilla::gfx;
19 namespace mozilla {
20 namespace gl {
22 GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl)
23 : mGL(gl)
24 {
25 mPrograms[0] = 0;
26 mPrograms[1] = 0;
27 mPrograms[2] = 0;
28 mPrograms[3] = 0;
29 }
31 GLReadTexImageHelper::~GLReadTexImageHelper()
32 {
33 mGL->fDeleteProgram(mPrograms[0]);
34 mGL->fDeleteProgram(mPrograms[1]);
35 mGL->fDeleteProgram(mPrograms[2]);
36 mGL->fDeleteProgram(mPrograms[3]);
37 }
39 static const GLchar
40 readTextureImageVS[] =
41 "attribute vec2 aVertex;\n"
42 "attribute vec2 aTexCoord;\n"
43 "varying vec2 vTexCoord;\n"
44 "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
46 static const GLchar
47 readTextureImageFS_TEXTURE_2D[] =
48 "#ifdef GL_ES\n"
49 "precision mediump float;\n"
50 "#endif\n"
51 "varying vec2 vTexCoord;\n"
52 "uniform sampler2D uTexture;\n"
53 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
56 static const GLchar
57 readTextureImageFS_TEXTURE_2D_BGRA[] =
58 "#ifdef GL_ES\n"
59 "precision mediump float;\n"
60 "#endif\n"
61 "varying vec2 vTexCoord;\n"
62 "uniform sampler2D uTexture;\n"
63 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
65 static const GLchar
66 readTextureImageFS_TEXTURE_EXTERNAL[] =
67 "#extension GL_OES_EGL_image_external : require\n"
68 "#ifdef GL_ES\n"
69 "precision mediump float;\n"
70 "#endif\n"
71 "varying vec2 vTexCoord;\n"
72 "uniform samplerExternalOES uTexture;\n"
73 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
75 static const GLchar
76 readTextureImageFS_TEXTURE_RECTANGLE[] =
77 "#extension GL_ARB_texture_rectangle\n"
78 "#ifdef GL_ES\n"
79 "precision mediump float;\n"
80 "#endif\n"
81 "varying vec2 vTexCoord;\n"
82 "uniform sampler2DRect uTexture;\n"
83 "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
85 GLuint
86 GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget, int aConfig) {
87 int variant = 0;
88 const GLchar* readTextureImageFS = nullptr;
89 if (aTextureTarget == LOCAL_GL_TEXTURE_2D)
90 {
91 if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP)
92 { // Need to swizzle R/B.
93 readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA;
94 variant = 1;
95 }
96 else
97 {
98 readTextureImageFS = readTextureImageFS_TEXTURE_2D;
99 variant = 0;
100 }
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;
107 }
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;
134 }
136 mGL->fDeleteShader(vs);
137 mGL->fDeleteShader(fs);
139 mPrograms[variant] = program;
140 }
142 return mPrograms[variant];
143 }
145 bool
146 GLReadTexImageHelper::DidGLErrorOccur(const char* str)
147 {
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;
153 }
155 return false;
156 }
158 static bool
159 GetActualReadFormats(GLContext* gl,
160 GLenum destFormat, GLenum destType,
161 GLenum& readFormat, GLenum& readType)
162 {
163 if (destFormat == LOCAL_GL_RGBA &&
164 destType == LOCAL_GL_UNSIGNED_BYTE)
165 {
166 readFormat = destFormat;
167 readType = destType;
168 return true;
169 }
171 bool fallback = true;
172 if (gl->IsGLES()) {
173 GLenum auxFormat = 0;
174 GLenum auxType = 0;
176 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat);
177 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType);
179 if (destFormat == auxFormat &&
180 destType == auxType)
181 {
182 fallback = false;
183 }
184 } else {
185 switch (destFormat) {
186 case LOCAL_GL_RGB: {
187 if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV)
188 fallback = false;
189 break;
190 }
191 case LOCAL_GL_BGRA: {
192 if (destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV)
193 fallback = false;
194 break;
195 }
196 }
197 }
199 if (fallback) {
200 readFormat = LOCAL_GL_RGBA;
201 readType = LOCAL_GL_UNSIGNED_BYTE;
202 return false;
203 } else {
204 readFormat = destFormat;
205 readType = destType;
206 return true;
207 }
208 }
210 static void SwapRAndBComponents(DataSourceSurface* surf)
211 {
212 uint8_t *row = surf->GetData();
213 if (!row) {
214 MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from DataSourceSurface.");
215 return;
216 }
218 size_t rowBytes = surf->GetSize().width*4;
219 size_t rowHole = surf->Stride() - rowBytes;
221 size_t rows = surf->GetSize().height;
223 while (rows) {
225 const uint8_t *rowEnd = row + rowBytes;
227 while (row != rowEnd) {
228 row[0] ^= row[2];
229 row[2] ^= row[0];
230 row[0] ^= row[2];
231 row += 4;
232 }
234 row += rowHole;
235 --rows;
236 }
237 }
239 static uint16_t PackRGB565(uint8_t r, uint8_t g, uint8_t b)
240 {
241 uint16_t pixel = ((r << 11) & 0xf800) |
242 ((g << 5) & 0x07e0) |
243 ((b ) & 0x001f);
245 return pixel;
246 }
248 static void CopyDataSourceSurface(DataSourceSurface* aSource,
249 DataSourceSurface* aDest)
250 {
251 MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
252 MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
253 aSource->GetFormat() == SurfaceFormat::R8G8B8X8);
255 uint8_t *srcRow = aSource->GetData();
256 size_t srcRowBytes = aSource->GetSize().width * BytesPerPixel(aSource->GetFormat());
257 size_t srcRowHole = aSource->Stride() - srcRowBytes;
259 uint8_t *destRow = aDest->GetData();
260 size_t destRowBytes = aDest->GetSize().width * BytesPerPixel(aDest->GetFormat());
261 size_t destRowHole = aDest->Stride() - destRowBytes;
263 bool needsRBSwap = false;
264 if (aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
265 aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
266 aDest->GetFormat() == SurfaceFormat::R5G6B5) {
267 needsRBSwap = true;
268 }
270 bool needsConvertTo16Bits = false;
271 if (aDest->GetFormat() == SurfaceFormat::R5G6B5) {
272 needsConvertTo16Bits = true;
273 }
275 size_t rows = aSource->GetSize().height;
277 while (rows) {
278 const uint8_t *srcRowEnd = srcRow + srcRowBytes;
280 while (srcRow != srcRowEnd) {
281 uint8_t r = needsRBSwap ? srcRow[2] : srcRow[0];
282 uint8_t g = srcRow[1];
283 uint8_t b = needsRBSwap ? srcRow[0] : srcRow[2];
284 uint8_t a = srcRow[3];
286 if (needsConvertTo16Bits) {
287 *(uint16_t*)destRow = PackRGB565(r, g, b);
288 } else {
289 destRow[0] = r;
290 destRow[1] = g;
291 destRow[2] = b;
292 destRow[3] = a;
293 }
294 srcRow += BytesPerPixel(aSource->GetFormat());
295 destRow += BytesPerPixel(aDest->GetFormat());
296 }
298 srcRow += srcRowHole;
299 destRow += destRowHole;
300 --rows;
301 }
302 }
304 static int
305 CalcStride(int width, int pixelSize, int alignment)
306 {
307 MOZ_ASSERT(alignment);
309 int stride = width * pixelSize;
310 if (stride % alignment) { // Extra at the end of the line?
311 int alignmentCount = stride / alignment;
312 stride = (alignmentCount+1) * alignment;
313 }
314 return stride;
315 }
317 static int
318 GuessAlignment(int width, int pixelSize, int stride)
319 {
320 int alignment = 8; // Max GLES allows.
321 while (CalcStride(width, pixelSize, alignment) != stride) {
322 alignment /= 2;
323 if (!alignment) {
324 MOZ_ASSERT(alignment);
325 return 1;
326 }
327 }
328 return alignment;
329 }
331 void
332 ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) {
333 gl->MakeCurrent();
334 MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0));
336 /* gfxImageFormat::ARGB32:
337 * RGBA+UByte: be[RGBA], le[ABGR]
338 * RGBA+UInt: be[ABGR], le[RGBA]
339 * BGRA+UInt: be[ARGB], le[BGRA]
340 * BGRA+UIntRev: be[BGRA], le[ARGB]
341 *
342 * gfxImageFormat::RGB16_565:
343 * RGB+UShort: le[rrrrrggg,gggbbbbb]
344 */
345 bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32;
347 int destPixelSize;
348 GLenum destFormat;
349 GLenum destType;
351 switch (dest->Format()) {
352 case gfxImageFormat::RGB24: // XRGB
353 case gfxImageFormat::ARGB32:
354 destPixelSize = 4;
355 // Needs host (little) endian ARGB.
356 destFormat = LOCAL_GL_BGRA;
357 destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
358 break;
360 case gfxImageFormat::RGB16_565:
361 destPixelSize = 2;
362 destFormat = LOCAL_GL_RGB;
363 destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
364 break;
366 default:
367 MOZ_CRASH("Bad format.");
368 }
369 MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride());
371 GLenum readFormat = destFormat;
372 GLenum readType = destType;
373 bool needsTempSurf = !GetActualReadFormats(gl,
374 destFormat, destType,
375 readFormat, readType);
377 nsAutoPtr<gfxImageSurface> tempSurf;
378 gfxImageSurface* readSurf = nullptr;
379 int readAlignment = 0;
380 if (needsTempSurf) {
381 if (gl->DebugMode()) {
382 NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
383 }
384 SurfaceFormat readFormatGFX;
386 switch (readFormat) {
387 case LOCAL_GL_RGBA:
388 case LOCAL_GL_BGRA: {
389 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
390 : SurfaceFormat::B8G8R8X8;
391 break;
392 }
393 case LOCAL_GL_RGB: {
394 MOZ_ASSERT(destPixelSize == 2);
395 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
396 readFormatGFX = SurfaceFormat::R5G6B5;
397 break;
398 }
399 default: {
400 MOZ_CRASH("Bad read format.");
401 }
402 }
404 switch (readType) {
405 case LOCAL_GL_UNSIGNED_BYTE: {
406 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
407 readAlignment = 1;
408 break;
409 }
410 case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
411 MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
412 readAlignment = 4;
413 break;
414 }
415 case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
416 MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
417 readAlignment = 2;
418 break;
419 }
420 default: {
421 MOZ_CRASH("Bad read type.");
422 }
423 }
425 tempSurf = new gfxImageSurface(dest->GetSize(),
426 SurfaceFormatToImageFormat(readFormatGFX),
427 false);
428 readSurf = tempSurf;
429 } else {
430 // Figure out alignment. We don't need to know why, we just need it
431 // to be valid.
432 readAlignment = GuessAlignment(dest->Width(),
433 destPixelSize,
434 dest->Stride());
435 readSurf = dest;
436 }
437 MOZ_ASSERT(readAlignment);
439 GLint currentPackAlignment = 0;
440 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment);
442 if (currentPackAlignment != readAlignment)
443 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
445 GLsizei width = dest->Width();
446 GLsizei height = dest->Height();
448 readSurf->Flush();
449 gl->fReadPixels(0, 0,
450 width, height,
451 readFormat, readType,
452 readSurf->Data());
453 readSurf->MarkDirty();
455 if (currentPackAlignment != readAlignment)
456 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
458 if (readSurf != dest) {
459 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
460 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
461 // So we just copied in RGBA in big endian, or le: 0xAABBGGRR.
462 // We want 0xAARRGGBB, so swap R and B:
463 dest->Flush();
464 RefPtr<DataSourceSurface> readDSurf =
465 Factory::CreateWrappingDataSourceSurface(readSurf->Data(),
466 readSurf->Stride(),
467 ToIntSize(readSurf->GetSize()),
468 ImageFormatToSurfaceFormat(readSurf->Format()));
469 SwapRAndBComponents(readDSurf);
470 dest->MarkDirty();
472 gfxContext ctx(dest);
473 ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
474 ctx.SetSource(readSurf);
475 ctx.Paint();
476 }
478 // Check if GL is giving back 1.0 alpha for
479 // RGBA reads to RGBA images from no-alpha buffers.
480 #ifdef XP_MACOSX
481 if (gl->WorkAroundDriverBugs() &&
482 gl->Vendor() == gl::GLVendor::NVIDIA &&
483 dest->Format() == gfxImageFormat::ARGB32 &&
484 width && height)
485 {
486 GLint alphaBits = 0;
487 gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
488 if (!alphaBits) {
489 const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);
491 MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride());
493 dest->Flush();
494 uint32_t* itr = (uint32_t*)dest->Data();
495 uint32_t testPixel = *itr;
496 if ((testPixel & alphaMask) != alphaMask) {
497 // We need to set the alpha channel to 1.0 manually.
498 uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4.
500 for (; itr != itrEnd; itr++) {
501 *itr |= alphaMask;
502 }
503 }
504 dest->MarkDirty();
505 }
506 }
507 #endif
508 }
510 void
511 ReadPixelsIntoDataSourceSurface(GLContext* gl, DataSourceSurface* dest) {
512 gl->MakeCurrent();
513 MOZ_ASSERT(dest->GetSize().width != 0);
514 MOZ_ASSERT(dest->GetSize().height != 0);
516 bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
517 dest->GetFormat() == SurfaceFormat::R8G8B8A8;
519 int destPixelSize;
520 GLenum destFormat;
521 GLenum destType;
523 switch (dest->GetFormat()) {
524 case SurfaceFormat::B8G8R8A8:
525 case SurfaceFormat::B8G8R8X8:
526 // Needs host (little) endian ARGB.
527 destFormat = LOCAL_GL_BGRA;
528 destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
529 break;
530 case SurfaceFormat::R8G8B8A8:
531 case SurfaceFormat::R8G8B8X8:
532 // Needs host (little) endian ABGR.
533 destFormat = LOCAL_GL_RGBA;
534 destType = LOCAL_GL_UNSIGNED_BYTE;
535 break;
536 case SurfaceFormat::R5G6B5:
537 destFormat = LOCAL_GL_RGB;
538 destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
539 break;
540 default:
541 MOZ_CRASH("Bad format.");
542 }
543 destPixelSize = BytesPerPixel(dest->GetFormat());
544 MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
546 GLenum readFormat = destFormat;
547 GLenum readType = destType;
548 bool needsTempSurf = !GetActualReadFormats(gl,
549 destFormat, destType,
550 readFormat, readType);
552 RefPtr<DataSourceSurface> tempSurf;
553 DataSourceSurface* readSurf = nullptr;
554 int readAlignment = 0;
555 if (needsTempSurf) {
556 if (gl->DebugMode()) {
557 NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
558 }
559 SurfaceFormat readFormatGFX;
561 // If needs temp surface, readFormat is always LOCAL_GL_RGBA
562 // and readType is always LOCAL_GL_UNSIGNED_BYTE
563 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
564 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
565 readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8
566 : SurfaceFormat::R8G8B8X8;
567 readAlignment = 1;
568 int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
569 tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
570 readFormatGFX,
571 stride);
572 readSurf = tempSurf;
573 } else {
574 // Figure out alignment. We don't need to know why, we just need it
575 // to be valid.
576 readAlignment = GuessAlignment(dest->GetSize().width,
577 destPixelSize,
578 dest->Stride());
579 readSurf = dest;
580 }
581 MOZ_ASSERT(readAlignment);
582 MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
584 GLint currentPackAlignment = 0;
585 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment);
587 if (currentPackAlignment != readAlignment)
588 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
590 GLsizei width = dest->GetSize().width;
591 GLsizei height = dest->GetSize().height;
593 gl->fReadPixels(0, 0,
594 width, height,
595 readFormat, readType,
596 readSurf->GetData());
598 if (currentPackAlignment != readAlignment)
599 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
601 if (readSurf != dest) {
602 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
603 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
604 CopyDataSourceSurface(readSurf, dest);
605 }
607 // Check if GL is giving back 1.0 alpha for
608 // RGBA reads to RGBA images from no-alpha buffers.
609 #ifdef XP_MACOSX
610 if (gl->WorkAroundDriverBugs() &&
611 gl->Vendor() == gl::GLVendor::NVIDIA &&
612 (dest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
613 dest->GetFormat() == SurfaceFormat::B8G8R8A8) &&
614 width && height)
615 {
616 GLint alphaBits = 0;
617 gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
618 if (!alphaBits) {
619 const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);
621 MOZ_ASSERT(dest->GetSize().width * destPixelSize == dest->Stride());
623 uint32_t* itr = (uint32_t*)dest->GetData();
624 uint32_t testPixel = *itr;
625 if ((testPixel & alphaMask) != alphaMask) {
626 // We need to set the alpha channel to 1.0 manually.
627 uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4.
629 for (; itr != itrEnd; itr++) {
630 *itr |= alphaMask;
631 }
632 }
633 }
634 }
635 #endif
636 }
638 static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf)
639 {
640 RefPtr<DataSourceSurface> temp =
641 Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
642 aSurf->GetFormat(),
643 aSurf->Stride());
644 RefPtr<DrawTarget> dt =
645 Factory::CreateDrawTargetForData(BackendType::CAIRO,
646 temp->GetData(),
647 temp->GetSize(),
648 temp->Stride(),
649 temp->GetFormat());
650 nsRefPtr<gfxContext> ctx = new gfxContext(dt);
651 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
652 ctx->Scale(1.0, -1.0);
653 ctx->Translate(-gfxPoint(0.0, aSurf->GetSize().height));
655 nsRefPtr<gfxImageSurface> thebesSurf =
656 new gfxImageSurface(aSurf->GetData(),
657 ThebesIntSize(aSurf->GetSize()),
658 aSurf->Stride(),
659 SurfaceFormatToImageFormat(aSurf->GetFormat()));
660 ctx->SetSource(thebesSurf);
661 ctx->Paint();
662 return temp.forget();
663 }
665 TemporaryRef<DataSourceSurface>
666 ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat)
667 {
668 gl->MakeCurrent();
669 gl->GuaranteeResolve();
670 gl->fActiveTexture(LOCAL_GL_TEXTURE0);
671 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
673 IntSize size;
674 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width);
675 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height);
677 RefPtr<DataSourceSurface> surf =
678 Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8,
679 GetAlignedStride<4>(size.width * BytesPerPixel(SurfaceFormat::B8G8R8A8)));
681 if (!surf) {
682 return nullptr;
683 }
685 uint32_t currentPackAlignment = 0;
686 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)¤tPackAlignment);
687 if (currentPackAlignment != 4) {
688 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
689 }
690 gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData());
691 if (currentPackAlignment != 4) {
692 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
693 }
695 if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) {
696 SwapRAndBComponents(surf);
697 }
699 if (aYInvert) {
700 surf = YInvertImageSurface(surf);
701 }
703 return surf.forget();
704 }
706 void
707 ReadScreenIntoImageSurface(GLContext* gl, gfxImageSurface* dest)
708 {
709 ScopedBindFramebuffer autoFB(gl, 0);
710 ReadPixelsIntoImageSurface(gl, dest);
711 }
714 #define CLEANUP_IF_GLERROR_OCCURRED(x) \
715 if (DidGLErrorOccur(x)) { \
716 isurf = nullptr; \
717 break; \
718 }
720 TemporaryRef<DataSourceSurface>
721 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
722 GLenum aTextureTarget,
723 const gfx::IntSize& aSize,
724 /* ShaderConfigOGL.mFeature */ int aConfig,
725 bool aYInvert)
726 {
727 MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
728 aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
729 aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
731 mGL->MakeCurrent();
733 /* Allocate resulting image surface */
734 int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
735 RefPtr<DataSourceSurface> isurf =
736 Factory::CreateDataSourceSurfaceWithStride(aSize,
737 SurfaceFormat::R8G8B8A8,
738 stride);
740 GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
741 GLuint rb, fb;
743 do {
744 mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
745 mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
746 mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
747 mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit);
748 mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
749 switch (aTextureTarget) {
750 case LOCAL_GL_TEXTURE_2D:
751 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex);
752 break;
753 case LOCAL_GL_TEXTURE_EXTERNAL:
754 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex);
755 break;
756 case LOCAL_GL_TEXTURE_RECTANGLE:
757 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex);
758 break;
759 default: /* Already checked above */
760 break;
761 }
763 ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false);
764 ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false);
765 ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height);
767 /* Setup renderbuffer */
768 mGL->fGenRenderbuffers(1, &rb);
769 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
771 GLenum rbInternalFormat =
772 mGL->IsGLES()
773 ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4)
774 : LOCAL_GL_RGBA;
775 mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height);
776 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
778 /* Setup framebuffer */
779 mGL->fGenFramebuffers(1, &fb);
780 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
781 mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
782 LOCAL_GL_RENDERBUFFER, rb);
783 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
785 MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE);
787 /* Setup vertex and fragment shader */
788 GLuint program = TextureImageProgramFor(aTextureTarget, aConfig);
789 MOZ_ASSERT(program);
791 mGL->fUseProgram(program);
792 CLEANUP_IF_GLERROR_OCCURRED("when using program");
793 mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0);
794 CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
796 /* Setup quad geometry */
797 mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
798 mGL->fEnableVertexAttribArray(0);
799 mGL->fEnableVertexAttribArray(1);
801 float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f;
802 float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f;
805 const float
806 vertexArray[4*2] = {
807 -1.0f, -1.0f,
808 1.0f, -1.0f,
809 -1.0f, 1.0f,
810 1.0f, 1.0f
811 };
812 mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, vertexArray);
814 const float u0 = 0.0f;
815 const float u1 = w;
816 const float v0 = aYInvert ? h : 0.0f;
817 const float v1 = aYInvert ? 0.0f : h;
818 const float texCoordArray[8] = { u0, v0,
819 u1, v0,
820 u0, v1,
821 u1, v1 };
822 mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, texCoordArray);
824 /* Bind the texture */
825 if (aTextureId) {
826 mGL->fBindTexture(aTextureTarget, aTextureId);
827 CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
828 }
830 /* Draw quad */
831 mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f);
832 mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
833 CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer");
835 mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
836 CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
838 mGL->fDisableVertexAttribArray(1);
839 mGL->fDisableVertexAttribArray(0);
841 /* Read-back draw results */
842 ReadPixelsIntoDataSourceSurface(mGL, isurf);
843 CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
844 } while (false);
846 /* Restore GL state */
847 //cleanup:
848 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
849 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
850 mGL->fUseProgram(oldprog);
852 // note that deleting 0 has no effect in any of these calls
853 mGL->fDeleteRenderbuffers(1, &rb);
854 mGL->fDeleteFramebuffers(1, &fb);
856 if (aTextureId)
857 mGL->fBindTexture(aTextureTarget, oldTex);
859 if (oldTexUnit != LOCAL_GL_TEXTURE0)
860 mGL->fActiveTexture(oldTexUnit);
862 return isurf.forget();
863 }
865 #undef CLEANUP_IF_GLERROR_OCCURRED
868 }
869 }