Bug 960378 - Allow Reading into non-tightly-packed surfaces. - r=bjacob
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 13 Feb 2014 15:38:53 -0800
changeset 168712 5d57e7e2dfe44ef35cc3b7d78f434bab4ec7adeb
parent 168711 5ae4fa0b13400c728dfe5617410e2c5290475e78
child 168713 60e089842cb60a584a3ef4dede84e9520c20d54c
push id26215
push userryanvm@gmail.com
push dateFri, 14 Feb 2014 13:54:11 +0000
treeherdermozilla-central@5d7caa093f4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbjacob
bugs960378
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 960378 - Allow Reading into non-tightly-packed surfaces. - r=bjacob
gfx/gl/GLReadTexImageHelper.cpp
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -151,17 +151,18 @@ GLReadTexImageHelper::DidGLErrorOccur(co
                       mGL->GLErrorToString(error), error, str);
         return true;
     }
 
     return false;
 }
 
 static bool
-GetActualReadFormats(GLContext* gl, GLenum destFormat, GLenum destType,
+GetActualReadFormats(GLContext* gl,
+                     GLenum destFormat, GLenum destType,
                      GLenum& readFormat, GLenum& readType)
 {
     if (destFormat == LOCAL_GL_RGBA &&
         destType == LOCAL_GL_UNSIGNED_BYTE)
     {
         readFormat = destFormat;
         readType = destType;
         return true;
@@ -226,26 +227,53 @@ static void SwapRAndBComponents(DataSour
       row += 4;
     }
 
     row += rowHole;
     --rows;
   }
 }
 
+static int
+CalcStride(int width, int pixelSize, int alignment)
+{
+    MOZ_ASSERT(alignment);
+
+    int stride = width * pixelSize;
+    if (stride % alignment) { // Extra at the end of the line?
+        int alignmentCount = stride / alignment;
+        stride = (alignmentCount+1) * alignment;
+    }
+    return stride;
+}
+
+static int
+GuessAlignment(int width, int pixelSize, int stride)
+{
+    int alignment = 8; // Max GLES allows.
+    while (CalcStride(width, pixelSize, alignment) != stride) {
+        alignment /= 2;
+        if (!alignment) {
+            MOZ_ASSERT(alignment);
+            return 1;
+        }
+    }
+    return alignment;
+}
+
 void
 ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) {
     gl->MakeCurrent();
     MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0));
 
     /* gfxImageFormat::ARGB32:
-     * RGBA+UByte: be[RGBA], le[ABGR]
-     * RGBA+UInt: le[RGBA]
-     * BGRA+UInt: le[BGRA]
-     * BGRA+UIntRev: le[ARGB]
+     * RGBA+UByte:   be[RGBA], le[ABGR]
+     * RGBA+UInt:    be[ABGR], le[RGBA]
+     * BGRA+UInt:    be[ARGB], le[BGRA]
+     * BGRA+UIntRev: be[BGRA], le[ARGB]
      *
      * gfxImageFormat::RGB16_565:
      * RGB+UShort: le[rrrrrggg,gggbbbbb]
      */
     bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32;
 
     int destPixelSize;
     GLenum destFormat;
@@ -264,99 +292,103 @@ ReadPixelsIntoImageSurface(GLContext* gl
             destPixelSize = 2;
             destFormat = LOCAL_GL_RGB;
             destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
             break;
 
         default:
             MOZ_CRASH("Bad format.");
     }
-    MOZ_ASSERT(dest->Stride() == dest->Width() * destPixelSize);
+    MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride());
 
     GLenum readFormat = destFormat;
     GLenum readType = destType;
     bool needsTempSurf = !GetActualReadFormats(gl,
                                                destFormat, destType,
                                                readFormat, readType);
 
     nsAutoPtr<gfxImageSurface> tempSurf;
     gfxImageSurface* readSurf = nullptr;
-    int readPixelSize = 0;
+    int readAlignment = 0;
     if (needsTempSurf) {
         if (gl->DebugMode()) {
             NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
         }
         SurfaceFormat readFormatGFX;
 
         switch (readFormat) {
             case LOCAL_GL_RGBA:
             case LOCAL_GL_BGRA: {
                 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
                                          : SurfaceFormat::B8G8R8X8;
                 break;
             }
             case LOCAL_GL_RGB: {
-                MOZ_ASSERT(readPixelSize == 2);
+                MOZ_ASSERT(destPixelSize == 2);
                 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
                 readFormatGFX = SurfaceFormat::R5G6B5;
                 break;
             }
             default: {
                 MOZ_CRASH("Bad read format.");
             }
         }
 
         switch (readType) {
             case LOCAL_GL_UNSIGNED_BYTE: {
                 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
-                readPixelSize = 4;
+                readAlignment = 1;
                 break;
             }
             case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
                 MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
-                readPixelSize = 4;
+                readAlignment = 4;
                 break;
             }
             case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
                 MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
-                readPixelSize = 2;
+                readAlignment = 2;
                 break;
             }
             default: {
                 MOZ_CRASH("Bad read type.");
             }
         }
 
         tempSurf = new gfxImageSurface(dest->GetSize(),
                                        SurfaceFormatToImageFormat(readFormatGFX),
                                        false);
         readSurf = tempSurf;
     } else {
-        readPixelSize = destPixelSize;
+        // Figure out alignment. We don't need to know why, we just need it
+        // to be valid.
+        readAlignment = GuessAlignment(dest->Width(),
+                                       destPixelSize,
+                                       dest->Stride());
         readSurf = dest;
     }
-    MOZ_ASSERT(readPixelSize);
+    MOZ_ASSERT(readAlignment);
 
     GLint currentPackAlignment = 0;
     gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &currentPackAlignment);
 
-    if (currentPackAlignment != readPixelSize)
-        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readPixelSize);
+    if (currentPackAlignment != readAlignment)
+        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
 
     GLsizei width = dest->Width();
     GLsizei height = dest->Height();
 
     readSurf->Flush();
     gl->fReadPixels(0, 0,
                     width, height,
                     readFormat, readType,
                     readSurf->Data());
     readSurf->MarkDirty();
 
-    if (currentPackAlignment != readPixelSize)
+    if (currentPackAlignment != readAlignment)
         gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
 
     if (readSurf != dest) {
         MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
         MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
         // So we just copied in RGBA in big endian, or le: 0xAABBGGRR.
         // We want 0xAARRGGBB, so swap R and B:
         dest->Flush();
@@ -382,16 +414,18 @@ ReadPixelsIntoImageSurface(GLContext* gl
         dest->Format() == gfxImageFormat::ARGB32 &&
         width && height)
     {
         GLint alphaBits = 0;
         gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
         if (!alphaBits) {
             const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);
 
+            MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride());
+
             dest->Flush();
             uint32_t* itr = (uint32_t*)dest->Data();
             uint32_t testPixel = *itr;
             if ((testPixel & alphaMask) != alphaMask) {
                 // We need to set the alpha channel to 1.0 manually.
                 uint32_t* itrEnd = itr + width*height;  // Stride is guaranteed to be width*4.
 
                 for (; itr != itrEnd; itr++) {