Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / images / SkImageDecoder_libjpeg.cpp
index 99401e6..4f78865 100644 (file)
@@ -238,7 +238,10 @@ protected:
     virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
     virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
 #endif
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
+                                    void* planes[3], size_t rowBytes[3],
+                                    SkYUVColorSpace* colorSpace) SK_OVERRIDE;
 
 private:
 #ifdef SK_BUILD_FOR_ANDROID
@@ -322,19 +325,42 @@ static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
 }
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+
 // This guy exists just to aid in debugging, as it allows debuggers to just
 // set a break-point in one place to see all error exists.
-static bool return_false(const jpeg_decompress_struct& cinfo,
-                         const SkBitmap& bm, const char caller[]) {
+static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
+                         int width, int height, const char caller[]) {
     if (!(c_suppressJPEGImageDecoderErrors)) {
         char buffer[JMSG_LENGTH_MAX];
         cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
         SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
-                 cinfo.err->msg_code, buffer, caller, bm.width(), bm.height());
+                 cinfo.err->msg_code, buffer, caller, width, height);
     }
-    return false;   // must always return false
 }
 
+static bool return_false(const jpeg_decompress_struct& cinfo,
+                         const char caller[]) {
+    print_jpeg_decoder_errors(cinfo, 0, 0, caller);
+    return false;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+static bool return_false(const jpeg_decompress_struct& cinfo,
+                         const SkBitmap& bm, const char caller[]) {
+    print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
+    return false;
+}
+#endif
+
+static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
+                                             const SkBitmap& bm, const char caller[]) {
+    print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
+    return SkImageDecoder::kFailure;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 // 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) {
@@ -483,7 +509,6 @@ static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
 #endif
 }
 
-
 /**
    Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
    Used when decoding fails partway through reading scanlines to fill
@@ -529,7 +554,7 @@ static bool get_src_config(const jpeg_decompress_struct& cinfo,
     return true;
 }
 
-bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 #ifdef TIME_DECODE
     SkAutoTime atm("JPEG Decode");
 #endif
@@ -545,7 +570,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     // All objects need to be instantiated before this setjmp call so that
     // they will be cleaned up properly if an error occurs.
     if (setjmp(errorManager.fJmpBuf)) {
-        return return_false(cinfo, *bm, "setjmp");
+        return return_failure(cinfo, *bm, "setjmp");
     }
 
     initialize_info(&cinfo, &srcManager);
@@ -553,7 +578,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 
     int status = jpeg_read_header(&cinfo, true);
     if (status != JPEG_HEADER_OK) {
-        return return_false(cinfo, *bm, "read_header");
+        return return_failure(cinfo, *bm, "read_header");
     }
 
     /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
@@ -580,8 +605,9 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
         // individual pixel. It is very unlikely to be opaque, since
         // an opaque A8 bitmap would not be very interesting.
         // Otherwise, a jpeg image is opaque.
-        return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
-                                             colorType, alphaType));
+        bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
+                                                     colorType, alphaType));
+        return success ? kSuccess : kFailure;
     }
 
     /*  image_width and image_height are the original dimensions, available
@@ -605,10 +631,11 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
             // individual pixel. It is very unlikely to be opaque, since
             // an opaque A8 bitmap would not be very interesting.
             // Otherwise, a jpeg image is opaque.
-            return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
-                                                 colorType, alphaType));
+            bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
+                                                         colorType, alphaType));
+            return success ? kSuccess : kFailure;
         } else {
-            return return_false(cinfo, *bm, "start_decompress");
+            return return_failure(cinfo, *bm, "start_decompress");
         }
     }
     sampleSize = recompute_sampleSize(sampleSize, cinfo);
@@ -616,7 +643,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     // should we allow the Chooser (if present) to pick a colortype for us???
     if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
-        return return_false(cinfo, *bm, "chooseFromOneChoice");
+        return return_failure(cinfo, *bm, "chooseFromOneChoice");
     }
 #endif
 
@@ -628,10 +655,10 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
                                   colorType, alphaType));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
     if (!this->allocPixelRef(bm, NULL)) {
-        return return_false(cinfo, *bm, "allocPixelRef");
+        return return_failure(cinfo, *bm, "allocPixelRef");
     }
 
     SkAutoLockPixels alp(*bm);
@@ -654,15 +681,16 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
                 // so return early.  We will return a partial image.
                 fill_below_level(cinfo.output_scanline, bm);
                 cinfo.output_scanline = cinfo.output_height;
-                break;  // Skip to jpeg_finish_decompress()
+                jpeg_finish_decompress(&cinfo);
+                return kPartialSuccess;
             }
             if (this->shouldCancelDecode()) {
-                return return_false(cinfo, *bm, "shouldCancelDecode");
+                return return_failure(cinfo, *bm, "shouldCancelDecode");
             }
             rowptr += bpr;
         }
         jpeg_finish_decompress(&cinfo);
-        return true;
+        return kSuccess;
     }
 #endif
 
@@ -671,11 +699,11 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     int srcBytesPerPixel;
 
     if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
-        return return_false(cinfo, *bm, "jpeg colorspace");
+        return return_failure(cinfo, *bm, "jpeg colorspace");
     }
 
     if (!sampler.begin(bm, sc, *this)) {
-        return return_false(cinfo, *bm, "sampler.begin");
+        return return_failure(cinfo, *bm, "sampler.begin");
     }
 
     SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
@@ -683,7 +711,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 
     //  Possibly skip initial rows [sampler.srcY0]
     if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
-        return return_false(cinfo, *bm, "skip rows");
+        return return_failure(cinfo, *bm, "skip rows");
     }
 
     // now loop through scanlines until y == bm->height() - 1
@@ -695,10 +723,11 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
             // so return early.  We will return a partial image.
             fill_below_level(y, bm);
             cinfo.output_scanline = cinfo.output_height;
-            break;  // Skip to jpeg_finish_decompress()
+            jpeg_finish_decompress(&cinfo);
+            return kSuccess;
         }
         if (this->shouldCancelDecode()) {
-            return return_false(cinfo, *bm, "shouldCancelDecode");
+            return return_failure(cinfo, *bm, "shouldCancelDecode");
         }
 
         if (JCS_CMYK == cinfo.out_color_space) {
@@ -712,20 +741,205 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
         }
 
         if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
-            return return_false(cinfo, *bm, "skip rows");
+            return return_failure(cinfo, *bm, "skip rows");
         }
     }
 
     // we formally skip the rest, so we don't get a complaint from libjpeg
     if (!skip_src_rows(&cinfo, srcRow,
                        cinfo.output_height - cinfo.output_scanline)) {
-        return return_false(cinfo, *bm, "skip rows");
+        return return_failure(cinfo, *bm, "skip rows");
+    }
+    jpeg_finish_decompress(&cinfo);
+
+    return kSuccess;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SizeType {
+    kSizeForMemoryAllocation_SizeType,
+    kActualSize_SizeType
+};
+
+static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
+                                SizeType sizeType) {
+    if (sizeType == kSizeForMemoryAllocation_SizeType) {
+        return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
+                             info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
+    }
+    return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
+                         info.cur_comp_info[component]->downsampled_height);
+}
+
+static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
+                                    SizeType sizeType) {
+    for (int i = 0; i < 3; ++i) {
+        componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
+    }
+}
+
+static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
+    // U size and V size have to be the same if we're calling output_raw_data()
+    SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
+    SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
+
+    JSAMPARRAY bufferraw[3];
+    JSAMPROW bufferraw2[32];
+    bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
+    bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
+    bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
+    int yWidth = cinfo.output_width;
+    int yHeight = cinfo.output_height;
+    int yMaxH = yHeight - 1;
+    int v = cinfo.cur_comp_info[0]->v_samp_factor;
+    int uvMaxH = uvSize.height() - 1;
+    JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
+    JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
+    JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
+    size_t rowBytesY = rowBytes[0];
+    size_t rowBytesU = rowBytes[1];
+    size_t rowBytesV = rowBytes[2];
+
+    int yScanlinesToRead = DCTSIZE * v;
+    SkAutoMalloc lastRowStorage(yWidth * 8);
+    JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
+    JSAMPROW uLastRow = yLastRow + 2 * yWidth;
+    JSAMPROW vLastRow = uLastRow + 2 * yWidth;
+    JSAMPROW dummyRow = vLastRow + 2 * yWidth;
+
+    while (cinfo.output_scanline < cinfo.output_height) {
+        // Request 8 or 16 scanlines: returns 0 or more scanlines.
+        bool hasYLastRow(false), hasUVLastRow(false);
+        // Assign 8 or 16 rows of memory to read the Y channel.
+        for (int i = 0; i < yScanlinesToRead; ++i) {
+            int scanline = (cinfo.output_scanline + i);
+            if (scanline < yMaxH) {
+                bufferraw2[i] = &outputY[scanline * rowBytesY];
+            } else if (scanline == yMaxH) {
+                bufferraw2[i] = yLastRow;
+                hasYLastRow = true;
+            } else {
+                bufferraw2[i] = dummyRow;
+            }
+        }
+        int scaledScanline = cinfo.output_scanline / v;
+        // Assign 8 rows of memory to read the U and V channels.
+        for (int i = 0; i < 8; ++i) {
+            int scanline = (scaledScanline + i);
+            if (scanline < uvMaxH) {
+                bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
+                bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
+            } else if (scanline == uvMaxH) {
+                bufferraw2[16 + i] = uLastRow;
+                bufferraw2[24 + i] = vLastRow;
+                hasUVLastRow = true;
+            } else {
+                bufferraw2[16 + i] = dummyRow;
+                bufferraw2[24 + i] = dummyRow;
+            }
+        }
+        JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
+
+        if (scanlinesRead == 0) {
+            return false;
+        }
+
+        if (hasYLastRow) {
+            memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
+        }
+        if (hasUVLastRow) {
+            memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
+            memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
+        }
+    }
+
+    cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
+
+    return true;
+}
+
+bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
+                                            void* planes[3], size_t rowBytes[3],
+                                            SkYUVColorSpace* colorSpace) {
+#ifdef TIME_DECODE
+    SkAutoTime atm("JPEG YUV8 Decode");
+#endif
+
+    if (this->getSampleSize() != 1) {
+        return false; // Resizing not supported
+    }
+
+    JPEGAutoClean autoClean;
+
+    jpeg_decompress_struct  cinfo;
+    skjpeg_source_mgr       srcManager(stream, this);
+
+    skjpeg_error_mgr errorManager;
+    set_error_mgr(&cinfo, &errorManager);
+
+    // All objects need to be instantiated before this setjmp call so that
+    // they will be cleaned up properly if an error occurs.
+    if (setjmp(errorManager.fJmpBuf)) {
+        return return_false(cinfo, "setjmp YUV8");
+    }
+
+    initialize_info(&cinfo, &srcManager);
+    autoClean.set(&cinfo);
+
+    int status = jpeg_read_header(&cinfo, true);
+    if (status != JPEG_HEADER_OK) {
+        return return_false(cinfo, "read_header YUV8");
+    }
+
+    if (cinfo.jpeg_color_space != JCS_YCbCr) {
+        // It's not an error to not be encoded in YUV, so no need to use return_false()
+        return false;
+    }
+
+    cinfo.out_color_space = JCS_YCbCr;
+    cinfo.raw_data_out = TRUE;
+
+    if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
+        update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
+        return true;
+    }
+
+    set_dct_method(*this, &cinfo);
+
+    SkASSERT(1 == cinfo.scale_num);
+    cinfo.scale_denom = 1;
+
+    turn_off_visual_optimizations(&cinfo);
+
+#ifdef ANDROID_RGB
+    cinfo.dither_mode = JDITHER_NONE;
+#endif
+
+    /*  image_width and image_height are the original dimensions, available
+        after jpeg_read_header(). To see the scaled dimensions, we have to call
+        jpeg_start_decompress(), and then read output_width and output_height.
+    */
+    if (!jpeg_start_decompress(&cinfo)) {
+        return return_false(cinfo, "start_decompress YUV8");
     }
+
+    if (!output_raw_data(cinfo, planes, rowBytes)) {
+        return return_false(cinfo, "output_raw_data");
+    }
+
+    update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
     jpeg_finish_decompress(&cinfo);
 
+    if (NULL != colorSpace) {
+        *colorSpace = kJPEG_SkYUVColorSpace;
+    }
+
     return true;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 #ifdef SK_BUILD_FOR_ANDROID
 bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {