gfx/gl/GLReadTexImageHelper.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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, &currentPackAlignment);
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, &currentPackAlignment);
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*)&currentPackAlignment);
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 }

mercurial