зеркало из https://github.com/mozilla/moz-skia.git
Upstream changes from Android for decoding jpeg images.
Review URL: https://codereview.chromium.org/12438025 git-svn-id: http://skia.googlecode.com/svn/trunk@8267 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
4d9853288b
Коммит
113994051b
217
gyp/libjpeg.gyp
217
gyp/libjpeg.gyp
|
@ -13,110 +13,127 @@
|
||||||
'use_system_libjpeg%': 0,
|
'use_system_libjpeg%': 0,
|
||||||
},
|
},
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['use_system_libjpeg==0', {
|
['skia_os == "android"', {
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'libjpeg',
|
|
||||||
'type': 'static_library',
|
|
||||||
'sources': [
|
|
||||||
'../third_party/externals/libjpeg/jcapimin.c',
|
|
||||||
'../third_party/externals/libjpeg/jcapistd.c',
|
|
||||||
'../third_party/externals/libjpeg/jccoefct.c',
|
|
||||||
'../third_party/externals/libjpeg/jccolor.c',
|
|
||||||
'../third_party/externals/libjpeg/jcdctmgr.c',
|
|
||||||
'../third_party/externals/libjpeg/jchuff.c',
|
|
||||||
'../third_party/externals/libjpeg/jchuff.h',
|
|
||||||
'../third_party/externals/libjpeg/jcinit.c',
|
|
||||||
'../third_party/externals/libjpeg/jcmainct.c',
|
|
||||||
'../third_party/externals/libjpeg/jcmarker.c',
|
|
||||||
'../third_party/externals/libjpeg/jcmaster.c',
|
|
||||||
'../third_party/externals/libjpeg/jcomapi.c',
|
|
||||||
'../third_party/externals/libjpeg/jconfig.h',
|
|
||||||
'../third_party/externals/libjpeg/jcparam.c',
|
|
||||||
'../third_party/externals/libjpeg/jcphuff.c',
|
|
||||||
'../third_party/externals/libjpeg/jcprepct.c',
|
|
||||||
'../third_party/externals/libjpeg/jcsample.c',
|
|
||||||
'../third_party/externals/libjpeg/jdapimin.c',
|
|
||||||
'../third_party/externals/libjpeg/jdapistd.c',
|
|
||||||
'../third_party/externals/libjpeg/jdatadst.c',
|
|
||||||
'../third_party/externals/libjpeg/jdatasrc.c',
|
|
||||||
'../third_party/externals/libjpeg/jdcoefct.c',
|
|
||||||
'../third_party/externals/libjpeg/jdcolor.c',
|
|
||||||
'../third_party/externals/libjpeg/jdct.h',
|
|
||||||
'../third_party/externals/libjpeg/jddctmgr.c',
|
|
||||||
'../third_party/externals/libjpeg/jdhuff.c',
|
|
||||||
'../third_party/externals/libjpeg/jdhuff.h',
|
|
||||||
'../third_party/externals/libjpeg/jdinput.c',
|
|
||||||
'../third_party/externals/libjpeg/jdmainct.c',
|
|
||||||
'../third_party/externals/libjpeg/jdmarker.c',
|
|
||||||
'../third_party/externals/libjpeg/jdmaster.c',
|
|
||||||
'../third_party/externals/libjpeg/jdmerge.c',
|
|
||||||
'../third_party/externals/libjpeg/jdphuff.c',
|
|
||||||
'../third_party/externals/libjpeg/jdpostct.c',
|
|
||||||
'../third_party/externals/libjpeg/jdsample.c',
|
|
||||||
'../third_party/externals/libjpeg/jerror.c',
|
|
||||||
'../third_party/externals/libjpeg/jerror.h',
|
|
||||||
'../third_party/externals/libjpeg/jfdctflt.c',
|
|
||||||
'../third_party/externals/libjpeg/jfdctfst.c',
|
|
||||||
'../third_party/externals/libjpeg/jfdctint.c',
|
|
||||||
'../third_party/externals/libjpeg/jidctflt.c',
|
|
||||||
'../third_party/externals/libjpeg/jidctfst.c',
|
|
||||||
'../third_party/externals/libjpeg/jidctint.c',
|
|
||||||
'../third_party/externals/libjpeg/jinclude.h',
|
|
||||||
'../third_party/externals/libjpeg/jmemmgr.c',
|
|
||||||
'../third_party/externals/libjpeg/jmemnobs.c',
|
|
||||||
'../third_party/externals/libjpeg/jmemsys.h',
|
|
||||||
'../third_party/externals/libjpeg/jmorecfg.h',
|
|
||||||
'../third_party/externals/libjpeg/jpegint.h',
|
|
||||||
'../third_party/externals/libjpeg/jpeglib.h',
|
|
||||||
'../third_party/externals/libjpeg/jquant1.c',
|
|
||||||
'../third_party/externals/libjpeg/jquant2.c',
|
|
||||||
'../third_party/externals/libjpeg/jutils.c',
|
|
||||||
'../third_party/externals/libjpeg/jversion.h',
|
|
||||||
],
|
|
||||||
'direct_dependent_settings': {
|
|
||||||
'include_dirs': [
|
|
||||||
'../third_party/externals/libjpeg',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'conditions': [
|
|
||||||
['OS!="win"', {
|
|
||||||
'product_name': 'jpeg',
|
|
||||||
'cflags': [
|
|
||||||
'-Wno-main', # supresses warnings about naming things "main"
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['OS=="android"', {
|
|
||||||
'cflags!': [
|
|
||||||
'-fno-rtti', # supresses warnings about invalid option of non-C++ code
|
|
||||||
'-Wall',
|
|
||||||
'-Werror',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['OS in ["linux", "freebsd", "openbsd", "solaris", "nacl"]', {
|
|
||||||
'cflags!': [
|
|
||||||
'-Werror',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}, {
|
|
||||||
'targets': [
|
'targets': [
|
||||||
{
|
{
|
||||||
'target_name': 'libjpeg',
|
'target_name': 'libjpeg',
|
||||||
'type': 'none',
|
'type': 'none',
|
||||||
'direct_dependent_settings': {
|
'dependencies': [
|
||||||
'defines': [
|
'android_deps.gyp:jpeg',
|
||||||
'USE_SYSTEM_LIBJPEG',
|
],
|
||||||
],
|
'export_dependent_settings': [
|
||||||
},
|
'android_deps.gyp:jpeg',
|
||||||
'link_settings': {
|
],
|
||||||
'libraries': [
|
},
|
||||||
'-ljpeg',
|
],
|
||||||
],
|
}, { # skia_os != android
|
||||||
},
|
'conditions': [
|
||||||
}
|
['use_system_libjpeg==0', {
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'libjpeg',
|
||||||
|
'type': 'static_library',
|
||||||
|
'sources': [
|
||||||
|
'../third_party/externals/libjpeg/jcapimin.c',
|
||||||
|
'../third_party/externals/libjpeg/jcapistd.c',
|
||||||
|
'../third_party/externals/libjpeg/jccoefct.c',
|
||||||
|
'../third_party/externals/libjpeg/jccolor.c',
|
||||||
|
'../third_party/externals/libjpeg/jcdctmgr.c',
|
||||||
|
'../third_party/externals/libjpeg/jchuff.c',
|
||||||
|
'../third_party/externals/libjpeg/jchuff.h',
|
||||||
|
'../third_party/externals/libjpeg/jcinit.c',
|
||||||
|
'../third_party/externals/libjpeg/jcmainct.c',
|
||||||
|
'../third_party/externals/libjpeg/jcmarker.c',
|
||||||
|
'../third_party/externals/libjpeg/jcmaster.c',
|
||||||
|
'../third_party/externals/libjpeg/jcomapi.c',
|
||||||
|
'../third_party/externals/libjpeg/jconfig.h',
|
||||||
|
'../third_party/externals/libjpeg/jcparam.c',
|
||||||
|
'../third_party/externals/libjpeg/jcphuff.c',
|
||||||
|
'../third_party/externals/libjpeg/jcprepct.c',
|
||||||
|
'../third_party/externals/libjpeg/jcsample.c',
|
||||||
|
'../third_party/externals/libjpeg/jdapimin.c',
|
||||||
|
'../third_party/externals/libjpeg/jdapistd.c',
|
||||||
|
'../third_party/externals/libjpeg/jdatadst.c',
|
||||||
|
'../third_party/externals/libjpeg/jdatasrc.c',
|
||||||
|
'../third_party/externals/libjpeg/jdcoefct.c',
|
||||||
|
'../third_party/externals/libjpeg/jdcolor.c',
|
||||||
|
'../third_party/externals/libjpeg/jdct.h',
|
||||||
|
'../third_party/externals/libjpeg/jddctmgr.c',
|
||||||
|
'../third_party/externals/libjpeg/jdhuff.c',
|
||||||
|
'../third_party/externals/libjpeg/jdhuff.h',
|
||||||
|
'../third_party/externals/libjpeg/jdinput.c',
|
||||||
|
'../third_party/externals/libjpeg/jdmainct.c',
|
||||||
|
'../third_party/externals/libjpeg/jdmarker.c',
|
||||||
|
'../third_party/externals/libjpeg/jdmaster.c',
|
||||||
|
'../third_party/externals/libjpeg/jdmerge.c',
|
||||||
|
'../third_party/externals/libjpeg/jdphuff.c',
|
||||||
|
'../third_party/externals/libjpeg/jdpostct.c',
|
||||||
|
'../third_party/externals/libjpeg/jdsample.c',
|
||||||
|
'../third_party/externals/libjpeg/jerror.c',
|
||||||
|
'../third_party/externals/libjpeg/jerror.h',
|
||||||
|
'../third_party/externals/libjpeg/jfdctflt.c',
|
||||||
|
'../third_party/externals/libjpeg/jfdctfst.c',
|
||||||
|
'../third_party/externals/libjpeg/jfdctint.c',
|
||||||
|
'../third_party/externals/libjpeg/jidctflt.c',
|
||||||
|
'../third_party/externals/libjpeg/jidctfst.c',
|
||||||
|
'../third_party/externals/libjpeg/jidctint.c',
|
||||||
|
'../third_party/externals/libjpeg/jinclude.h',
|
||||||
|
'../third_party/externals/libjpeg/jmemmgr.c',
|
||||||
|
'../third_party/externals/libjpeg/jmemnobs.c',
|
||||||
|
'../third_party/externals/libjpeg/jmemsys.h',
|
||||||
|
'../third_party/externals/libjpeg/jmorecfg.h',
|
||||||
|
'../third_party/externals/libjpeg/jpegint.h',
|
||||||
|
'../third_party/externals/libjpeg/jpeglib.h',
|
||||||
|
'../third_party/externals/libjpeg/jquant1.c',
|
||||||
|
'../third_party/externals/libjpeg/jquant2.c',
|
||||||
|
'../third_party/externals/libjpeg/jutils.c',
|
||||||
|
'../third_party/externals/libjpeg/jversion.h',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'../third_party/externals/libjpeg',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'conditions': [
|
||||||
|
['OS!="win"', {
|
||||||
|
'product_name': 'jpeg',
|
||||||
|
'cflags': [
|
||||||
|
'-Wno-main', # supresses warnings about naming things "main"
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS=="android"', {
|
||||||
|
'cflags!': [
|
||||||
|
'-fno-rtti', # supresses warnings about invalid option of non-C++ code
|
||||||
|
'-Wall',
|
||||||
|
'-Werror',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS in ["linux", "freebsd", "openbsd", "solaris", "nacl"]', {
|
||||||
|
'cflags!': [
|
||||||
|
'-Werror',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'libjpeg',
|
||||||
|
'type': 'none',
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'defines': [
|
||||||
|
'USE_SYSTEM_LIBJPEG',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'link_settings': {
|
||||||
|
'libraries': [
|
||||||
|
'-ljpeg',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}],
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,7 +15,10 @@
|
||||||
#include "SkScaledBitmapSampler.h"
|
#include "SkScaledBitmapSampler.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
#include "SkTemplates.h"
|
#include "SkTemplates.h"
|
||||||
|
#include "SkTime.h"
|
||||||
#include "SkUtils.h"
|
#include "SkUtils.h"
|
||||||
|
#include "SkRect.h"
|
||||||
|
#include "SkCanvas.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -23,7 +26,13 @@ extern "C" {
|
||||||
#include "jerror.h"
|
#include "jerror.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
// this enables timing code to report milliseconds for an encode
|
// Uncomment to enable the code path used by the Android framework with their
|
||||||
|
// custom image decoders.
|
||||||
|
//#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_DEBUG)
|
||||||
|
// #define SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
// These enable timing code that report milliseconds for an encoding/decoding
|
||||||
//#define TIME_ENCODE
|
//#define TIME_ENCODE
|
||||||
//#define TIME_DECODE
|
//#define TIME_DECODE
|
||||||
|
|
||||||
|
@ -31,39 +40,98 @@ extern "C" {
|
||||||
// disable for the moment, as we have some glitches when width != multiple of 4
|
// disable for the moment, as we have some glitches when width != multiple of 4
|
||||||
#define WE_CONVERT_TO_YUV
|
#define WE_CONVERT_TO_YUV
|
||||||
|
|
||||||
|
// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
|
||||||
|
// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID
|
||||||
|
/* Check if the device indicates that it has a large amount of system memory
|
||||||
|
* if so, increase the memory allocation to 30MB instead of the default 5MB.
|
||||||
|
*/
|
||||||
|
#ifdef ANDROID_LARGE_MEMORY_DEVICE
|
||||||
|
cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
|
||||||
|
#else
|
||||||
|
cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
|
||||||
|
#endif
|
||||||
|
#endif // SK_BUILD_FOR_ANDROID
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class SkJPEGImageIndex {
|
||||||
|
public:
|
||||||
|
SkJPEGImageIndex(SkStream* stream, SkImageDecoder* decoder)
|
||||||
|
: fSrcMgr(stream, decoder, true) {}
|
||||||
|
|
||||||
|
~SkJPEGImageIndex() {
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
jpeg_destroy_huffman_index(&fHuffmanIndex);
|
||||||
|
#endif
|
||||||
|
jpeg_finish_decompress(&fCInfo);
|
||||||
|
jpeg_destroy_decompress(&fCInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the cinfo struct using libjpeg and apply any necessary
|
||||||
|
* customizations.
|
||||||
|
*/
|
||||||
|
void initializeInfo() {
|
||||||
|
jpeg_create_decompress(&fCInfo);
|
||||||
|
overwrite_mem_buffer_size(&fCInfo);
|
||||||
|
fCInfo.src = &fSrcMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_decompress_struct* cinfo() { return &fCInfo; }
|
||||||
|
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
huffman_index* huffmanIndex() { return &fHuffmanIndex; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
skjpeg_source_mgr fSrcMgr;
|
||||||
|
jpeg_decompress_struct fCInfo;
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
huffman_index fHuffmanIndex;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
class SkJPEGImageDecoder : public SkImageDecoder {
|
class SkJPEGImageDecoder : public SkImageDecoder {
|
||||||
public:
|
public:
|
||||||
|
SkJPEGImageDecoder() {
|
||||||
|
fImageIndex = NULL;
|
||||||
|
fImageWidth = 0;
|
||||||
|
fImageHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SkJPEGImageDecoder() {
|
||||||
|
SkDELETE(fImageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
virtual Format getFormat() const {
|
virtual Format getFormat() const {
|
||||||
return kJPEG_Format;
|
return kJPEG_Format;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
|
||||||
|
virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
|
||||||
|
#endif
|
||||||
|
virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SkJPEGImageIndex* fImageIndex;
|
||||||
|
int fImageWidth;
|
||||||
|
int fImageHeight;
|
||||||
|
|
||||||
|
typedef SkImageDecoder INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "SkTime.h"
|
|
||||||
|
|
||||||
class AutoTimeMillis {
|
|
||||||
public:
|
|
||||||
AutoTimeMillis(const char label[]) : fLabel(label) {
|
|
||||||
if (!fLabel) {
|
|
||||||
fLabel = "";
|
|
||||||
}
|
|
||||||
fNow = SkTime::GetMSecs();
|
|
||||||
}
|
|
||||||
~AutoTimeMillis() {
|
|
||||||
SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
const char* fLabel;
|
|
||||||
SkMSec fNow;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Automatically clean up after throwing an exception */
|
/* Automatically clean up after throwing an exception */
|
||||||
class JPEGAutoClean {
|
class JPEGAutoClean {
|
||||||
public:
|
public:
|
||||||
|
@ -80,24 +148,6 @@ private:
|
||||||
jpeg_decompress_struct* cinfo_ptr;
|
jpeg_decompress_struct* cinfo_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SK_BUILD_FOR_ANDROID
|
|
||||||
|
|
||||||
/* For non-ndk builds we could look at the system's jpeg memory cap and use it
|
|
||||||
* if it is set. However, for now we will use the NDK compliant hardcoded values
|
|
||||||
*/
|
|
||||||
//#include <cutils/properties.h>
|
|
||||||
//static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
|
|
||||||
|
|
||||||
static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
|
|
||||||
#ifdef ANDROID_LARGE_MEMORY_DEVICE
|
|
||||||
cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
|
|
||||||
#else
|
|
||||||
cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/* If we need to better match the request, we might examine the image and
|
/* If we need to better match the request, we might examine the image and
|
||||||
|
@ -116,26 +166,39 @@ static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
|
||||||
/* These are initialized to 0, so if they have non-zero values, we assume
|
/* These are initialized to 0, so if they have non-zero values, we assume
|
||||||
they are "valid" (i.e. have been computed by libjpeg)
|
they are "valid" (i.e. have been computed by libjpeg)
|
||||||
*/
|
*/
|
||||||
return cinfo.output_width != 0 && cinfo.output_height != 0;
|
return 0 != cinfo.output_width && 0 != cinfo.output_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
|
static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
|
||||||
int count) {
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
JSAMPLE* rowptr = (JSAMPLE*)buffer;
|
JSAMPLE* rowptr = (JSAMPLE*)buffer;
|
||||||
int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
|
int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
|
||||||
if (row_count != 1) {
|
if (1 != row_count) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
|
||||||
|
huffman_index *index, void* buffer, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
JSAMPLE* rowptr = (JSAMPLE*)buffer;
|
||||||
|
int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
|
||||||
|
if (1 != row_count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// This guy exists just to aid in debugging, as it allows debuggers to just
|
// 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.
|
// set a break-point in one place to see all error exists.
|
||||||
static bool return_false(const jpeg_decompress_struct& cinfo,
|
static bool return_false(const jpeg_decompress_struct& cinfo,
|
||||||
const SkBitmap& bm, const char msg[]) {
|
const SkBitmap& bm, const char msg[]) {
|
||||||
#if 0
|
#ifdef SK_DEBUG
|
||||||
SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
|
SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
|
||||||
cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
|
cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
|
||||||
bm.width(), bm.height());
|
bm.width(), bm.height());
|
||||||
|
@ -168,34 +231,31 @@ static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
|
||||||
|
|
||||||
bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
#ifdef TIME_DECODE
|
#ifdef TIME_DECODE
|
||||||
AutoTimeMillis atm("JPEG Decode");
|
SkAutoTime atm("JPEG Decode");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SkAutoMalloc srcStorage;
|
|
||||||
JPEGAutoClean autoClean;
|
JPEGAutoClean autoClean;
|
||||||
|
|
||||||
jpeg_decompress_struct cinfo;
|
jpeg_decompress_struct cinfo;
|
||||||
skjpeg_error_mgr sk_err;
|
skjpeg_error_mgr errorManager;
|
||||||
skjpeg_source_mgr sk_stream(stream, this, false);
|
skjpeg_source_mgr srcManager(stream, this, false);
|
||||||
|
|
||||||
cinfo.err = jpeg_std_error(&sk_err);
|
cinfo.err = jpeg_std_error(&errorManager);
|
||||||
sk_err.error_exit = skjpeg_error_exit;
|
errorManager.error_exit = skjpeg_error_exit;
|
||||||
|
|
||||||
// All objects need to be instantiated before this setjmp call so that
|
// All objects need to be instantiated before this setjmp call so that
|
||||||
// they will be cleaned up properly if an error occurs.
|
// they will be cleaned up properly if an error occurs.
|
||||||
if (setjmp(sk_err.fJmpBuf)) {
|
if (setjmp(errorManager.fJmpBuf)) {
|
||||||
return return_false(cinfo, *bm, "setjmp");
|
return return_false(cinfo, *bm, "setjmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
jpeg_create_decompress(&cinfo);
|
jpeg_create_decompress(&cinfo);
|
||||||
autoClean.set(&cinfo);
|
autoClean.set(&cinfo);
|
||||||
|
|
||||||
#ifdef SK_BUILD_FOR_ANDROID
|
|
||||||
overwrite_mem_buffer_size(&cinfo);
|
overwrite_mem_buffer_size(&cinfo);
|
||||||
#endif
|
|
||||||
|
|
||||||
//jpeg_stdio_src(&cinfo, file);
|
//jpeg_stdio_src(&cinfo, file);
|
||||||
cinfo.src = &sk_stream;
|
cinfo.src = &srcManager;
|
||||||
|
|
||||||
int status = jpeg_read_header(&cinfo, true);
|
int status = jpeg_read_header(&cinfo, true);
|
||||||
if (status != JPEG_HEADER_OK) {
|
if (status != JPEG_HEADER_OK) {
|
||||||
|
@ -208,7 +268,12 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
*/
|
*/
|
||||||
int sampleSize = this->getSampleSize();
|
int sampleSize = this->getSampleSize();
|
||||||
|
|
||||||
cinfo.dct_method = JDCT_IFAST;
|
if (this->getPreferQualityOverSpeed()) {
|
||||||
|
cinfo.dct_method = JDCT_ISLOW;
|
||||||
|
} else {
|
||||||
|
cinfo.dct_method = JDCT_IFAST;
|
||||||
|
}
|
||||||
|
|
||||||
cinfo.scale_num = 1;
|
cinfo.scale_num = 1;
|
||||||
cinfo.scale_denom = sampleSize;
|
cinfo.scale_denom = sampleSize;
|
||||||
|
|
||||||
|
@ -250,7 +315,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
|
if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
|
||||||
bm->setConfig(config, cinfo.image_width, cinfo.image_height);
|
bm->setConfig(config, cinfo.image_width, cinfo.image_height);
|
||||||
bm->setIsOpaque(true);
|
bm->setIsOpaque(true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -270,8 +335,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
to complete the setup. However, output dimensions seem to get
|
to complete the setup. However, output dimensions seem to get
|
||||||
computed very early, which is why this special check can pay off.
|
computed very early, which is why this special check can pay off.
|
||||||
*/
|
*/
|
||||||
if (SkImageDecoder::kDecodeBounds_Mode == mode &&
|
if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
|
||||||
valid_output_dimensions(cinfo)) {
|
|
||||||
SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
|
SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
|
||||||
recompute_sampleSize(sampleSize, cinfo));
|
recompute_sampleSize(sampleSize, cinfo));
|
||||||
bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
|
bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
|
||||||
|
@ -284,11 +348,38 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
sampleSize = recompute_sampleSize(sampleSize, cinfo);
|
sampleSize = recompute_sampleSize(sampleSize, cinfo);
|
||||||
|
|
||||||
// should we allow the Chooser (if present) to pick a config for us???
|
// should we allow the Chooser (if present) to pick a config for us???
|
||||||
if (!this->chooseFromOneChoice(config, cinfo.output_width,
|
if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
|
||||||
cinfo.output_height)) {
|
|
||||||
return return_false(cinfo, *bm, "chooseFromOneChoice");
|
return return_false(cinfo, *bm, "chooseFromOneChoice");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
|
||||||
|
|
||||||
|
bm->lockPixels();
|
||||||
|
JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
|
||||||
|
bm->unlockPixels();
|
||||||
|
bool reuseBitmap = (rowptr != NULL);
|
||||||
|
|
||||||
|
if (reuseBitmap) {
|
||||||
|
if (sampler.scaledWidth() != bm->width() ||
|
||||||
|
sampler.scaledHeight() != bm->height()) {
|
||||||
|
// Dimensions must match
|
||||||
|
return false;
|
||||||
|
} else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
|
||||||
|
bm->setIsOpaque(true);
|
||||||
|
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this->allocPixelRef(bm, NULL)) {
|
||||||
|
return return_false(cinfo, *bm, "allocPixelRef");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkAutoLockPixels alp(*bm);
|
||||||
|
|
||||||
#ifdef ANDROID_RGB
|
#ifdef ANDROID_RGB
|
||||||
/* short-circuit the SkScaledBitmapSampler when possible, as this gives
|
/* short-circuit the SkScaledBitmapSampler when possible, as this gives
|
||||||
a significant performance boost.
|
a significant performance boost.
|
||||||
|
@ -299,16 +390,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
(config == SkBitmap::kRGB_565_Config &&
|
(config == SkBitmap::kRGB_565_Config &&
|
||||||
cinfo.out_color_space == JCS_RGB_565)))
|
cinfo.out_color_space == JCS_RGB_565)))
|
||||||
{
|
{
|
||||||
bm->setConfig(config, cinfo.output_width, cinfo.output_height);
|
rowptr = (JSAMPLE*)bm->getPixels();
|
||||||
bm->setIsOpaque(true);
|
|
||||||
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!this->allocPixelRef(bm, NULL)) {
|
|
||||||
return return_false(cinfo, *bm, "allocPixelRef");
|
|
||||||
}
|
|
||||||
SkAutoLockPixels alp(*bm);
|
|
||||||
JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
|
|
||||||
INT32 const bpr = bm->rowBytes();
|
INT32 const bpr = bm->rowBytes();
|
||||||
|
|
||||||
while (cinfo.output_scanline < cinfo.output_height) {
|
while (cinfo.output_scanline < cinfo.output_height) {
|
||||||
|
@ -323,6 +405,9 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
}
|
}
|
||||||
rowptr += bpr;
|
rowptr += bpr;
|
||||||
}
|
}
|
||||||
|
if (reuseBitmap) {
|
||||||
|
bm->notifyPixelsChanged();
|
||||||
|
}
|
||||||
jpeg_finish_decompress(&cinfo);
|
jpeg_finish_decompress(&cinfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -348,27 +433,13 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
return return_false(cinfo, *bm, "jpeg colorspace");
|
return return_false(cinfo, *bm, "jpeg colorspace");
|
||||||
}
|
}
|
||||||
|
|
||||||
SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
|
|
||||||
sampleSize);
|
|
||||||
|
|
||||||
bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
|
|
||||||
// jpegs are always opaque (i.e. have no per-pixel alpha)
|
|
||||||
bm->setIsOpaque(true);
|
|
||||||
|
|
||||||
if (SkImageDecoder::kDecodeBounds_Mode == mode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!this->allocPixelRef(bm, NULL)) {
|
|
||||||
return return_false(cinfo, *bm, "allocPixelRef");
|
|
||||||
}
|
|
||||||
|
|
||||||
SkAutoLockPixels alp(*bm);
|
|
||||||
if (!sampler.begin(bm, sc, this->getDitherImage())) {
|
if (!sampler.begin(bm, sc, this->getDitherImage())) {
|
||||||
return return_false(cinfo, *bm, "sampler.begin");
|
return return_false(cinfo, *bm, "sampler.begin");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The CMYK work-around relies on 4 components per pixel here
|
// The CMYK work-around relies on 4 components per pixel here
|
||||||
uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4);
|
SkAutoMalloc srcStorage(cinfo.output_width * 4);
|
||||||
|
uint8_t* srcRow = (uint8_t*)srcStorage.get();
|
||||||
|
|
||||||
// Possibly skip initial rows [sampler.srcY0]
|
// Possibly skip initial rows [sampler.srcY0]
|
||||||
if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
|
if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
|
||||||
|
@ -406,12 +477,279 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
||||||
cinfo.output_height - cinfo.output_scanline)) {
|
cinfo.output_height - cinfo.output_scanline)) {
|
||||||
return return_false(cinfo, *bm, "skip rows");
|
return return_false(cinfo, *bm, "skip rows");
|
||||||
}
|
}
|
||||||
|
if (reuseBitmap) {
|
||||||
|
bm->notifyPixelsChanged();
|
||||||
|
}
|
||||||
jpeg_finish_decompress(&cinfo);
|
jpeg_finish_decompress(&cinfo);
|
||||||
|
|
||||||
// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
|
||||||
|
|
||||||
|
SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
|
||||||
|
jpeg_decompress_struct* cinfo = imageIndex->cinfo();
|
||||||
|
huffman_index* huffmanIndex = imageIndex->huffmanIndex();
|
||||||
|
|
||||||
|
skjpeg_error_mgr sk_err;
|
||||||
|
cinfo->err = jpeg_std_error(&sk_err);
|
||||||
|
sk_err.error_exit = skjpeg_error_exit;
|
||||||
|
|
||||||
|
// All objects need to be instantiated before this setjmp call so that
|
||||||
|
// they will be cleaned up properly if an error occurs.
|
||||||
|
if (setjmp(sk_err.fJmpBuf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the cinfo used to create/build the huffmanIndex
|
||||||
|
imageIndex->initializeInfo();
|
||||||
|
cinfo->do_fancy_upsampling = 0;
|
||||||
|
cinfo->do_block_smoothing = 0;
|
||||||
|
|
||||||
|
int status = jpeg_read_header(cinfo, true);
|
||||||
|
if (JPEG_HEADER_OK != status) {
|
||||||
|
SkDELETE(imageIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_create_huffman_index(cinfo, huffmanIndex);
|
||||||
|
cinfo->scale_num = 1;
|
||||||
|
cinfo->scale_denom = 1;
|
||||||
|
if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
|
||||||
|
SkDELETE(imageIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy the cinfo used to create/build the huffman index
|
||||||
|
jpeg_destroy_decompress(cinfo);
|
||||||
|
|
||||||
|
// Init decoder to image decode mode
|
||||||
|
imageIndex->initializeInfo();
|
||||||
|
|
||||||
|
status = jpeg_read_header(cinfo, true);
|
||||||
|
if (JPEG_HEADER_OK != status) {
|
||||||
|
SkDELETE(imageIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cinfo->out_color_space = JCS_RGBA_8888;
|
||||||
|
cinfo->do_fancy_upsampling = 0;
|
||||||
|
cinfo->do_block_smoothing = 0;
|
||||||
|
|
||||||
|
// instead of jpeg_start_decompress() we start a tiled decompress
|
||||||
|
jpeg_start_tile_decompress(cinfo);
|
||||||
|
|
||||||
|
cinfo->scale_num = 1;
|
||||||
|
*height = cinfo->output_height;
|
||||||
|
*width = cinfo->output_width;
|
||||||
|
fImageWidth = *width;
|
||||||
|
fImageHeight = *height;
|
||||||
|
|
||||||
|
SkDELETE(fImageIndex);
|
||||||
|
fImageIndex = imageIndex;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
|
||||||
|
if (NULL == fImageIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
|
||||||
|
|
||||||
|
SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
|
||||||
|
if (!rect.intersect(region)) {
|
||||||
|
// If the requested region is entirely outside the image return false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
skjpeg_error_mgr errorManager;
|
||||||
|
cinfo->err = jpeg_std_error(&errorManager);
|
||||||
|
errorManager.error_exit = skjpeg_error_exit;
|
||||||
|
if (setjmp(errorManager.fJmpBuf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int requestedSampleSize = this->getSampleSize();
|
||||||
|
cinfo->scale_denom = requestedSampleSize;
|
||||||
|
|
||||||
|
if (this->getPreferQualityOverSpeed()) {
|
||||||
|
cinfo->dct_method = JDCT_ISLOW;
|
||||||
|
} else {
|
||||||
|
cinfo->dct_method = JDCT_IFAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
|
||||||
|
if (config != SkBitmap::kARGB_8888_Config &&
|
||||||
|
config != SkBitmap::kARGB_4444_Config &&
|
||||||
|
config != SkBitmap::kRGB_565_Config) {
|
||||||
|
config = SkBitmap::kARGB_8888_Config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default format is RGB */
|
||||||
|
cinfo->out_color_space = JCS_RGB;
|
||||||
|
|
||||||
|
#ifdef ANDROID_RGB
|
||||||
|
cinfo->dither_mode = JDITHER_NONE;
|
||||||
|
if (SkBitmap::kARGB_8888_Config == config) {
|
||||||
|
cinfo->out_color_space = JCS_RGBA_8888;
|
||||||
|
} else if (SkBitmap::kRGB_565_Config == config) {
|
||||||
|
cinfo->out_color_space = JCS_RGB_565;
|
||||||
|
if (this->getDitherImage()) {
|
||||||
|
cinfo->dither_mode = JDITHER_ORDERED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int startX = rect.fLeft;
|
||||||
|
int startY = rect.fTop;
|
||||||
|
int width = rect.width();
|
||||||
|
int height = rect.height();
|
||||||
|
|
||||||
|
jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
|
||||||
|
&startX, &startY, &width, &height);
|
||||||
|
int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
|
||||||
|
int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
|
||||||
|
|
||||||
|
SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
|
||||||
|
|
||||||
|
SkBitmap bitmap;
|
||||||
|
bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
|
||||||
|
bitmap.setIsOpaque(true);
|
||||||
|
|
||||||
|
// Check ahead of time if the swap(dest, src) is possible or not.
|
||||||
|
// If yes, then we will stick to AllocPixelRef since it's cheaper with the
|
||||||
|
// swap happening. If no, then we will use alloc to allocate pixels to
|
||||||
|
// prevent garbage collection.
|
||||||
|
int w = rect.width() / actualSampleSize;
|
||||||
|
int h = rect.height() / actualSampleSize;
|
||||||
|
bool swapOnly = (rect == region) && bm->isNull() &&
|
||||||
|
(w == bitmap.width()) && (h == bitmap.height()) &&
|
||||||
|
((startX - rect.x()) / actualSampleSize == 0) &&
|
||||||
|
((startY - rect.y()) / actualSampleSize == 0);
|
||||||
|
if (swapOnly) {
|
||||||
|
if (!this->allocPixelRef(&bitmap, NULL)) {
|
||||||
|
return return_false(*cinfo, bitmap, "allocPixelRef");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!bitmap.allocPixels()) {
|
||||||
|
return return_false(*cinfo, bitmap, "allocPixels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkAutoLockPixels alp(bitmap);
|
||||||
|
|
||||||
|
#ifdef ANDROID_RGB
|
||||||
|
/* short-circuit the SkScaledBitmapSampler when possible, as this gives
|
||||||
|
a significant performance boost.
|
||||||
|
*/
|
||||||
|
if (skiaSampleSize == 1 &&
|
||||||
|
((config == SkBitmap::kARGB_8888_Config &&
|
||||||
|
cinfo->out_color_space == JCS_RGBA_8888) ||
|
||||||
|
(config == SkBitmap::kRGB_565_Config &&
|
||||||
|
cinfo->out_color_space == JCS_RGB_565)))
|
||||||
|
{
|
||||||
|
JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
|
||||||
|
INT32 const bpr = bitmap.rowBytes();
|
||||||
|
int rowTotalCount = 0;
|
||||||
|
|
||||||
|
while (rowTotalCount < height) {
|
||||||
|
int rowCount = jpeg_read_tile_scanline(cinfo,
|
||||||
|
fImageIndex->huffmanIndex(),
|
||||||
|
&rowptr);
|
||||||
|
// if row_count == 0, then we didn't get a scanline, so abort.
|
||||||
|
// if we supported partial images, we might return true in this case
|
||||||
|
if (0 == rowCount) {
|
||||||
|
return return_false(*cinfo, bitmap, "read_scanlines");
|
||||||
|
}
|
||||||
|
if (this->shouldCancelDecode()) {
|
||||||
|
return return_false(*cinfo, bitmap, "shouldCancelDecode");
|
||||||
|
}
|
||||||
|
rowTotalCount += rowCount;
|
||||||
|
rowptr += bpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapOnly) {
|
||||||
|
bm->swap(bitmap);
|
||||||
|
} else {
|
||||||
|
cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
|
||||||
|
region.width(), region.height(), startX, startY);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// check for supported formats
|
||||||
|
SkScaledBitmapSampler::SrcConfig sc;
|
||||||
|
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) {
|
||||||
|
sc = SkScaledBitmapSampler::kRGBX;
|
||||||
|
} else if (JCS_RGB_565 == cinfo->out_color_space) {
|
||||||
|
sc = SkScaledBitmapSampler::kRGB_565;
|
||||||
|
#endif
|
||||||
|
} else if (1 == cinfo->out_color_components &&
|
||||||
|
JCS_GRAYSCALE == cinfo->out_color_space) {
|
||||||
|
sc = SkScaledBitmapSampler::kGray;
|
||||||
|
} else {
|
||||||
|
return return_false(*cinfo, *bm, "jpeg colorspace");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
|
||||||
|
return return_false(*cinfo, bitmap, "sampler.begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CMYK work-around relies on 4 components per pixel here
|
||||||
|
SkAutoMalloc srcStorage(width * 4);
|
||||||
|
uint8_t* srcRow = (uint8_t*)srcStorage.get();
|
||||||
|
|
||||||
|
// Possibly skip initial rows [sampler.srcY0]
|
||||||
|
if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
|
||||||
|
return return_false(*cinfo, bitmap, "skip rows");
|
||||||
|
}
|
||||||
|
|
||||||
|
// now loop through scanlines until y == bitmap->height() - 1
|
||||||
|
for (int y = 0;; y++) {
|
||||||
|
JSAMPLE* rowptr = (JSAMPLE*)srcRow;
|
||||||
|
int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
|
||||||
|
if (0 == row_count) {
|
||||||
|
return return_false(*cinfo, bitmap, "read_scanlines");
|
||||||
|
}
|
||||||
|
if (this->shouldCancelDecode()) {
|
||||||
|
return return_false(*cinfo, bitmap, "shouldCancelDecode");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JCS_CMYK == cinfo->out_color_space) {
|
||||||
|
convert_CMYK_to_RGB(srcRow, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
sampler.next(srcRow);
|
||||||
|
if (bitmap.height() - 1 == y) {
|
||||||
|
// we're done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
|
||||||
|
sampler.srcDY() - 1)) {
|
||||||
|
return return_false(*cinfo, bitmap, "skip rows");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (swapOnly) {
|
||||||
|
bm->swap(bitmap);
|
||||||
|
} else {
|
||||||
|
cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
|
||||||
|
region.width(), region.height(), startX, startY);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "SkColorPriv.h"
|
#include "SkColorPriv.h"
|
||||||
|
@ -582,7 +920,7 @@ class SkJPEGImageEncoder : public SkImageEncoder {
|
||||||
protected:
|
protected:
|
||||||
virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
|
virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
|
||||||
#ifdef TIME_ENCODE
|
#ifdef TIME_ENCODE
|
||||||
AutoTimeMillis atm("JPEG Encode");
|
SkAutoTime atm("JPEG Encode");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const WriteScanline writer = ChooseWriter(bm);
|
const WriteScanline writer = ChooseWriter(bm);
|
||||||
|
|
|
@ -9,14 +9,41 @@
|
||||||
|
|
||||||
#include "SkJpegUtility.h"
|
#include "SkJpegUtility.h"
|
||||||
|
|
||||||
|
// Uncomment to enable the code path used by the Android framework with their
|
||||||
|
// custom image decoders.
|
||||||
|
//#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_DEBUG)
|
||||||
|
// #define SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
//#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
static void sk_init_source(j_decompress_ptr cinfo) {
|
static void sk_init_source(j_decompress_ptr cinfo) {
|
||||||
skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
|
skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
|
||||||
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
||||||
src->bytes_in_buffer = 0;
|
src->bytes_in_buffer = 0;
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
src->current_offset = 0;
|
||||||
|
#endif
|
||||||
src->fStream->rewind();
|
src->fStream->rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
|
static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
|
||||||
skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
|
skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
|
||||||
if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
|
if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
|
||||||
|
@ -29,6 +56,9 @@ static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
src->current_offset += bytes;
|
||||||
|
#endif
|
||||||
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
||||||
src->bytes_in_buffer = bytes;
|
src->bytes_in_buffer = bytes;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -46,6 +76,9 @@ static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
|
||||||
cinfo->err->error_exit((j_common_ptr)cinfo);
|
cinfo->err->error_exit((j_common_ptr)cinfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
src->current_offset += bytes;
|
||||||
|
#endif
|
||||||
bytesToSkip -= bytes;
|
bytesToSkip -= bytes;
|
||||||
}
|
}
|
||||||
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
src->next_input_byte = (const JOCTET*)src->fBuffer;
|
||||||
|
@ -74,40 +107,11 @@ static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
|
||||||
static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
|
static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
|
||||||
|
|
||||||
|
|
||||||
#if 0 // UNUSED
|
|
||||||
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->bytes_in_buffer = src->fMemoryBaseSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
|
|
||||||
SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
|
|
||||||
skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
|
|
||||||
// SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
|
|
||||||
src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
|
|
||||||
src->bytes_in_buffer -= num_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
|
|
||||||
SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder,
|
skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder,
|
||||||
bool ownStream) : fStream(stream) {
|
bool ownStream) : fStream(stream) {
|
||||||
fDecoder = decoder;
|
fDecoder = decoder;
|
||||||
// const void* baseAddr = stream->getMemoryBase();
|
|
||||||
fMemoryBase = NULL;
|
fMemoryBase = NULL;
|
||||||
fUnrefStream = ownStream;
|
fUnrefStream = ownStream;
|
||||||
fMemoryBaseSize = 0;
|
fMemoryBaseSize = 0;
|
||||||
|
@ -117,6 +121,9 @@ skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder,
|
||||||
skip_input_data = sk_skip_input_data;
|
skip_input_data = sk_skip_input_data;
|
||||||
resync_to_restart = sk_resync_to_restart;
|
resync_to_restart = sk_resync_to_restart;
|
||||||
term_source = sk_term_source;
|
term_source = sk_term_source;
|
||||||
|
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
|
||||||
|
seek_input_data = sk_seek_input_data;
|
||||||
|
#endif
|
||||||
// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
|
// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче