Allow decoding JPEG into A8.

If the original image is grayscale, allow decoding into A8.

Change the size of PrefConfigTable to allow for 8bit gray, a new source config.

Add a new sampler to SkScaledBitmapSampler to 'convert' to A8.

FIXME: Should there be a dithered option for gray scale?

R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10157 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 5d94bb1..ccdc6df 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -145,31 +145,37 @@
         fUsePrefTable = false;
     } else {
         fUsePrefTable = true;
-        memcpy(fPrefTable, pref, sizeof(fPrefTable));
+        fPrefTable.fPrefFor_8Index_NoAlpha_src = pref[0];
+        fPrefTable.fPrefFor_8Index_YesAlpha_src = pref[1];
+        fPrefTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config;
+        fPrefTable.fPrefFor_8bpc_NoAlpha_src = pref[4];
+        fPrefTable.fPrefFor_8bpc_YesAlpha_src = pref[5];
     }
 }
 
+void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
+    fUsePrefTable = true;
+    fPrefTable = prefTable;
+}
+
 SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
                                                bool srcHasAlpha) const {
     SkBitmap::Config config;
 
     if (fUsePrefTable) {
-        int index = 0;
         switch (srcDepth) {
             case kIndex_SrcDepth:
-                index = 0;
+                config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
+                                     : fPrefTable.fPrefFor_8Index_NoAlpha_src;
                 break;
-            case k16Bit_SrcDepth:
-                index = 2;
+            case k8BitGray_SrcDepth:
+                config = fPrefTable.fPrefFor_8Gray_src;
                 break;
             case k32Bit_SrcDepth:
-                index = 4;
+                config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
+                                     : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
                 break;
         }
-        if (srcHasAlpha) {
-            index += 1;
-        }
-        config = fPrefTable[index];
     } else {
         config = fDefaultPref;
     }
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index bea1e98..e4d7ace 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -289,21 +289,33 @@
     /* this gives another few percents */
     cinfo.do_block_smoothing = 0;
 
+    SrcDepth srcDepth = k32Bit_SrcDepth;
     /* default format is RGB */
     if (cinfo.jpeg_color_space == JCS_CMYK) {
         // libjpeg cannot convert from CMYK to RGB - here we set up
         // so libjpeg will give us CMYK samples back and we will
         // later manually convert them to RGB
         cinfo.out_color_space = JCS_CMYK;
+    } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+        cinfo.out_color_space = JCS_GRAYSCALE;
+        srcDepth = k8BitGray_SrcDepth;
     } else {
         cinfo.out_color_space = JCS_RGB;
     }
 
-    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
+    SkBitmap::Config config = this->getPrefConfig(srcDepth, false);
     // only these make sense for jpegs
-    if (config != SkBitmap::kARGB_8888_Config &&
-        config != SkBitmap::kARGB_4444_Config &&
-        config != SkBitmap::kRGB_565_Config) {
+    if (SkBitmap::kA8_Config == config) {
+        if (cinfo.jpeg_color_space != JCS_GRAYSCALE) {
+            // Converting from a non grayscale image to A8 is
+            // not currently supported.
+            config = SkBitmap::kARGB_8888_Config;
+            // Change the output from jpeg back to RGB.
+            cinfo.out_color_space = JCS_RGB;
+        }
+    } else if (config != SkBitmap::kARGB_8888_Config &&
+               config != SkBitmap::kARGB_4444_Config &&
+               config != SkBitmap::kRGB_565_Config) {
         config = SkBitmap::kARGB_8888_Config;
     }
 
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
index ca41de9..021f86b 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -289,6 +289,15 @@
     return false;
 }
 
+// A8
+static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int,
+                            const SkPMColor[]) {
+    memcpy(dstRow, src, width);
+    return true;
+}
+
 // 8888 Unpremul
 
 static bool Sample_Gray_D8888_Unpremul(void* SK_RESTRICT dstRow,
@@ -396,6 +405,12 @@
         NULL,                           NULL,
         Sample_Index_DI,                Sample_Index_DI,
         NULL,                           NULL,
+        // A8
+        Sample_Gray_DA8,                Sample_Gray_DA8,
+        NULL,                           NULL,
+        NULL,                           NULL,
+        NULL,                           NULL,
+        NULL,                           NULL,
         // 8888 Unpremul (no dither distinction)
         Sample_Gray_D8888_Unpremul,     Sample_Gray_D8888_Unpremul,
         Sample_RGBx_D8888,              Sample_RGBx_D8888,
@@ -405,7 +420,7 @@
     };
     // The jump between dst configs in the table
     static const int gProcDstConfigSpan = 10;
-    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 5 * gProcDstConfigSpan,
+    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 6 * gProcDstConfigSpan,
                       gProcs_has_the_wrong_number_of_entries);
 
     fCTable = ctable;
@@ -456,6 +471,9 @@
         case SkBitmap::kIndex8_Config:
             index += 3 * gProcDstConfigSpan;
             break;
+        case SkBitmap::kA8_Config:
+            index += 4 * gProcDstConfigSpan;
+            break;
         default:
             return false;
     }
@@ -464,7 +482,7 @@
         if (dst->config() != SkBitmap::kARGB_8888_Config) {
             return false;
         }
-        index += 4 * gProcDstConfigSpan;
+        index += 5 * gProcDstConfigSpan;
     }
 
     fRowProc = gProcs[index];