Fix for CMYK jpeg decoding issue (69 - unable to read some jpeg files on android)

http://codereview.appspot.com/5785054/



git-svn-id: http://skia.googlecode.com/svn/trunk@3438 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp
index f3cb120..048ed21 100644
--- a/src/images/SkImageDecoder_Factory.cpp
+++ b/src/images/SkImageDecoder_Factory.cpp
@@ -12,6 +12,37 @@
 #include "SkStream.h"
 #include "SkTRegistry.h"
 
+//extern SkImageDecoder* sk_libbmp_dfactory(SkStream*);
+//extern SkImageDecoder* sk_libgif_dfactory(SkStream*);
+//extern SkImageDecoder* sk_libico_dfactory(SkStream*);
+extern SkImageDecoder* sk_libjpeg_dfactory(SkStream*);
+//extern SkImageDecoder* sk_libpng_dfactory(SkStream*);
+//extern SkImageDecoder* sk_wbmp_dfactory(SkStream*);
+
+// To get the various image decoding classes to register themselves
+// pre-main we need to ensure they are linked into the application.
+// Ultimately we need to move to using DLLs rather than tightly
+// coupling the factory with the file format classes.
+void ForceLinking()
+{
+    SkImageDecoder* codec = NULL;
+
+    // TODO: rather than force the linking here expose a
+    // "Sk*ImageDecoderCreate" function for each codec
+    // and let the app add these calls to force the linking.
+    // Besides decoupling the codecs from the factory this
+    // will also give the app the ability to circumvent the
+    // factory and explicitly create a decoder w/o reaching
+    // into Skia's guts
+
+//    codec = sk_libbmp_dfactory(NULL);
+//    codec = sk_libgif_dfactory(NULL);
+//    codec = sk_libico_dfactory(NULL);
+    codec = sk_libjpeg_dfactory(NULL);
+//    codec = sk_libpng_dfactory(NULL);
+//    codec = sk_wbmp_dfactory(NULL);
+}
+
 typedef SkTRegistry<SkImageDecoder*, SkStream*> DecodeReg;
 
 // N.B. You can't use "DecodeReg::gHead here" due to complex C++
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
index b5e49e8..8683e21 100644
--- a/src/images/SkImageDecoder_libbmp.cpp
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -27,7 +27,7 @@
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
 };
 
-static SkImageDecoder* Factory(SkStream* stream) {
+SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
     static const char kBmpMagic[] = { 'B', 'M' };
     
     size_t len = stream->getLength();
@@ -41,7 +41,7 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libbmp_dfactory);
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index 7a451a0..3bc33c3 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -330,7 +330,7 @@
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
     char buf[GIF_STAMP_LEN];
     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
@@ -342,4 +342,4 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index bb6bc95..226c84a 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -24,7 +24,6 @@
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
 };
 
-SkImageDecoder* SkCreateICOImageDecoder();
 SkImageDecoder* SkCreateICOImageDecoder() {
     return new SkICOImageDecoder;
 }
@@ -371,7 +370,7 @@
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+SkImageDecoder* sk_libico_dfactory(SkStream* stream) {
     // Check to see if the first four bytes are 0,0,1,0
     // FIXME: Is that required and sufficient?
     SkAutoMalloc autoMal(4);
@@ -386,5 +385,5 @@
     return SkNEW(SkICOImageDecoder);
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libico_dfactory);
 
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 77d383a..5cb45a2 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -146,6 +146,29 @@
     return false;   // must always return false
 }
 
+// Convert a scanline of CMYK samples to RGBX in place. Note that this
+// method moves the "scanline" pointer in its processing
+static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
+    // At this point we've received CMYK pixels from libjpeg. We
+    // perform a crude conversion to RGB (based on the formulae 
+    // from easyrgb.com):
+    //  CMYK -> CMY
+    //    C = ( C * (1 - K) + K )      // for each CMY component
+    //  CMY -> RGB
+    //    R = ( 1 - C ) * 255          // for each RGB component
+    // Unfortunately we are seeing inverted CMYK so all the original terms
+    // are 1-. This yields:
+    //  CMYK -> CMY
+    //    C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
+    // The conversion from CMY->RGB remains the same
+    for (unsigned int x = 0; x < width; ++x, scanline += 4) {
+        scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
+        scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
+        scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
+        scanline[3] = 255;
+    }
+}
+
 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 #ifdef TIME_DECODE
     AutoTimeMillis atm("JPEG Decode");
@@ -156,7 +179,7 @@
 
     jpeg_decompress_struct  cinfo;
     skjpeg_error_mgr        sk_err;
-    skjpeg_source_mgr       sk_stream(stream, this);
+    skjpeg_source_mgr       sk_stream(stream, this, false);
 
     cinfo.err = jpeg_std_error(&sk_err);
     sk_err.error_exit = skjpeg_error_exit;
@@ -201,7 +224,14 @@
     cinfo.do_block_smoothing = 0;
 
     /* default format is RGB */
-    cinfo.out_color_space = JCS_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 {
+        cinfo.out_color_space = JCS_RGB;
+    }
 
     SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
     // only these make sense for jpegs
@@ -213,9 +243,9 @@
 
 #ifdef ANDROID_RGB
     cinfo.dither_mode = JDITHER_NONE;
-    if (config == SkBitmap::kARGB_8888_Config) {
+    if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
         cinfo.out_color_space = JCS_RGBA_8888;
-    } else if (config == SkBitmap::kRGB_565_Config) {
+    } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
         cinfo.out_color_space = JCS_RGB_565;
         if (this->getDitherImage()) {
             cinfo.dither_mode = JDITHER_ORDERED;
@@ -300,10 +330,13 @@
         return true;
     }
 #endif
-    
+
     // check for supported formats
     SkScaledBitmapSampler::SrcConfig sc;
-    if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
+    if (JCS_CMYK == cinfo.out_color_space) {
+        // In this case we will manually convert the CMYK values to RGB
+        sc = SkScaledBitmapSampler::kRGBX;
+    } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
         sc = SkScaledBitmapSampler::kRGB;
 #ifdef ANDROID_RGB
     } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
@@ -322,7 +355,7 @@
                                   sampleSize);
 
     bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
-    // jpegs are always opauqe (i.e. have no per-pixel alpha)
+    // jpegs are always opaque (i.e. have no per-pixel alpha)
     bm->setIsOpaque(true);
 
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
@@ -332,12 +365,13 @@
         return return_false(cinfo, *bm, "allocPixelRef");
     }
 
-    SkAutoLockPixels alp(*bm);                          
+    SkAutoLockPixels alp(*bm);
     if (!sampler.begin(bm, sc, this->getDitherImage())) {
         return return_false(cinfo, *bm, "sampler.begin");
     }
 
-    uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
+    // The CMYK work-around relies on 4 components per pixel here
+    uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4);
 
     //  Possibly skip initial rows [sampler.srcY0]
     if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
@@ -354,7 +388,11 @@
         if (this->shouldCancelDecode()) {
             return return_false(cinfo, *bm, "shouldCancelDecode");
         }
-        
+
+        if (JCS_CMYK == cinfo.out_color_space) {
+            convert_CMYK_to_RGB(srcRow, cinfo.output_width);
+        }
+
         sampler.next(srcRow);
         if (bm->height() - 1 == y) {
             // we're done
@@ -593,7 +631,7 @@
         jpeg_start_compress(&cinfo, TRUE);
 
         const int       width = bm.width();
-        uint8_t*        oneRowP = (uint8_t*)oneRow.alloc(width * 3);
+        uint8_t*        oneRowP = (uint8_t*)oneRow.reset(width * 3);
 
         const SkPMColor* colors = ctLocker.lockColors(bm);
         const void*      srcRow = bm.getPixels();
@@ -618,7 +656,7 @@
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* DFactory(SkStream* stream) {
+SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
     static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
     static const size_t HEADER_SIZE = sizeof(gHeader);
 
@@ -634,9 +672,11 @@
     return SkNEW(SkJPEGImageDecoder);
 }
 
-static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
     return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
-static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
+
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index a7d910f..262cf54 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -151,7 +151,7 @@
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
     wbmp_head   head;
 
     if (head.init(stream)) {
@@ -160,5 +160,5 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
 
diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp
index aa5237f..e28c512 100644
--- a/src/images/SkJpegUtility.cpp
+++ b/src/images/SkJpegUtility.cpp
@@ -14,26 +14,9 @@
     skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
     src->next_input_byte = (const JOCTET*)src->fBuffer;
     src->bytes_in_buffer = 0;
-    src->current_offset = 0;
     src->fStream->rewind();
 }
 
-static boolean sk_seek_input_data(j_decompress_ptr cinfo, long byte_offset) {
-    skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
-
-    if (byte_offset > src->current_offset) {
-        (void)src->fStream->skip(byte_offset - src->current_offset);
-    } else {
-        src->fStream->rewind();
-        (void)src->fStream->skip(byte_offset);
-    }
-
-    src->current_offset = byte_offset;
-    src->next_input_byte = (const JOCTET*)src->fBuffer;
-    src->bytes_in_buffer = 0;
-    return TRUE;
-}
-
 static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
     skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
     if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
@@ -46,7 +29,6 @@
         return FALSE;
     }
 
-    src->current_offset += bytes;
     src->next_input_byte = (const JOCTET*)src->fBuffer;
     src->bytes_in_buffer = bytes;
     return TRUE;
@@ -64,7 +46,6 @@
                 cinfo->err->error_exit((j_common_ptr)cinfo);
                 return;
             }
-            src->current_offset += bytes;
             bytesToSkip -= bytes;
         }
         src->next_input_byte = (const JOCTET*)src->fBuffer;
@@ -96,9 +77,7 @@
 static void skmem_init_source(j_decompress_ptr cinfo) {
     skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
     src->next_input_byte = (const JOCTET*)src->fMemoryBase;
-    src->start_input_byte = (const JOCTET*)src->fMemoryBase;
     src->bytes_in_buffer = src->fMemoryBaseSize;
-    src->current_offset = src->fMemoryBaseSize;
 }
 
 static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
@@ -127,8 +106,6 @@
                                      bool ownStream) : fStream(stream) {
     fDecoder = decoder;
     const void* baseAddr = stream->getMemoryBase();
-    size_t bufferSize = 4096;
-    size_t len;
     fMemoryBase = NULL;
     fUnrefStream = ownStream;
     fMemoryBaseSize = 0;
@@ -138,7 +115,6 @@
     skip_input_data = sk_skip_input_data;
     resync_to_restart = sk_resync_to_restart;
     term_source = sk_term_source;
-    seek_input_data = sk_seek_input_data;
 //    SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
 }