Bug 887318 - Fix SkiaGL readback when BGRA unavailable r=jgilbert
authorJames Willcox <jwillcox@mozilla.com>
Thu, 27 Jun 2013 10:20:21 -0400
changeset 139186 fb0c86ed08c2a198ea0513202da252a8be675ed5
parent 139185 8e87e5a44607cf2a119715afb8315c87353f0500
child 139187 5e051e9912f81bbefc7846b5d0fcba7bfb0e6d73
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjgilbert
bugs887318
milestone25.0a1
Bug 887318 - Fix SkiaGL readback when BGRA unavailable r=jgilbert
gfx/gl/GLContextSkia.cpp
gfx/skia/patches/0024-Bug-887318-fix-bgra-readback.patch
gfx/skia/src/gpu/gl/GrGpuGL.cpp
gfx/skia/src/gpu/gl/GrGpuGL.h
--- a/gfx/gl/GLContextSkia.cpp
+++ b/gfx/gl/GLContextSkia.cpp
@@ -305,17 +305,17 @@ const GLubyte* glGetString_mozilla(GrGLe
             return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
         } else {
             return reinterpret_cast<const GLubyte*>("2.0");
         }
     } else if (name == LOCAL_GL_EXTENSIONS) {
         // Only expose the bare minimum extensions we want to support to ensure a functional Ganesh
         // as GLContext only exposes certain extensions
         static bool extensionsStringBuilt = false;
-        static char extensionsString[120];
+        static char extensionsString[256];
 
         if (!extensionsStringBuilt) {
             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) {
                 strcpy(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
             }
 
             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) {
                 strcat(extensionsString, "GL_OES_packed_depth_stencil ");
@@ -324,16 +324,24 @@ const GLubyte* glGetString_mozilla(GrGLe
             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
                 strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
             }
 
             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
                 strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
             }
 
+            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_bgra)) {
+                strcat(extensionsString, "GL_EXT_bgra ");
+            }
+
+            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
+                strcat(extensionsString, "GL_EXT_read_format_bgra ");
+            }
+
             extensionsStringBuilt = true;
         }
 
         return reinterpret_cast<const GLubyte*>(extensionsString);
 
     } else if (name == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
         if (sGLContext.get()->IsGLES2()) {
             return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
new file mode 100644
--- /dev/null
+++ b/gfx/skia/patches/0024-Bug-887318-fix-bgra-readback.patch
@@ -0,0 +1,217 @@
+diff --git a/gfx/gl/GLContextSkia.cpp b/gfx/gl/GLContextSkia.cpp
+--- a/gfx/gl/GLContextSkia.cpp
++++ b/gfx/gl/GLContextSkia.cpp
+@@ -303,39 +303,47 @@ const GLubyte* glGetString_mozilla(GrGLe
+     if (name == LOCAL_GL_VERSION) {
+         if (sGLContext.get()->IsGLES2()) {
+             return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
+         } else {
+             return reinterpret_cast<const GLubyte*>("2.0");
+         }
+     } else if (name == LOCAL_GL_EXTENSIONS) {
+         // Only expose the bare minimum extensions we want to support to ensure a functional Ganesh
+         // as GLContext only exposes certain extensions
+         static bool extensionsStringBuilt = false;
+-        static char extensionsString[120];
++        static char extensionsString[256];
+ 
+         if (!extensionsStringBuilt) {
+             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) {
+                 strcpy(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) {
+                 strcat(extensionsString, "GL_OES_packed_depth_stencil ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
+                 strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
+                 strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
+             }
+ 
++            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_bgra)) {
++                strcat(extensionsString, "GL_EXT_bgra ");
++            }
++
++            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
++                strcat(extensionsString, "GL_EXT_read_format_bgra ");
++            }
++
+             extensionsStringBuilt = true;
+         }
+ 
+         return reinterpret_cast<const GLubyte*>(extensionsString);
+ 
+     } else if (name == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
+         if (sGLContext.get()->IsGLES2()) {
+             return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
+         } else {
+             return reinterpret_cast<const GLubyte*>("1.10");
+diff --git a/gfx/skia/src/gpu/gl/GrGpuGL.cpp b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+--- a/gfx/skia/src/gpu/gl/GrGpuGL.cpp
++++ b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+@@ -1,18 +1,18 @@
+ /*
+  * Copyright 2011 Google Inc.
+  *
+  * Use of this source code is governed by a BSD-style license that can be
+  * found in the LICENSE file.
+  */
+ 
+-
++#include <algorithm>
+ #include "GrGpuGL.h"
+ #include "GrGLStencilBuffer.h"
+ #include "GrGLPath.h"
+ #include "GrGLShaderBuilder.h"
+ #include "GrTemplates.h"
+ #include "GrTypes.h"
+ #include "SkTemplates.h"
+ 
+ static const GrGLuint GR_MAX_GLUINT = ~0U;
+ static const GrGLint  GR_INVAL_GLINT = ~0;
+@@ -1381,29 +1381,67 @@ bool GrGpuGL::readPixelsWillPayForYFlip(
+     // Note the rowBytes might be tight to the passed in data, but if data
+     // gets clipped in x to the target the rowBytes will no longer be tight.
+     if (left >= 0 && (left + width) < renderTarget->width()) {
+            return 0 == rowBytes ||
+                   GrBytesPerPixel(config) * width == rowBytes;
+     } else {
+         return false;
+     }
+ }
+ 
++static void swizzleRow(void* buffer, int byteLen) {
++    uint8_t* src = (uint8_t*)buffer;
++    uint8_t* end = src + byteLen;
++
++    GrAssert((end - src) % 4 == 0);
++
++    for (; src != end; src += 4) {
++        std::swap(src[0], src[2]);
++    }
++}
++
++bool GrGpuGL::canReadBGRA() const
++{
++    if (kDesktop_GrGLBinding == this->glBinding() ||
++        this->hasExtension("GL_EXT_bgra"))
++        return true;
++
++    if (this->hasExtension("GL_EXT_read_format_bgra")) {
++        GrGLint readFormat = 0;
++        GrGLint readType = 0;
++
++        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
++        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
++
++        return readFormat == GR_GL_BGRA && readType == GR_GL_UNSIGNED_BYTE;
++    }
++
++    return false;
++}
++
+ bool GrGpuGL::onReadPixels(GrRenderTarget* target,
+                            int left, int top,
+                            int width, int height,
+                            GrPixelConfig config,
+                            void* buffer,
+                            size_t rowBytes) {
+     GrGLenum format;
+     GrGLenum type;
+     bool flipY = kBottomLeft_GrSurfaceOrigin == target->origin();
++    bool needSwizzle = false;
++
++    if (kBGRA_8888_GrPixelConfig == config && !this->canReadBGRA()) {
++        // Read RGBA and swizzle after
++        config = kRGBA_8888_GrPixelConfig;
++        needSwizzle = true;
++    }
++
+     if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
+         return false;
+     }
+     size_t bpp = GrBytesPerPixel(config);
+     if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
+                                  &left, &top, &width, &height,
+                                  const_cast<const void**>(&buffer),
+                                  &rowBytes)) {
+         return false;
+     }
+@@ -1478,35 +1516,46 @@ bool GrGpuGL::onReadPixels(GrRenderTarge
+             scratch.reset(tightRowBytes);
+             void* tmpRow = scratch.get();
+             // flip y in-place by rows
+             const int halfY = height >> 1;
+             char* top = reinterpret_cast<char*>(buffer);
+             char* bottom = top + (height - 1) * rowBytes;
+             for (int y = 0; y < halfY; y++) {
+                 memcpy(tmpRow, top, tightRowBytes);
+                 memcpy(top, bottom, tightRowBytes);
+                 memcpy(bottom, tmpRow, tightRowBytes);
++
++                if (needSwizzle) {
++                    swizzleRow(top, tightRowBytes);
++                    swizzleRow(bottom, tightRowBytes);
++                }
++
+                 top += rowBytes;
+                 bottom -= rowBytes;
+             }
+         }
+     } else {
+-        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
++        GrAssert(readDst != buffer);
++        GrAssert(rowBytes != tightRowBytes);
+         // copy from readDst to buffer while flipping y
+         // const int halfY = height >> 1;
+         const char* src = reinterpret_cast<const char*>(readDst);
+         char* dst = reinterpret_cast<char*>(buffer);
+         if (flipY) {
+             dst += (height-1) * rowBytes;
+         }
+         for (int y = 0; y < height; y++) {
+             memcpy(dst, src, tightRowBytes);
++            if (needSwizzle) {
++                swizzleRow(dst, tightRowBytes);
++            }
++
+             src += readDstRowBytes;
+             if (!flipY) {
+                 dst += rowBytes;
+             } else {
+                 dst -= rowBytes;
+             }
+         }
+     }
+     return true;
+ }
+diff --git a/gfx/skia/src/gpu/gl/GrGpuGL.h b/gfx/skia/src/gpu/gl/GrGpuGL.h
+--- a/gfx/skia/src/gpu/gl/GrGpuGL.h
++++ b/gfx/skia/src/gpu/gl/GrGpuGL.h
+@@ -243,20 +243,22 @@ private:
+                        GrPixelConfig dataConfig,
+                        const void* data,
+                        size_t rowBytes);
+ 
+     bool createRenderTargetObjects(int width, int height,
+                                    GrGLuint texID,
+                                    GrGLRenderTarget::Desc* desc);
+ 
+     void fillInConfigRenderableTable();
+ 
++    bool canReadBGRA() const;
++
+     GrGLContext fGLContext;
+ 
+     // GL program-related state
+     ProgramCache*               fProgramCache;
+     SkAutoTUnref<GrGLProgram>   fCurrentProgram;
+ 
+     ///////////////////////////////////////////////////////////////////////////
+     ///@name Caching of GL State
+     ///@{
+     int                         fHWActiveTextureUnitIdx;
--- a/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+++ b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
@@ -1,16 +1,16 @@
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
-
+#include <algorithm>
 #include "GrGpuGL.h"
 #include "GrGLStencilBuffer.h"
 #include "GrGLPath.h"
 #include "GrGLShaderBuilder.h"
 #include "GrTemplates.h"
 #include "GrTypes.h"
 #include "SkTemplates.h"
 
@@ -1383,25 +1383,63 @@ bool GrGpuGL::readPixelsWillPayForYFlip(
     if (left >= 0 && (left + width) < renderTarget->width()) {
            return 0 == rowBytes ||
                   GrBytesPerPixel(config) * width == rowBytes;
     } else {
         return false;
     }
 }
 
+static void swizzleRow(void* buffer, int byteLen) {
+    uint8_t* src = (uint8_t*)buffer;
+    uint8_t* end = src + byteLen;
+
+    GrAssert((end - src) % 4 == 0);
+
+    for (; src != end; src += 4) {
+        std::swap(src[0], src[2]);
+    }
+}
+
+bool GrGpuGL::canReadBGRA() const
+{
+    if (kDesktop_GrGLBinding == this->glBinding() ||
+        this->hasExtension("GL_EXT_bgra"))
+        return true;
+
+    if (this->hasExtension("GL_EXT_read_format_bgra")) {
+        GrGLint readFormat = 0;
+        GrGLint readType = 0;
+
+        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
+        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
+
+        return readFormat == GR_GL_BGRA && readType == GR_GL_UNSIGNED_BYTE;
+    }
+
+    return false;
+}
+
 bool GrGpuGL::onReadPixels(GrRenderTarget* target,
                            int left, int top,
                            int width, int height,
                            GrPixelConfig config,
                            void* buffer,
                            size_t rowBytes) {
     GrGLenum format;
     GrGLenum type;
     bool flipY = kBottomLeft_GrSurfaceOrigin == target->origin();
+    bool needSwizzle = false;
+
+    if (kBGRA_8888_GrPixelConfig == config && !this->canReadBGRA()) {
+        // Read RGBA and swizzle after
+        config = kRGBA_8888_GrPixelConfig;
+        needSwizzle = true;
+    }
+
     if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
         return false;
     }
     size_t bpp = GrBytesPerPixel(config);
     if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
                                  &left, &top, &width, &height,
                                  const_cast<const void**>(&buffer),
                                  &rowBytes)) {
@@ -1480,31 +1518,42 @@ bool GrGpuGL::onReadPixels(GrRenderTarge
             // flip y in-place by rows
             const int halfY = height >> 1;
             char* top = reinterpret_cast<char*>(buffer);
             char* bottom = top + (height - 1) * rowBytes;
             for (int y = 0; y < halfY; y++) {
                 memcpy(tmpRow, top, tightRowBytes);
                 memcpy(top, bottom, tightRowBytes);
                 memcpy(bottom, tmpRow, tightRowBytes);
+
+                if (needSwizzle) {
+                    swizzleRow(top, tightRowBytes);
+                    swizzleRow(bottom, tightRowBytes);
+                }
+
                 top += rowBytes;
                 bottom -= rowBytes;
             }
         }
     } else {
-        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
+        GrAssert(readDst != buffer);
+        GrAssert(rowBytes != tightRowBytes);
         // copy from readDst to buffer while flipping y
         // const int halfY = height >> 1;
         const char* src = reinterpret_cast<const char*>(readDst);
         char* dst = reinterpret_cast<char*>(buffer);
         if (flipY) {
             dst += (height-1) * rowBytes;
         }
         for (int y = 0; y < height; y++) {
             memcpy(dst, src, tightRowBytes);
+            if (needSwizzle) {
+                swizzleRow(dst, tightRowBytes);
+            }
+
             src += readDstRowBytes;
             if (!flipY) {
                 dst += rowBytes;
             } else {
                 dst -= rowBytes;
             }
         }
     }
--- a/gfx/skia/src/gpu/gl/GrGpuGL.h
+++ b/gfx/skia/src/gpu/gl/GrGpuGL.h
@@ -245,16 +245,18 @@ private:
                        size_t rowBytes);
 
     bool createRenderTargetObjects(int width, int height,
                                    GrGLuint texID,
                                    GrGLRenderTarget::Desc* desc);
 
     void fillInConfigRenderableTable();
 
+    bool canReadBGRA() const;
+
     GrGLContext fGLContext;
 
     // GL program-related state
     ProgramCache*               fProgramCache;
     SkAutoTUnref<GrGLProgram>   fCurrentProgram;
 
     ///////////////////////////////////////////////////////////////////////////
     ///@name Caching of GL State