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