gfx/gl/GLReadTexImageHelper.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial