Handle gray alpha conversions in SkSwizzler
authormsarett <msarett@google.com>
Wed, 3 Feb 2016 18:44:46 +0000 (10:44 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 3 Feb 2016 18:44:46 +0000 (10:44 -0800)
Nothing fancy here.  By doing the entire conversion ourselves, we
only need to make one conversion pass over each row.  Additionally,
we optimize the premultiply since we know each color component of
the pixel is identical.

This will also enable us to follow up with platform specific
optimizations.

PNG Decode Time Nexus 6P (for a test set of GrayAlpha encoded PNGs)
Regular   Unpremul 0.95x
Zero Init Unpremul 0.94x
Regular   Premul   0.91x
Zero Init Premul   0.90x

BUG=skia:4767
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1665583002

Review URL: https://codereview.chromium.org/1665583002

src/codec/SkPngCodec.cpp
src/codec/SkSwizzler.cpp
src/codec/SkSwizzler.h

index 710630d..232373c 100644 (file)
@@ -297,9 +297,10 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
             }
 
             if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
-                // Convert to RGBA if there is a transparency chunk.
                 png_set_tRNS_to_alpha(png_ptr);
-                png_set_gray_to_rgb(png_ptr);
+
+                // We will recommend kN32 here since we do not support kGray
+                // with alpha.
                 colorType = kN32_SkColorType;
                 alphaType = kUnpremul_SkAlphaType;
             } else {
@@ -308,8 +309,8 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
             }
             break;
         case PNG_COLOR_TYPE_GRAY_ALPHA:
-            // Convert to RGBA if the image has alpha.
-            png_set_gray_to_rgb(png_ptr);
+            // We will recommend kN32 here since we do not support anything
+            // similar to GRAY_ALPHA.
             colorType = kN32_SkColorType;
             alphaType = kUnpremul_SkAlphaType;
             break;
@@ -384,10 +385,10 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
     }
     png_read_update_info(fPng_ptr, fInfo_ptr);
 
-    // srcColorType was determined in read_header() which determined png color type
-    const SkColorType srcColorType = this->getInfo().colorType();
+    // suggestedColorType was determined in read_header() based on the encodedColorType
+    const SkColorType suggestedColorType = this->getInfo().colorType();
 
-    switch (srcColorType) {
+    switch (suggestedColorType) {
         case kIndex_8_SkColorType:
             //decode palette to Skia format
             fSrcConfig = SkSwizzler::kIndex;
@@ -399,13 +400,27 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
         case kGray_8_SkColorType:
             fSrcConfig = SkSwizzler::kGray;
             break;
-        case kN32_SkColorType:
-            if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
+        case kN32_SkColorType: {
+            const uint8_t encodedColorType = png_get_color_type(fPng_ptr, fInfo_ptr);
+            if (PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType ||
+                    PNG_COLOR_TYPE_GRAY == encodedColorType) {
+                // If encodedColorType is GRAY, there must be a transparent chunk.
+                // Otherwise, suggestedColorType would be kGray.  We have already
+                // instructed libpng to convert the transparent chunk to alpha,
+                // so we can treat both GRAY and GRAY_ALPHA as kGrayAlpha.
+                SkASSERT(encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA ||
+                        png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS));
+
+                fSrcConfig = SkSwizzler::kGrayAlpha;
+            } else {
+                if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
                     fSrcConfig = SkSwizzler::kRGB;
                 } else {
                     fSrcConfig = SkSwizzler::kRGBA;
+                }
             }
             break;
+        }
         default:
             // We will always recommend one of the above colorTypes.
             SkASSERT(false);
index fa93a6e..ce6dd26 100644 (file)
@@ -295,6 +295,33 @@ static void swizzle_gray_to_565(
     }
 }
 
+// kGrayAlpha
+
+static void swizzle_grayalpha_to_n32_unpremul(
+        void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
+        const SkPMColor ctable[]) {
+
+    src += offset;
+    SkPMColor* dst32 = (SkPMColor*) dst;
+    for (int x = 0; x < width; x++) {
+        dst32[x] = SkPackARGB32NoCheck(src[1], src[0], src[0], src[0]);
+        src += deltaSrc;
+    }
+}
+
+static void swizzle_grayalpha_to_n32_premul(
+        void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
+        const SkPMColor ctable[]) {
+
+    src += offset;
+    SkPMColor* dst32 = (SkPMColor*) dst;
+    for (int x = 0; x < width; x++) {
+        uint8_t pmgray = SkMulDiv255Round(src[1], src[0]);
+        dst32[x] = SkPackARGB32NoCheck(src[1], pmgray, pmgray, pmgray);
+        src += deltaSrc;
+    }
+}
+
 // kBGRX
 
 static void swizzle_bgrx_to_n32(
@@ -556,6 +583,25 @@ static void swizzle_cmyk_to_565(
 }
 
 template <SkSwizzler::RowProc proc>
+void SkSwizzler::SkipLeadingGrayAlphaZerosThen(
+        void* dst, const uint8_t* src, int width,
+        int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) {
+    SkASSERT(!ctable);
+
+    const uint16_t* src16 = (const uint16_t*) (src + offset);
+    uint32_t* dst32 = (uint32_t*) dst;
+
+    // This may miss opportunities to skip when the output is premultiplied,
+    // e.g. for a src pixel 0x00FF which is not zero but becomes zero after premultiplication.
+    while (width > 0 && *src16 == 0x0000) {
+        width--;
+        dst32++;
+        src16 += deltaSrc / 2;
+    }
+    proc(dst32, (const uint8_t*)src16, width, bpp, deltaSrc, 0, ctable);
+}
+
+template <SkSwizzler::RowProc proc>
 void SkSwizzler::SkipLeading8888ZerosThen(
         void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth,
         int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) {
@@ -665,6 +711,28 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
                     break;
             }
             break;
+        case kGrayAlpha:
+            switch (dstInfo.colorType()) {
+                case kN32_SkColorType:
+                    if (dstInfo.alphaType() == kUnpremul_SkAlphaType) {
+                        if (SkCodec::kYes_ZeroInitialized == zeroInit) {
+                            proc = &SkipLeadingGrayAlphaZerosThen
+                                    <swizzle_grayalpha_to_n32_unpremul>;
+                        } else {
+                            proc = &swizzle_grayalpha_to_n32_unpremul;
+                        }
+                    } else {
+                        if (SkCodec::kYes_ZeroInitialized == zeroInit) {
+                            proc = &SkipLeadingGrayAlphaZerosThen<swizzle_grayalpha_to_n32_premul>;
+                        } else {
+                            proc = &swizzle_grayalpha_to_n32_premul;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
         case kBGR:
         case kBGRX:
             switch (dstInfo.colorType()) {
index eebf886..7eebe7f 100644 (file)
@@ -22,6 +22,7 @@ public:
         kUnknown,  // Invalid type.
         kBit,      // A single bit to distinguish between white and black.
         kGray,
+        kGrayAlpha,
         kIndex1,
         kIndex2,
         kIndex4,
@@ -55,6 +56,7 @@ public:
             case kIndex:
             case kNoOp8:
                 return 8;
+            case kGrayAlpha:
             case kNoOp16:
                 return 16;
             case kRGB:
@@ -162,6 +164,10 @@ private:
                                          int dstWidth, int bpp, int deltaSrc, int offset,
                                          const SkPMColor ctable[]);
 
+    template <RowProc Proc>
+    static void SkipLeadingGrayAlphaZerosThen(void* dst, const uint8_t* src, int width, int bpp,
+                                              int deltaSrc, int offset, const SkPMColor ctable[]);
+
     // May be NULL.  We have not implemented optimized functions for all supported transforms.
     const RowProc       fFastProc;
     // Always non-NULL.  Supports sampling.