Add query for block dimensions of a given format

R=robertphillips@google.com

Author: krajcevski@google.com

Review URL: https://codereview.chromium.org/422023006
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 547a05e..ba62858 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -13,7 +13,6 @@
 
 #include "SkData.h"
 #include "SkStrokeRec.h"
-#include "SkTextureCompressor.h"
 
 // TODO: try to remove this #include
 #include "GrContext.h"
@@ -37,6 +36,20 @@
     return modeMap[op];
 }
 
+static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
+    static const GrPixelConfig configMap[] = {
+        kLATC_GrPixelConfig,       // kLATC_Format,
+        kR11_EAC_GrPixelConfig,    // kR11_EAC_Format,
+        kASTC_12x12_GrPixelConfig  // kASTC_12x12_Format,
+    };
+    GR_STATIC_ASSERT(0 == SkTextureCompressor::kLATC_Format);
+    GR_STATIC_ASSERT(1 == SkTextureCompressor::kR11_EAC_Format);
+    GR_STATIC_ASSERT(2 == SkTextureCompressor::kASTC_12x12_Format);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(configMap) == SkTextureCompressor::kFormatCnt);
+
+    return configMap[fmt];
+}
+
 }
 
 /**
@@ -102,11 +115,16 @@
                           -resultBounds.fTop * SK_Scalar1);
     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
                                      resultBounds.height());
+
 #if GR_COMPRESS_ALPHA_MASK
+    fCompressedFormat = SkTextureCompressor::kR11_EAC_Format;
+
     // Make sure that the width is a multiple of 16 so that we can use
     // specialized SIMD instructions that compress 4 blocks at a time.
-    const int cmpWidth = (bounds.fRight + 15) & ~15;
-    const int cmpHeight = (bounds.fBottom + 3) & ~3;
+    int dimX, dimY;
+    SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
+    const int cmpWidth = dimX * ((bounds.fRight + (dimX - 1)) / dimX);
+    const int cmpHeight = dimY * ((bounds.fRight + (dimY - 1)) / dimY);
 #else
     const int cmpWidth = bounds.fRight;
     const int cmpHeight = bounds.fBottom;
@@ -135,21 +153,24 @@
     GrTextureDesc desc;
     desc.fWidth = fBM.width();
     desc.fHeight = fBM.height();
-    desc.fConfig = kAlpha_8_GrPixelConfig;
 
 #if GR_COMPRESS_ALPHA_MASK
-    static const int kCompressedBlockSize = 4;
-    static const GrPixelConfig kCompressedConfig = kR11_EAC_GrPixelConfig;
 
-    if (desc.fWidth % kCompressedBlockSize == 0 &&
-        desc.fHeight % kCompressedBlockSize == 0) {
-        desc.fConfig = kCompressedConfig;
-    }
+#ifdef SK_DEBUG
+    int dimX, dimY;
+    SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
+    SkASSERT((desc.fWidth % dimX) == 0);
+    SkASSERT((desc.fHeight % dimY) == 0);
+#endif
+
+    desc.fConfig = fmt_to_config(fCompressedFormat);
 
     // If this config isn't supported then we should fall back to A8
     if (!(fContext->getGpu()->caps()->isConfigTexturable(desc.fConfig))) {
         desc.fConfig = kAlpha_8_GrPixelConfig;
     }
+#else
+    desc.fConfig = kAlpha_8_GrPixelConfig;
 #endif
 
     texture->set(fContext, desc);
diff --git a/src/gpu/GrSWMaskHelper.h b/src/gpu/GrSWMaskHelper.h
index 340b1a3..ad1d432 100644
--- a/src/gpu/GrSWMaskHelper.h
+++ b/src/gpu/GrSWMaskHelper.h
@@ -15,6 +15,7 @@
 #include "SkMatrix.h"
 #include "SkRasterClip.h"
 #include "SkRegion.h"
+#include "SkTextureCompressor.h"
 #include "SkTypes.h"
 
 class GrAutoScratchTexture;
@@ -101,6 +102,10 @@
     SkDraw          fDraw;
     SkRasterClip    fRasterClip;
 
+#if GR_COMPRESS_ALPHA_MASK
+    SkTextureCompressor::Format fCompressedFormat;
+#endif
+
     // Actually sends the texture data to the GPU. This is called from
     // toTexture with the data filled in depending on the texture config.
     void sendTextureData(GrTexture *texture, const GrTextureDesc& desc,
diff --git a/src/opts/SkTextureCompression_opts.h b/src/opts/SkTextureCompression_opts.h
index e618280..07d645f 100644
--- a/src/opts/SkTextureCompression_opts.h
+++ b/src/opts/SkTextureCompression_opts.h
@@ -14,4 +14,8 @@
 SkTextureCompressor::CompressionProc
 SkTextureCompressorGetPlatformProc(SkColorType colorType, SkTextureCompressor::Format fmt);
 
+// Returns true if dimX and dimY are set to the block size of the supplied
+// compression format according to how the platform can consume them. Returns false otherwise.
+bool SkTextureCompressorGetPlatformDims(SkTextureCompressor::Format fmt, int* dimX, int* dimY);
+
 #endif  // SkTextureCompression_opts_DEFINED
diff --git a/src/opts/SkTextureCompression_opts_arm.cpp b/src/opts/SkTextureCompression_opts_arm.cpp
index 3a97a5e..36ff15c 100644
--- a/src/opts/SkTextureCompression_opts_arm.cpp
+++ b/src/opts/SkTextureCompression_opts_arm.cpp
@@ -36,3 +36,24 @@
     }
 #endif
 }
+
+bool SkTextureCompressorGetPlatformDims(SkTextureCompressor::Format fmt, int* dimX, int* dimY) {
+#if SK_ARM_NEON_IS_NONE
+    return false;
+#else
+#if SK_ARM_NEON_IS_DYNAMIC
+    if (!sk_cpu_arm_has_neon()) {
+        return false;
+    }
+#endif
+    switch (fmt) {
+        case SkTextureCompressor::kR11_EAC_Format:
+            *dimX = 16;
+            *dimY = 4;
+            return true;
+        default:
+            return false;
+    }
+    return false;
+#endif
+}
diff --git a/src/opts/SkTextureCompression_opts_none.cpp b/src/opts/SkTextureCompression_opts_none.cpp
index f86fbf5..caa2118 100644
--- a/src/opts/SkTextureCompression_opts_none.cpp
+++ b/src/opts/SkTextureCompression_opts_none.cpp
@@ -11,3 +11,7 @@
 SkTextureCompressorGetPlatformProc(SkColorType colorType, SkTextureCompressor::Format fmt) {
     return NULL;
 }
+
+bool SkTextureCompressorGetPlatformDims(SkTextureCompressor::Format fmt, int* dimX, int* dimY) {
+    return false;
+}
diff --git a/src/utils/SkTextureCompressor.cpp b/src/utils/SkTextureCompressor.cpp
index 43d906a..30fd307 100644
--- a/src/utils/SkTextureCompressor.cpp
+++ b/src/utils/SkTextureCompressor.cpp
@@ -20,21 +20,48 @@
 
 namespace SkTextureCompressor {
 
+void GetBlockDimensions(Format format, int* dimX, int* dimY) {
+    if (NULL == dimX || NULL == dimY) {
+        return;
+    }
+
+    if (SkTextureCompressorGetPlatformDims(format, dimX, dimY)) {
+        return;
+    }
+
+    switch(format) {
+        // These formats are 64 bits per 4x4 block.
+        default:
+            SkDEBUGFAIL("Unknown compression format!");
+            // fall through
+        case kR11_EAC_Format:
+        case kLATC_Format:
+            *dimX = 4;
+            *dimY = 4;
+            break;
+
+        // This format is 12x12 blocks to 128 bits.
+        case kASTC_12x12_Format:
+            *dimX = 12;
+            *dimY = 12;
+            break;        
+    }
+}
+
 int GetCompressedDataSize(Format fmt, int width, int height) {
-    int blockDimension = 0;
+    int dimX, dimY;
+    GetBlockDimensions(fmt, &dimX, &dimY);
     int encodedBlockSize = 0;
             
     switch (fmt) {
         // These formats are 64 bits per 4x4 block.
         case kR11_EAC_Format:
         case kLATC_Format:
-            blockDimension = 4;
             encodedBlockSize = 8;
             break;
 
         // This format is 12x12 blocks to 128 bits.
         case kASTC_12x12_Format:
-            blockDimension = 12;
             encodedBlockSize = 16;
             break;
 
@@ -43,9 +70,9 @@
             return -1;
     }
 
-    if(((width % blockDimension) == 0) && ((height % blockDimension) == 0)) {
-        const int blocksX = width / blockDimension;
-        const int blocksY = height / blockDimension;
+    if(((width % dimX) == 0) && ((height % dimY) == 0)) {
+        const int blocksX = width / dimX;
+        const int blocksY = height / dimY;
 
         return blocksX * blocksY * encodedBlockSize;
     }
diff --git a/src/utils/SkTextureCompressor.h b/src/utils/SkTextureCompressor.h
index 5238ba2..c6305ba 100644
--- a/src/utils/SkTextureCompressor.h
+++ b/src/utils/SkTextureCompressor.h
@@ -55,6 +55,12 @@
     // RGB source data into an R11 EAC texture, you're gonna have a bad time.
     SkBlitter* CreateBlitterForFormat(int width, int height, void* compressedBuffer,
                                       Format format);
+
+    // Returns the desired dimensions of the block size for the given format. These dimensions
+    // don't necessarily correspond to the hardware-specified dimensions, since there may
+    // be specialized algorithms that operate on multiple blocks at once. These dimensions
+    // reflect that optimization and return the appropriate operable dimensions.
+    void GetBlockDimensions(Format format, int* dimX, int* dimY);
 }
 
 #endif