1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/gl/GLReadTexImageHelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,869 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "GLReadTexImageHelper.h" 1.11 +#include "GLContext.h" 1.12 +#include "OGLShaderProgram.h" 1.13 +#include "gfxTypes.h" 1.14 +#include "gfxContext.h" 1.15 +#include "gfxImageSurface.h" 1.16 +#include "ScopedGLHelpers.h" 1.17 +#include "mozilla/gfx/2D.h" 1.18 +#include "gfx2DGlue.h" 1.19 + 1.20 +using namespace mozilla::gfx; 1.21 + 1.22 +namespace mozilla { 1.23 +namespace gl { 1.24 + 1.25 +GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl) 1.26 + : mGL(gl) 1.27 +{ 1.28 + mPrograms[0] = 0; 1.29 + mPrograms[1] = 0; 1.30 + mPrograms[2] = 0; 1.31 + mPrograms[3] = 0; 1.32 +} 1.33 + 1.34 +GLReadTexImageHelper::~GLReadTexImageHelper() 1.35 +{ 1.36 + mGL->fDeleteProgram(mPrograms[0]); 1.37 + mGL->fDeleteProgram(mPrograms[1]); 1.38 + mGL->fDeleteProgram(mPrograms[2]); 1.39 + mGL->fDeleteProgram(mPrograms[3]); 1.40 +} 1.41 + 1.42 +static const GLchar 1.43 +readTextureImageVS[] = 1.44 + "attribute vec2 aVertex;\n" 1.45 + "attribute vec2 aTexCoord;\n" 1.46 + "varying vec2 vTexCoord;\n" 1.47 + "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }"; 1.48 + 1.49 +static const GLchar 1.50 +readTextureImageFS_TEXTURE_2D[] = 1.51 + "#ifdef GL_ES\n" 1.52 + "precision mediump float;\n" 1.53 + "#endif\n" 1.54 + "varying vec2 vTexCoord;\n" 1.55 + "uniform sampler2D uTexture;\n" 1.56 + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; 1.57 + 1.58 + 1.59 +static const GLchar 1.60 +readTextureImageFS_TEXTURE_2D_BGRA[] = 1.61 + "#ifdef GL_ES\n" 1.62 + "precision mediump float;\n" 1.63 + "#endif\n" 1.64 + "varying vec2 vTexCoord;\n" 1.65 + "uniform sampler2D uTexture;\n" 1.66 + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }"; 1.67 + 1.68 +static const GLchar 1.69 +readTextureImageFS_TEXTURE_EXTERNAL[] = 1.70 + "#extension GL_OES_EGL_image_external : require\n" 1.71 + "#ifdef GL_ES\n" 1.72 + "precision mediump float;\n" 1.73 + "#endif\n" 1.74 + "varying vec2 vTexCoord;\n" 1.75 + "uniform samplerExternalOES uTexture;\n" 1.76 + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; 1.77 + 1.78 +static const GLchar 1.79 +readTextureImageFS_TEXTURE_RECTANGLE[] = 1.80 + "#extension GL_ARB_texture_rectangle\n" 1.81 + "#ifdef GL_ES\n" 1.82 + "precision mediump float;\n" 1.83 + "#endif\n" 1.84 + "varying vec2 vTexCoord;\n" 1.85 + "uniform sampler2DRect uTexture;\n" 1.86 + "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }"; 1.87 + 1.88 +GLuint 1.89 +GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget, int aConfig) { 1.90 + int variant = 0; 1.91 + const GLchar* readTextureImageFS = nullptr; 1.92 + if (aTextureTarget == LOCAL_GL_TEXTURE_2D) 1.93 + { 1.94 + if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) 1.95 + { // Need to swizzle R/B. 1.96 + readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA; 1.97 + variant = 1; 1.98 + } 1.99 + else 1.100 + { 1.101 + readTextureImageFS = readTextureImageFS_TEXTURE_2D; 1.102 + variant = 0; 1.103 + } 1.104 + } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { 1.105 + readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL; 1.106 + variant = 2; 1.107 + } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { 1.108 + readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE; 1.109 + variant = 3; 1.110 + } 1.111 + 1.112 + /* This might be overkill, but assure that we don't access out-of-bounds */ 1.113 + MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms)); 1.114 + if (!mPrograms[variant]) { 1.115 + GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER); 1.116 + const GLchar* vsSourcePtr = &readTextureImageVS[0]; 1.117 + mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr); 1.118 + mGL->fCompileShader(vs); 1.119 + 1.120 + GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); 1.121 + mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr); 1.122 + mGL->fCompileShader(fs); 1.123 + 1.124 + GLuint program = mGL->fCreateProgram(); 1.125 + mGL->fAttachShader(program, vs); 1.126 + mGL->fAttachShader(program, fs); 1.127 + mGL->fBindAttribLocation(program, 0, "aVertex"); 1.128 + mGL->fBindAttribLocation(program, 1, "aTexCoord"); 1.129 + mGL->fLinkProgram(program); 1.130 + 1.131 + GLint success; 1.132 + mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success); 1.133 + 1.134 + if (!success) { 1.135 + mGL->fDeleteProgram(program); 1.136 + program = 0; 1.137 + } 1.138 + 1.139 + mGL->fDeleteShader(vs); 1.140 + mGL->fDeleteShader(fs); 1.141 + 1.142 + mPrograms[variant] = program; 1.143 + } 1.144 + 1.145 + return mPrograms[variant]; 1.146 +} 1.147 + 1.148 +bool 1.149 +GLReadTexImageHelper::DidGLErrorOccur(const char* str) 1.150 +{ 1.151 + GLenum error = mGL->fGetError(); 1.152 + if (error != LOCAL_GL_NO_ERROR) { 1.153 + printf_stderr("GL ERROR: %s (0x%04x) %s\n", 1.154 + mGL->GLErrorToString(error), error, str); 1.155 + return true; 1.156 + } 1.157 + 1.158 + return false; 1.159 +} 1.160 + 1.161 +static bool 1.162 +GetActualReadFormats(GLContext* gl, 1.163 + GLenum destFormat, GLenum destType, 1.164 + GLenum& readFormat, GLenum& readType) 1.165 +{ 1.166 + if (destFormat == LOCAL_GL_RGBA && 1.167 + destType == LOCAL_GL_UNSIGNED_BYTE) 1.168 + { 1.169 + readFormat = destFormat; 1.170 + readType = destType; 1.171 + return true; 1.172 + } 1.173 + 1.174 + bool fallback = true; 1.175 + if (gl->IsGLES()) { 1.176 + GLenum auxFormat = 0; 1.177 + GLenum auxType = 0; 1.178 + 1.179 + gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat); 1.180 + gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType); 1.181 + 1.182 + if (destFormat == auxFormat && 1.183 + destType == auxType) 1.184 + { 1.185 + fallback = false; 1.186 + } 1.187 + } else { 1.188 + switch (destFormat) { 1.189 + case LOCAL_GL_RGB: { 1.190 + if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV) 1.191 + fallback = false; 1.192 + break; 1.193 + } 1.194 + case LOCAL_GL_BGRA: { 1.195 + if (destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) 1.196 + fallback = false; 1.197 + break; 1.198 + } 1.199 + } 1.200 + } 1.201 + 1.202 + if (fallback) { 1.203 + readFormat = LOCAL_GL_RGBA; 1.204 + readType = LOCAL_GL_UNSIGNED_BYTE; 1.205 + return false; 1.206 + } else { 1.207 + readFormat = destFormat; 1.208 + readType = destType; 1.209 + return true; 1.210 + } 1.211 +} 1.212 + 1.213 +static void SwapRAndBComponents(DataSourceSurface* surf) 1.214 +{ 1.215 + uint8_t *row = surf->GetData(); 1.216 + if (!row) { 1.217 + MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from DataSourceSurface."); 1.218 + return; 1.219 + } 1.220 + 1.221 + size_t rowBytes = surf->GetSize().width*4; 1.222 + size_t rowHole = surf->Stride() - rowBytes; 1.223 + 1.224 + size_t rows = surf->GetSize().height; 1.225 + 1.226 + while (rows) { 1.227 + 1.228 + const uint8_t *rowEnd = row + rowBytes; 1.229 + 1.230 + while (row != rowEnd) { 1.231 + row[0] ^= row[2]; 1.232 + row[2] ^= row[0]; 1.233 + row[0] ^= row[2]; 1.234 + row += 4; 1.235 + } 1.236 + 1.237 + row += rowHole; 1.238 + --rows; 1.239 + } 1.240 +} 1.241 + 1.242 +static uint16_t PackRGB565(uint8_t r, uint8_t g, uint8_t b) 1.243 +{ 1.244 + uint16_t pixel = ((r << 11) & 0xf800) | 1.245 + ((g << 5) & 0x07e0) | 1.246 + ((b ) & 0x001f); 1.247 + 1.248 + return pixel; 1.249 +} 1.250 + 1.251 +static void CopyDataSourceSurface(DataSourceSurface* aSource, 1.252 + DataSourceSurface* aDest) 1.253 +{ 1.254 + MOZ_ASSERT(aSource->GetSize() == aDest->GetSize()); 1.255 + MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 || 1.256 + aSource->GetFormat() == SurfaceFormat::R8G8B8X8); 1.257 + 1.258 + uint8_t *srcRow = aSource->GetData(); 1.259 + size_t srcRowBytes = aSource->GetSize().width * BytesPerPixel(aSource->GetFormat()); 1.260 + size_t srcRowHole = aSource->Stride() - srcRowBytes; 1.261 + 1.262 + uint8_t *destRow = aDest->GetData(); 1.263 + size_t destRowBytes = aDest->GetSize().width * BytesPerPixel(aDest->GetFormat()); 1.264 + size_t destRowHole = aDest->Stride() - destRowBytes; 1.265 + 1.266 + bool needsRBSwap = false; 1.267 + if (aDest->GetFormat() == SurfaceFormat::B8G8R8A8 || 1.268 + aDest->GetFormat() == SurfaceFormat::B8G8R8X8 || 1.269 + aDest->GetFormat() == SurfaceFormat::R5G6B5) { 1.270 + needsRBSwap = true; 1.271 + } 1.272 + 1.273 + bool needsConvertTo16Bits = false; 1.274 + if (aDest->GetFormat() == SurfaceFormat::R5G6B5) { 1.275 + needsConvertTo16Bits = true; 1.276 + } 1.277 + 1.278 + size_t rows = aSource->GetSize().height; 1.279 + 1.280 + while (rows) { 1.281 + const uint8_t *srcRowEnd = srcRow + srcRowBytes; 1.282 + 1.283 + while (srcRow != srcRowEnd) { 1.284 + uint8_t r = needsRBSwap ? srcRow[2] : srcRow[0]; 1.285 + uint8_t g = srcRow[1]; 1.286 + uint8_t b = needsRBSwap ? srcRow[0] : srcRow[2]; 1.287 + uint8_t a = srcRow[3]; 1.288 + 1.289 + if (needsConvertTo16Bits) { 1.290 + *(uint16_t*)destRow = PackRGB565(r, g, b); 1.291 + } else { 1.292 + destRow[0] = r; 1.293 + destRow[1] = g; 1.294 + destRow[2] = b; 1.295 + destRow[3] = a; 1.296 + } 1.297 + srcRow += BytesPerPixel(aSource->GetFormat()); 1.298 + destRow += BytesPerPixel(aDest->GetFormat()); 1.299 + } 1.300 + 1.301 + srcRow += srcRowHole; 1.302 + destRow += destRowHole; 1.303 + --rows; 1.304 + } 1.305 +} 1.306 + 1.307 +static int 1.308 +CalcStride(int width, int pixelSize, int alignment) 1.309 +{ 1.310 + MOZ_ASSERT(alignment); 1.311 + 1.312 + int stride = width * pixelSize; 1.313 + if (stride % alignment) { // Extra at the end of the line? 1.314 + int alignmentCount = stride / alignment; 1.315 + stride = (alignmentCount+1) * alignment; 1.316 + } 1.317 + return stride; 1.318 +} 1.319 + 1.320 +static int 1.321 +GuessAlignment(int width, int pixelSize, int stride) 1.322 +{ 1.323 + int alignment = 8; // Max GLES allows. 1.324 + while (CalcStride(width, pixelSize, alignment) != stride) { 1.325 + alignment /= 2; 1.326 + if (!alignment) { 1.327 + MOZ_ASSERT(alignment); 1.328 + return 1; 1.329 + } 1.330 + } 1.331 + return alignment; 1.332 +} 1.333 + 1.334 +void 1.335 +ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { 1.336 + gl->MakeCurrent(); 1.337 + MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0)); 1.338 + 1.339 + /* gfxImageFormat::ARGB32: 1.340 + * RGBA+UByte: be[RGBA], le[ABGR] 1.341 + * RGBA+UInt: be[ABGR], le[RGBA] 1.342 + * BGRA+UInt: be[ARGB], le[BGRA] 1.343 + * BGRA+UIntRev: be[BGRA], le[ARGB] 1.344 + * 1.345 + * gfxImageFormat::RGB16_565: 1.346 + * RGB+UShort: le[rrrrrggg,gggbbbbb] 1.347 + */ 1.348 + bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32; 1.349 + 1.350 + int destPixelSize; 1.351 + GLenum destFormat; 1.352 + GLenum destType; 1.353 + 1.354 + switch (dest->Format()) { 1.355 + case gfxImageFormat::RGB24: // XRGB 1.356 + case gfxImageFormat::ARGB32: 1.357 + destPixelSize = 4; 1.358 + // Needs host (little) endian ARGB. 1.359 + destFormat = LOCAL_GL_BGRA; 1.360 + destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; 1.361 + break; 1.362 + 1.363 + case gfxImageFormat::RGB16_565: 1.364 + destPixelSize = 2; 1.365 + destFormat = LOCAL_GL_RGB; 1.366 + destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; 1.367 + break; 1.368 + 1.369 + default: 1.370 + MOZ_CRASH("Bad format."); 1.371 + } 1.372 + MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride()); 1.373 + 1.374 + GLenum readFormat = destFormat; 1.375 + GLenum readType = destType; 1.376 + bool needsTempSurf = !GetActualReadFormats(gl, 1.377 + destFormat, destType, 1.378 + readFormat, readType); 1.379 + 1.380 + nsAutoPtr<gfxImageSurface> tempSurf; 1.381 + gfxImageSurface* readSurf = nullptr; 1.382 + int readAlignment = 0; 1.383 + if (needsTempSurf) { 1.384 + if (gl->DebugMode()) { 1.385 + NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); 1.386 + } 1.387 + SurfaceFormat readFormatGFX; 1.388 + 1.389 + switch (readFormat) { 1.390 + case LOCAL_GL_RGBA: 1.391 + case LOCAL_GL_BGRA: { 1.392 + readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8 1.393 + : SurfaceFormat::B8G8R8X8; 1.394 + break; 1.395 + } 1.396 + case LOCAL_GL_RGB: { 1.397 + MOZ_ASSERT(destPixelSize == 2); 1.398 + MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); 1.399 + readFormatGFX = SurfaceFormat::R5G6B5; 1.400 + break; 1.401 + } 1.402 + default: { 1.403 + MOZ_CRASH("Bad read format."); 1.404 + } 1.405 + } 1.406 + 1.407 + switch (readType) { 1.408 + case LOCAL_GL_UNSIGNED_BYTE: { 1.409 + MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); 1.410 + readAlignment = 1; 1.411 + break; 1.412 + } 1.413 + case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { 1.414 + MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); 1.415 + readAlignment = 4; 1.416 + break; 1.417 + } 1.418 + case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { 1.419 + MOZ_ASSERT(readFormat == LOCAL_GL_RGB); 1.420 + readAlignment = 2; 1.421 + break; 1.422 + } 1.423 + default: { 1.424 + MOZ_CRASH("Bad read type."); 1.425 + } 1.426 + } 1.427 + 1.428 + tempSurf = new gfxImageSurface(dest->GetSize(), 1.429 + SurfaceFormatToImageFormat(readFormatGFX), 1.430 + false); 1.431 + readSurf = tempSurf; 1.432 + } else { 1.433 + // Figure out alignment. We don't need to know why, we just need it 1.434 + // to be valid. 1.435 + readAlignment = GuessAlignment(dest->Width(), 1.436 + destPixelSize, 1.437 + dest->Stride()); 1.438 + readSurf = dest; 1.439 + } 1.440 + MOZ_ASSERT(readAlignment); 1.441 + 1.442 + GLint currentPackAlignment = 0; 1.443 + gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); 1.444 + 1.445 + if (currentPackAlignment != readAlignment) 1.446 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); 1.447 + 1.448 + GLsizei width = dest->Width(); 1.449 + GLsizei height = dest->Height(); 1.450 + 1.451 + readSurf->Flush(); 1.452 + gl->fReadPixels(0, 0, 1.453 + width, height, 1.454 + readFormat, readType, 1.455 + readSurf->Data()); 1.456 + readSurf->MarkDirty(); 1.457 + 1.458 + if (currentPackAlignment != readAlignment) 1.459 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); 1.460 + 1.461 + if (readSurf != dest) { 1.462 + MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); 1.463 + MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); 1.464 + // So we just copied in RGBA in big endian, or le: 0xAABBGGRR. 1.465 + // We want 0xAARRGGBB, so swap R and B: 1.466 + dest->Flush(); 1.467 + RefPtr<DataSourceSurface> readDSurf = 1.468 + Factory::CreateWrappingDataSourceSurface(readSurf->Data(), 1.469 + readSurf->Stride(), 1.470 + ToIntSize(readSurf->GetSize()), 1.471 + ImageFormatToSurfaceFormat(readSurf->Format())); 1.472 + SwapRAndBComponents(readDSurf); 1.473 + dest->MarkDirty(); 1.474 + 1.475 + gfxContext ctx(dest); 1.476 + ctx.SetOperator(gfxContext::OPERATOR_SOURCE); 1.477 + ctx.SetSource(readSurf); 1.478 + ctx.Paint(); 1.479 + } 1.480 + 1.481 + // Check if GL is giving back 1.0 alpha for 1.482 + // RGBA reads to RGBA images from no-alpha buffers. 1.483 +#ifdef XP_MACOSX 1.484 + if (gl->WorkAroundDriverBugs() && 1.485 + gl->Vendor() == gl::GLVendor::NVIDIA && 1.486 + dest->Format() == gfxImageFormat::ARGB32 && 1.487 + width && height) 1.488 + { 1.489 + GLint alphaBits = 0; 1.490 + gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); 1.491 + if (!alphaBits) { 1.492 + const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); 1.493 + 1.494 + MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride()); 1.495 + 1.496 + dest->Flush(); 1.497 + uint32_t* itr = (uint32_t*)dest->Data(); 1.498 + uint32_t testPixel = *itr; 1.499 + if ((testPixel & alphaMask) != alphaMask) { 1.500 + // We need to set the alpha channel to 1.0 manually. 1.501 + uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. 1.502 + 1.503 + for (; itr != itrEnd; itr++) { 1.504 + *itr |= alphaMask; 1.505 + } 1.506 + } 1.507 + dest->MarkDirty(); 1.508 + } 1.509 + } 1.510 +#endif 1.511 +} 1.512 + 1.513 +void 1.514 +ReadPixelsIntoDataSourceSurface(GLContext* gl, DataSourceSurface* dest) { 1.515 + gl->MakeCurrent(); 1.516 + MOZ_ASSERT(dest->GetSize().width != 0); 1.517 + MOZ_ASSERT(dest->GetSize().height != 0); 1.518 + 1.519 + bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 || 1.520 + dest->GetFormat() == SurfaceFormat::R8G8B8A8; 1.521 + 1.522 + int destPixelSize; 1.523 + GLenum destFormat; 1.524 + GLenum destType; 1.525 + 1.526 + switch (dest->GetFormat()) { 1.527 + case SurfaceFormat::B8G8R8A8: 1.528 + case SurfaceFormat::B8G8R8X8: 1.529 + // Needs host (little) endian ARGB. 1.530 + destFormat = LOCAL_GL_BGRA; 1.531 + destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; 1.532 + break; 1.533 + case SurfaceFormat::R8G8B8A8: 1.534 + case SurfaceFormat::R8G8B8X8: 1.535 + // Needs host (little) endian ABGR. 1.536 + destFormat = LOCAL_GL_RGBA; 1.537 + destType = LOCAL_GL_UNSIGNED_BYTE; 1.538 + break; 1.539 + case SurfaceFormat::R5G6B5: 1.540 + destFormat = LOCAL_GL_RGB; 1.541 + destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; 1.542 + break; 1.543 + default: 1.544 + MOZ_CRASH("Bad format."); 1.545 + } 1.546 + destPixelSize = BytesPerPixel(dest->GetFormat()); 1.547 + MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride()); 1.548 + 1.549 + GLenum readFormat = destFormat; 1.550 + GLenum readType = destType; 1.551 + bool needsTempSurf = !GetActualReadFormats(gl, 1.552 + destFormat, destType, 1.553 + readFormat, readType); 1.554 + 1.555 + RefPtr<DataSourceSurface> tempSurf; 1.556 + DataSourceSurface* readSurf = nullptr; 1.557 + int readAlignment = 0; 1.558 + if (needsTempSurf) { 1.559 + if (gl->DebugMode()) { 1.560 + NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); 1.561 + } 1.562 + SurfaceFormat readFormatGFX; 1.563 + 1.564 + // If needs temp surface, readFormat is always LOCAL_GL_RGBA 1.565 + // and readType is always LOCAL_GL_UNSIGNED_BYTE 1.566 + MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); 1.567 + MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); 1.568 + readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8 1.569 + : SurfaceFormat::R8G8B8X8; 1.570 + readAlignment = 1; 1.571 + int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX); 1.572 + tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(), 1.573 + readFormatGFX, 1.574 + stride); 1.575 + readSurf = tempSurf; 1.576 + } else { 1.577 + // Figure out alignment. We don't need to know why, we just need it 1.578 + // to be valid. 1.579 + readAlignment = GuessAlignment(dest->GetSize().width, 1.580 + destPixelSize, 1.581 + dest->Stride()); 1.582 + readSurf = dest; 1.583 + } 1.584 + MOZ_ASSERT(readAlignment); 1.585 + MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0); 1.586 + 1.587 + GLint currentPackAlignment = 0; 1.588 + gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); 1.589 + 1.590 + if (currentPackAlignment != readAlignment) 1.591 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); 1.592 + 1.593 + GLsizei width = dest->GetSize().width; 1.594 + GLsizei height = dest->GetSize().height; 1.595 + 1.596 + gl->fReadPixels(0, 0, 1.597 + width, height, 1.598 + readFormat, readType, 1.599 + readSurf->GetData()); 1.600 + 1.601 + if (currentPackAlignment != readAlignment) 1.602 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); 1.603 + 1.604 + if (readSurf != dest) { 1.605 + MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); 1.606 + MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); 1.607 + CopyDataSourceSurface(readSurf, dest); 1.608 + } 1.609 + 1.610 + // Check if GL is giving back 1.0 alpha for 1.611 + // RGBA reads to RGBA images from no-alpha buffers. 1.612 +#ifdef XP_MACOSX 1.613 + if (gl->WorkAroundDriverBugs() && 1.614 + gl->Vendor() == gl::GLVendor::NVIDIA && 1.615 + (dest->GetFormat() == SurfaceFormat::R8G8B8A8 || 1.616 + dest->GetFormat() == SurfaceFormat::B8G8R8A8) && 1.617 + width && height) 1.618 + { 1.619 + GLint alphaBits = 0; 1.620 + gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); 1.621 + if (!alphaBits) { 1.622 + const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); 1.623 + 1.624 + MOZ_ASSERT(dest->GetSize().width * destPixelSize == dest->Stride()); 1.625 + 1.626 + uint32_t* itr = (uint32_t*)dest->GetData(); 1.627 + uint32_t testPixel = *itr; 1.628 + if ((testPixel & alphaMask) != alphaMask) { 1.629 + // We need to set the alpha channel to 1.0 manually. 1.630 + uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. 1.631 + 1.632 + for (; itr != itrEnd; itr++) { 1.633 + *itr |= alphaMask; 1.634 + } 1.635 + } 1.636 + } 1.637 + } 1.638 +#endif 1.639 +} 1.640 + 1.641 +static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf) 1.642 +{ 1.643 + RefPtr<DataSourceSurface> temp = 1.644 + Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(), 1.645 + aSurf->GetFormat(), 1.646 + aSurf->Stride()); 1.647 + RefPtr<DrawTarget> dt = 1.648 + Factory::CreateDrawTargetForData(BackendType::CAIRO, 1.649 + temp->GetData(), 1.650 + temp->GetSize(), 1.651 + temp->Stride(), 1.652 + temp->GetFormat()); 1.653 + nsRefPtr<gfxContext> ctx = new gfxContext(dt); 1.654 + ctx->SetOperator(gfxContext::OPERATOR_SOURCE); 1.655 + ctx->Scale(1.0, -1.0); 1.656 + ctx->Translate(-gfxPoint(0.0, aSurf->GetSize().height)); 1.657 + 1.658 + nsRefPtr<gfxImageSurface> thebesSurf = 1.659 + new gfxImageSurface(aSurf->GetData(), 1.660 + ThebesIntSize(aSurf->GetSize()), 1.661 + aSurf->Stride(), 1.662 + SurfaceFormatToImageFormat(aSurf->GetFormat())); 1.663 + ctx->SetSource(thebesSurf); 1.664 + ctx->Paint(); 1.665 + return temp.forget(); 1.666 +} 1.667 + 1.668 +TemporaryRef<DataSourceSurface> 1.669 +ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat) 1.670 +{ 1.671 + gl->MakeCurrent(); 1.672 + gl->GuaranteeResolve(); 1.673 + gl->fActiveTexture(LOCAL_GL_TEXTURE0); 1.674 + gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture); 1.675 + 1.676 + IntSize size; 1.677 + gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width); 1.678 + gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height); 1.679 + 1.680 + RefPtr<DataSourceSurface> surf = 1.681 + Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8, 1.682 + GetAlignedStride<4>(size.width * BytesPerPixel(SurfaceFormat::B8G8R8A8))); 1.683 + 1.684 + if (!surf) { 1.685 + return nullptr; 1.686 + } 1.687 + 1.688 + uint32_t currentPackAlignment = 0; 1.689 + gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)¤tPackAlignment); 1.690 + if (currentPackAlignment != 4) { 1.691 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); 1.692 + } 1.693 + gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData()); 1.694 + if (currentPackAlignment != 4) { 1.695 + gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); 1.696 + } 1.697 + 1.698 + if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) { 1.699 + SwapRAndBComponents(surf); 1.700 + } 1.701 + 1.702 + if (aYInvert) { 1.703 + surf = YInvertImageSurface(surf); 1.704 + } 1.705 + 1.706 + return surf.forget(); 1.707 +} 1.708 + 1.709 +void 1.710 +ReadScreenIntoImageSurface(GLContext* gl, gfxImageSurface* dest) 1.711 +{ 1.712 + ScopedBindFramebuffer autoFB(gl, 0); 1.713 + ReadPixelsIntoImageSurface(gl, dest); 1.714 +} 1.715 + 1.716 + 1.717 +#define CLEANUP_IF_GLERROR_OCCURRED(x) \ 1.718 + if (DidGLErrorOccur(x)) { \ 1.719 + isurf = nullptr; \ 1.720 + break; \ 1.721 + } 1.722 + 1.723 +TemporaryRef<DataSourceSurface> 1.724 +GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, 1.725 + GLenum aTextureTarget, 1.726 + const gfx::IntSize& aSize, 1.727 + /* ShaderConfigOGL.mFeature */ int aConfig, 1.728 + bool aYInvert) 1.729 +{ 1.730 + MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D || 1.731 + aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL || 1.732 + aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB); 1.733 + 1.734 + mGL->MakeCurrent(); 1.735 + 1.736 + /* Allocate resulting image surface */ 1.737 + int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8); 1.738 + RefPtr<DataSourceSurface> isurf = 1.739 + Factory::CreateDataSourceSurfaceWithStride(aSize, 1.740 + SurfaceFormat::R8G8B8A8, 1.741 + stride); 1.742 + 1.743 + GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; 1.744 + GLuint rb, fb; 1.745 + 1.746 + do { 1.747 + mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb); 1.748 + mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb); 1.749 + mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog); 1.750 + mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit); 1.751 + mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1.752 + switch (aTextureTarget) { 1.753 + case LOCAL_GL_TEXTURE_2D: 1.754 + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex); 1.755 + break; 1.756 + case LOCAL_GL_TEXTURE_EXTERNAL: 1.757 + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex); 1.758 + break; 1.759 + case LOCAL_GL_TEXTURE_RECTANGLE: 1.760 + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex); 1.761 + break; 1.762 + default: /* Already checked above */ 1.763 + break; 1.764 + } 1.765 + 1.766 + ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false); 1.767 + ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false); 1.768 + ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height); 1.769 + 1.770 + /* Setup renderbuffer */ 1.771 + mGL->fGenRenderbuffers(1, &rb); 1.772 + mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); 1.773 + 1.774 + GLenum rbInternalFormat = 1.775 + mGL->IsGLES() 1.776 + ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4) 1.777 + : LOCAL_GL_RGBA; 1.778 + mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height); 1.779 + CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer"); 1.780 + 1.781 + /* Setup framebuffer */ 1.782 + mGL->fGenFramebuffers(1, &fb); 1.783 + mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); 1.784 + mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, 1.785 + LOCAL_GL_RENDERBUFFER, rb); 1.786 + CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer"); 1.787 + 1.788 + MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE); 1.789 + 1.790 + /* Setup vertex and fragment shader */ 1.791 + GLuint program = TextureImageProgramFor(aTextureTarget, aConfig); 1.792 + MOZ_ASSERT(program); 1.793 + 1.794 + mGL->fUseProgram(program); 1.795 + CLEANUP_IF_GLERROR_OCCURRED("when using program"); 1.796 + mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0); 1.797 + CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location"); 1.798 + 1.799 + /* Setup quad geometry */ 1.800 + mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); 1.801 + mGL->fEnableVertexAttribArray(0); 1.802 + mGL->fEnableVertexAttribArray(1); 1.803 + 1.804 + float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f; 1.805 + float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f; 1.806 + 1.807 + 1.808 + const float 1.809 + vertexArray[4*2] = { 1.810 + -1.0f, -1.0f, 1.811 + 1.0f, -1.0f, 1.812 + -1.0f, 1.0f, 1.813 + 1.0f, 1.0f 1.814 + }; 1.815 + mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, vertexArray); 1.816 + 1.817 + const float u0 = 0.0f; 1.818 + const float u1 = w; 1.819 + const float v0 = aYInvert ? h : 0.0f; 1.820 + const float v1 = aYInvert ? 0.0f : h; 1.821 + const float texCoordArray[8] = { u0, v0, 1.822 + u1, v0, 1.823 + u0, v1, 1.824 + u1, v1 }; 1.825 + mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, texCoordArray); 1.826 + 1.827 + /* Bind the texture */ 1.828 + if (aTextureId) { 1.829 + mGL->fBindTexture(aTextureTarget, aTextureId); 1.830 + CLEANUP_IF_GLERROR_OCCURRED("when binding texture"); 1.831 + } 1.832 + 1.833 + /* Draw quad */ 1.834 + mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f); 1.835 + mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT); 1.836 + CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer"); 1.837 + 1.838 + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); 1.839 + CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); 1.840 + 1.841 + mGL->fDisableVertexAttribArray(1); 1.842 + mGL->fDisableVertexAttribArray(0); 1.843 + 1.844 + /* Read-back draw results */ 1.845 + ReadPixelsIntoDataSourceSurface(mGL, isurf); 1.846 + CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); 1.847 + } while (false); 1.848 + 1.849 + /* Restore GL state */ 1.850 +//cleanup: 1.851 + mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb); 1.852 + mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb); 1.853 + mGL->fUseProgram(oldprog); 1.854 + 1.855 + // note that deleting 0 has no effect in any of these calls 1.856 + mGL->fDeleteRenderbuffers(1, &rb); 1.857 + mGL->fDeleteFramebuffers(1, &fb); 1.858 + 1.859 + if (aTextureId) 1.860 + mGL->fBindTexture(aTextureTarget, oldTex); 1.861 + 1.862 + if (oldTexUnit != LOCAL_GL_TEXTURE0) 1.863 + mGL->fActiveTexture(oldTexUnit); 1.864 + 1.865 + return isurf.forget(); 1.866 +} 1.867 + 1.868 +#undef CLEANUP_IF_GLERROR_OCCURRED 1.869 + 1.870 + 1.871 +} 1.872 +}