Support decoding Gray to A8 in PNG.
authorscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 18 Jul 2013 20:03:15 +0000 (20:03 +0000)
committerscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 18 Jul 2013 20:03:15 +0000 (20:03 +0000)
Move the code which sets the filler and forces gray to rgb after
we get the config, so we can skip them if the caller wants A8.

Call set_gray_to_rgb consistently for both normal decode and subset
decode.

In PNG, prevent subset decodes from alternating configs, which
would otherwise fail.

Use SK_RESTRICT for pointers in getBitmapConfig.

Ref the SkStream input to buildTileIndex, so it will not be destroyed
before calling decodeSubset.

Convert some fields to match Skia style.

Builds on https://codereview.chromium.org/18083026/, which has not yet been checked in.

R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10162 2bbb7eff-a529-9590-31e7-b0007b416f81

src/images/SkImageDecoder_libpng.cpp

index d59a67b..4c44b91 100644 (file)
@@ -42,18 +42,24 @@ extern "C" {
 
 class SkPNGImageIndex {
 public:
-    SkPNGImageIndex(png_structp png_ptr, png_infop info_ptr) {
-        this->png_ptr = png_ptr;
-        this->info_ptr = info_ptr;
+    SkPNGImageIndex(SkStream* stream, png_structp png_ptr, png_infop info_ptr)
+        : fStream(stream)
+        , fPng_ptr(png_ptr)
+        , fInfo_ptr(info_ptr)
+        , fConfig(SkBitmap::kNo_Config) {
+        SkASSERT(stream != NULL);
+        stream->ref();
     }
     ~SkPNGImageIndex() {
-        if (NULL != png_ptr) {
-            png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+        if (NULL != fPng_ptr) {
+            png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
         }
     }
 
-    png_structp png_ptr;
-    png_infop info_ptr;
+    SkAutoTUnref<SkStream>  fStream;
+    png_structp             fPng_ptr;
+    png_infop               fInfo_ptr;
+    SkBitmap::Config        fConfig;
 };
 
 class SkPNGImageDecoder : public SkImageDecoder {
@@ -261,10 +267,6 @@ bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
         png_set_expand_gray_1_2_4_to_8(png_ptr);
     }
 
-    /* Make a grayscale image into RGB. */
-    if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
-        png_set_gray_to_rgb(png_ptr);
-    }
     return true;
 }
 
@@ -326,11 +328,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
 
     SkAutoLockPixels alp(*decodedBitmap);
 
-    /* Add filler (or alpha) byte (before/after each RGB triplet) */
-    if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) {
-        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
-    }
-
     /* Turn on interlace handling.  REQUIRED if you are not using
     *  png_read_image().  To see how to handle interlacing passes,
     *  see the png_read_row() method below:
@@ -344,7 +341,11 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
     */
     png_read_update_info(png_ptr, info_ptr);
 
-    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+    if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
+        && 1 == sampleSize) {
+        // A8 is only allowed if the original was GRAY.
+        SkASSERT(config != SkBitmap::kA8_Config
+                 || PNG_COLOR_TYPE_GRAY == colorType);
         for (int i = 0; i < number_passes; i++) {
             for (png_uint_32 y = 0; y < origHeight; y++) {
                 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
@@ -358,6 +359,11 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
         if (colorTable != NULL) {
             sc = SkScaledBitmapSampler::kIndex;
             srcBytesPerPixel = 1;
+        } else if (SkBitmap::kA8_Config == config) {
+            // A8 is only allowed if the original was GRAY.
+            SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
+            sc = SkScaledBitmapSampler::kGray;
+            srcBytesPerPixel = 1;
         } else if (hasAlpha) {
             sc = SkScaledBitmapSampler::kRGBA;
         } else {
@@ -436,8 +442,10 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
 
 
 bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
-                                        SkBitmap::Config *configp, bool * SK_RESTRICT hasAlphap,
-                                        bool *doDitherp, SkPMColor *theTranspColorp) {
+                                        SkBitmap::Config* SK_RESTRICT configp,
+                                        bool* SK_RESTRICT hasAlphap,
+                                        bool* SK_RESTRICT doDitherp,
+                                        SkPMColor* SK_RESTRICT theTranspColorp) {
     png_uint_32 origWidth, origHeight;
     int bitDepth, colorType;
     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
@@ -510,7 +518,14 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
             PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
             *hasAlphap = true;
         }
-        *configp = this->getPrefConfig(k32Bit_SrcDepth, *hasAlphap);
+
+        SrcDepth srcDepth = k32Bit_SrcDepth;
+        if (PNG_COLOR_TYPE_GRAY == colorType) {
+            srcDepth = k8BitGray_SrcDepth;
+            SkASSERT(!*hasAlphap);
+        }
+
+        *configp = this->getPrefConfig(srcDepth, *hasAlphap);
         // now match the request against our capabilities
         if (*hasAlphap) {
             if (*configp != SkBitmap::kARGB_4444_Config) {
@@ -518,7 +533,8 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
             }
         } else {
             if (*configp != SkBitmap::kRGB_565_Config &&
-                *configp != SkBitmap::kARGB_4444_Config) {
+                *configp != SkBitmap::kARGB_4444_Config &&
+                *configp != SkBitmap::kA8_Config) {
                 *configp = SkBitmap::kARGB_8888_Config;
             }
         }
@@ -546,6 +562,33 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
     if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
         *configp = SkBitmap::kARGB_8888_Config;
     }
+
+    if (fImageIndex != NULL) {
+        if (SkBitmap::kNo_Config == fImageIndex->fConfig) {
+            // This is the first time for this subset decode. From now on,
+            // all decodes must be in the same config.
+            fImageIndex->fConfig = *configp;
+        } else if (fImageIndex->fConfig != *configp) {
+            // Requesting a different config for a subsequent decode is not
+            // supported. Report failure before we make changes to png_ptr.
+            return false;
+        }
+    }
+
+    bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType
+                            && *configp != SkBitmap::kA8_Config;
+
+    // Unless the user is requesting A8, convert a grayscale image into RGB.
+    // GRAY_ALPHA will always be converted to RGB
+    if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        png_set_gray_to_rgb(png_ptr);
+    }
+
+    // Add filler (or alpha) byte (after each RGB triplet) if necessary.
+    if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
+        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+    }
+
     return true;
 }
 
@@ -647,7 +690,7 @@ bool SkPNGImageDecoder::onBuildTileIndex(SkStream* sk_stream, int *width, int *h
     if (fImageIndex) {
         SkDELETE(fImageIndex);
     }
-    fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (png_ptr, info_ptr));
+    fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (sk_stream, png_ptr, info_ptr));
 
     return true;
 }
@@ -657,8 +700,8 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
         return false;
     }
 
-    png_structp png_ptr = fImageIndex->png_ptr;
-    png_infop info_ptr = fImageIndex->info_ptr;
+    png_structp png_ptr = fImageIndex->fPng_ptr;
+    png_infop info_ptr = fImageIndex->fInfo_ptr;
     if (setjmp(png_jmpbuf(png_ptr))) {
         return false;
     }
@@ -724,11 +767,6 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
     }
     SkAutoLockPixels alp(decodedBitmap);
 
-    /* Add filler (or alpha) byte (before/after each RGB triplet) */
-    if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY) {
-        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
-    }
-
     /* Turn on interlace handling.  REQUIRED if you are not using
     * png_read_image().  To see how to handle interlacing passes,
     * see the png_read_row() method below:
@@ -752,7 +790,12 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
 
     int actualTop = rect.fTop;
 
-    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+    if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
+        && 1 == sampleSize) {
+        // A8 is only allowed if the original was GRAY.
+        SkASSERT(config != SkBitmap::kA8_Config
+                 || PNG_COLOR_TYPE_GRAY == colorType);
+
         for (int i = 0; i < number_passes; i++) {
             png_configure_decoder(png_ptr, &actualTop, i);
             for (int j = 0; j < rect.fTop - actualTop; j++) {
@@ -772,6 +815,11 @@ bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
         if (colorTable != NULL) {
             sc = SkScaledBitmapSampler::kIndex;
             srcBytesPerPixel = 1;
+        } else if (SkBitmap::kA8_Config == config) {
+            // A8 is only allowed if the original was GRAY.
+            SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
+            sc = SkScaledBitmapSampler::kGray;
+            srcBytesPerPixel = 1;
         } else if (hasAlpha) {
             sc = SkScaledBitmapSampler::kRGBA;
         } else {