am 6b849e21
: Unhide BitmapRegionDecoder.
Merge commit '6b849e2123be98eb2a1a25b8abf0b13a279ce952' into gingerbread-plus-aosp * commit '6b849e2123be98eb2a1a25b8abf0b13a279ce952': Unhide BitmapRegionDecoder.
This commit is contained in:
140
api/current.xml
140
api/current.xml
@ -63930,6 +63930,146 @@
|
|||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
</class>
|
</class>
|
||||||
|
<class name="BitmapRegionDecoder"
|
||||||
|
extends="java.lang.Object"
|
||||||
|
abstract="false"
|
||||||
|
static="false"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<method name="decodeRegion"
|
||||||
|
return="android.graphics.Bitmap"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="rect" type="android.graphics.Rect">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="options" type="android.graphics.BitmapFactory.Options">
|
||||||
|
</parameter>
|
||||||
|
</method>
|
||||||
|
<method name="getHeight"
|
||||||
|
return="int"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</method>
|
||||||
|
<method name="getWidth"
|
||||||
|
return="int"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</method>
|
||||||
|
<method name="isRecycled"
|
||||||
|
return="boolean"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</method>
|
||||||
|
<method name="newInstance"
|
||||||
|
return="android.graphics.BitmapRegionDecoder"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="true"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="data" type="byte[]">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="offset" type="int">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="length" type="int">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="isShareable" type="boolean">
|
||||||
|
</parameter>
|
||||||
|
<exception name="IOException" type="java.io.IOException">
|
||||||
|
</exception>
|
||||||
|
</method>
|
||||||
|
<method name="newInstance"
|
||||||
|
return="android.graphics.BitmapRegionDecoder"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="true"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="fd" type="java.io.FileDescriptor">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="isShareable" type="boolean">
|
||||||
|
</parameter>
|
||||||
|
<exception name="IOException" type="java.io.IOException">
|
||||||
|
</exception>
|
||||||
|
</method>
|
||||||
|
<method name="newInstance"
|
||||||
|
return="android.graphics.BitmapRegionDecoder"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="true"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="is" type="java.io.InputStream">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="isShareable" type="boolean">
|
||||||
|
</parameter>
|
||||||
|
<exception name="IOException" type="java.io.IOException">
|
||||||
|
</exception>
|
||||||
|
</method>
|
||||||
|
<method name="newInstance"
|
||||||
|
return="android.graphics.BitmapRegionDecoder"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="true"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="pathName" type="java.lang.String">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="isShareable" type="boolean">
|
||||||
|
</parameter>
|
||||||
|
<exception name="IOException" type="java.io.IOException">
|
||||||
|
</exception>
|
||||||
|
</method>
|
||||||
|
<method name="recycle"
|
||||||
|
return="void"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</method>
|
||||||
|
</class>
|
||||||
<class name="BitmapShader"
|
<class name="BitmapShader"
|
||||||
extends="android.graphics.Shader"
|
extends="android.graphics.Shader"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
|
@ -101,11 +101,12 @@ LOCAL_SRC_FILES:= \
|
|||||||
android_graphics_PixelFormat.cpp \
|
android_graphics_PixelFormat.cpp \
|
||||||
android/graphics/Picture.cpp \
|
android/graphics/Picture.cpp \
|
||||||
android/graphics/PorterDuff.cpp \
|
android/graphics/PorterDuff.cpp \
|
||||||
android/graphics/LargeBitmap.cpp \
|
android/graphics/BitmapRegionDecoder.cpp \
|
||||||
android/graphics/Rasterizer.cpp \
|
android/graphics/Rasterizer.cpp \
|
||||||
android/graphics/Region.cpp \
|
android/graphics/Region.cpp \
|
||||||
android/graphics/Shader.cpp \
|
android/graphics/Shader.cpp \
|
||||||
android/graphics/Typeface.cpp \
|
android/graphics/Typeface.cpp \
|
||||||
|
android/graphics/Utils.cpp \
|
||||||
android/graphics/Xfermode.cpp \
|
android/graphics/Xfermode.cpp \
|
||||||
android/graphics/YuvToJpegEncoder.cpp \
|
android/graphics/YuvToJpegEncoder.cpp \
|
||||||
android_media_AudioRecord.cpp \
|
android_media_AudioRecord.cpp \
|
||||||
|
@ -53,7 +53,7 @@ extern int register_android_os_Binder(JNIEnv* env);
|
|||||||
extern int register_android_os_Process(JNIEnv* env);
|
extern int register_android_os_Process(JNIEnv* env);
|
||||||
extern int register_android_graphics_Bitmap(JNIEnv*);
|
extern int register_android_graphics_Bitmap(JNIEnv*);
|
||||||
extern int register_android_graphics_BitmapFactory(JNIEnv*);
|
extern int register_android_graphics_BitmapFactory(JNIEnv*);
|
||||||
extern int register_android_graphics_LargeBitmap(JNIEnv*);
|
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
|
||||||
extern int register_android_graphics_Camera(JNIEnv* env);
|
extern int register_android_graphics_Camera(JNIEnv* env);
|
||||||
extern int register_android_graphics_Graphics(JNIEnv* env);
|
extern int register_android_graphics_Graphics(JNIEnv* env);
|
||||||
extern int register_android_graphics_Interpolator(JNIEnv* env);
|
extern int register_android_graphics_Interpolator(JNIEnv* env);
|
||||||
@ -1208,7 +1208,7 @@ static const RegJNIRec gRegJNI[] = {
|
|||||||
|
|
||||||
REG_JNI(register_android_graphics_Bitmap),
|
REG_JNI(register_android_graphics_Bitmap),
|
||||||
REG_JNI(register_android_graphics_BitmapFactory),
|
REG_JNI(register_android_graphics_BitmapFactory),
|
||||||
REG_JNI(register_android_graphics_LargeBitmap),
|
REG_JNI(register_android_graphics_BitmapRegionDecoder),
|
||||||
REG_JNI(register_android_graphics_Camera),
|
REG_JNI(register_android_graphics_Camera),
|
||||||
REG_JNI(register_android_graphics_Canvas),
|
REG_JNI(register_android_graphics_Canvas),
|
||||||
REG_JNI(register_android_graphics_ColorFilter),
|
REG_JNI(register_android_graphics_ColorFilter),
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "SkUtils.h"
|
#include "SkUtils.h"
|
||||||
#include "CreateJavaOutputStreamAdaptor.h"
|
#include "CreateJavaOutputStreamAdaptor.h"
|
||||||
#include "AutoDecodeCancel.h"
|
#include "AutoDecodeCancel.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#include <android_runtime/AndroidRuntime.h>
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
#include <utils/Asset.h>
|
#include <utils/Asset.h>
|
||||||
@ -99,57 +100,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AssetStreamAdaptor : public SkStream {
|
|
||||||
public:
|
|
||||||
AssetStreamAdaptor(Asset* a) : fAsset(a) {}
|
|
||||||
|
|
||||||
virtual bool rewind() {
|
|
||||||
off_t pos = fAsset->seek(0, SEEK_SET);
|
|
||||||
if (pos == (off_t)-1) {
|
|
||||||
SkDebugf("----- fAsset->seek(rewind) failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual size_t read(void* buffer, size_t size) {
|
|
||||||
ssize_t amount;
|
|
||||||
|
|
||||||
if (NULL == buffer) {
|
|
||||||
if (0 == size) { // caller is asking us for our total length
|
|
||||||
return fAsset->getLength();
|
|
||||||
}
|
|
||||||
// asset->seek returns new total offset
|
|
||||||
// we want to return amount that was skipped
|
|
||||||
|
|
||||||
off_t oldOffset = fAsset->seek(0, SEEK_CUR);
|
|
||||||
if (-1 == oldOffset) {
|
|
||||||
SkDebugf("---- fAsset->seek(oldOffset) failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
off_t newOffset = fAsset->seek(size, SEEK_CUR);
|
|
||||||
if (-1 == newOffset) {
|
|
||||||
SkDebugf("---- fAsset->seek(%d) failed\n", size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
amount = newOffset - oldOffset;
|
|
||||||
} else {
|
|
||||||
amount = fAsset->read(buffer, size);
|
|
||||||
if (amount <= 0) {
|
|
||||||
SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amount < 0) {
|
|
||||||
amount = 0;
|
|
||||||
}
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Asset* fAsset;
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static inline int32_t validOrNeg1(bool isValid, int32_t value) {
|
static inline int32_t validOrNeg1(bool isValid, int32_t value) {
|
||||||
@ -201,12 +151,6 @@ static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
|
|||||||
!env->GetBooleanField(options, gOptions_nativeAllocFieldID);
|
!env->GetBooleanField(options, gOptions_nativeAllocFieldID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jobject nullObjectReturn(const char msg[]) {
|
|
||||||
if (msg) {
|
|
||||||
SkDebugf("--- %s\n", msg);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
|
static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
|
||||||
int sampleSize, bool ditherImage) {
|
int sampleSize, bool ditherImage) {
|
||||||
@ -377,23 +321,6 @@ static ssize_t getFDSize(int fd) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Restore the file descriptor's offset in our destructor
|
|
||||||
*/
|
|
||||||
class AutoFDSeek {
|
|
||||||
public:
|
|
||||||
AutoFDSeek(int fd) : fFD(fd) {
|
|
||||||
fCurr = ::lseek(fd, 0, SEEK_CUR);
|
|
||||||
}
|
|
||||||
~AutoFDSeek() {
|
|
||||||
if (fCurr >= 0) {
|
|
||||||
::lseek(fFD, fCurr, SEEK_SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
int fFD;
|
|
||||||
off_t fCurr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
|
static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
|
||||||
jobject fileDescriptor,
|
jobject fileDescriptor,
|
||||||
jobject padding,
|
jobject padding,
|
||||||
@ -560,134 +487,6 @@ static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
|
|
||||||
size_t bufferSize = 4096;
|
|
||||||
size_t streamLen = 0;
|
|
||||||
size_t len;
|
|
||||||
char* data = (char*)sk_malloc_throw(bufferSize);
|
|
||||||
|
|
||||||
while ((len = stream->read(data + streamLen,
|
|
||||||
bufferSize - streamLen)) != 0) {
|
|
||||||
streamLen += len;
|
|
||||||
if (streamLen == bufferSize) {
|
|
||||||
bufferSize *= 2;
|
|
||||||
data = (char*)sk_realloc_throw(data, bufferSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = (char*)sk_realloc_throw(data, streamLen);
|
|
||||||
SkMemoryStream* streamMem = new SkMemoryStream();
|
|
||||||
streamMem->setMemoryOwned(data, streamLen);
|
|
||||||
return streamMem;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
|
|
||||||
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
|
|
||||||
int width, height;
|
|
||||||
if (NULL == decoder) {
|
|
||||||
doThrowIOE(env, "Image format not supported");
|
|
||||||
return nullObjectReturn("SkImageDecoder::Factory returned null");
|
|
||||||
}
|
|
||||||
|
|
||||||
JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
|
|
||||||
decoder->setAllocator(javaAllocator);
|
|
||||||
JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
|
|
||||||
decoder->setReporter(javaMemoryReporter);
|
|
||||||
javaAllocator->unref();
|
|
||||||
javaMemoryReporter->unref();
|
|
||||||
|
|
||||||
if (!decoder->buildTileIndex(stream, &width, &height)) {
|
|
||||||
char msg[100];
|
|
||||||
snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
|
|
||||||
decoder->getFormatName());
|
|
||||||
doThrowIOE(env, msg);
|
|
||||||
return nullObjectReturn("decoder->buildTileIndex returned false");
|
|
||||||
}
|
|
||||||
|
|
||||||
SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
|
|
||||||
|
|
||||||
return GraphicsJNI::createLargeBitmap(env, bm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
|
|
||||||
int offset, int length, jboolean isShareable) {
|
|
||||||
/* If isShareable we could decide to just wrap the java array and
|
|
||||||
share it, but that means adding a globalref to the java array object
|
|
||||||
For now we just always copy the array's data if isShareable.
|
|
||||||
*/
|
|
||||||
AutoJavaByteArray ar(env, byteArray);
|
|
||||||
SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
|
|
||||||
return doBuildTileIndex(env, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
|
|
||||||
jobject fileDescriptor, jboolean isShareable) {
|
|
||||||
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
|
|
||||||
|
|
||||||
jint descriptor = env->GetIntField(fileDescriptor,
|
|
||||||
gFileDescriptor_descriptor);
|
|
||||||
SkStream *stream = NULL;
|
|
||||||
struct stat fdStat;
|
|
||||||
int newFD;
|
|
||||||
if (fstat(descriptor, &fdStat) == -1) {
|
|
||||||
doThrowIOE(env, "broken file descriptor");
|
|
||||||
return nullObjectReturn("fstat return -1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isShareable &&
|
|
||||||
S_ISREG(fdStat.st_mode) &&
|
|
||||||
(newFD = ::dup(descriptor)) != -1) {
|
|
||||||
SkFDStream* fdStream = new SkFDStream(newFD, true);
|
|
||||||
if (!fdStream->isValid()) {
|
|
||||||
fdStream->unref();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
stream = fdStream;
|
|
||||||
} else {
|
|
||||||
SkFDStream* fdStream = new SkFDStream(descriptor, false);
|
|
||||||
if (!fdStream->isValid()) {
|
|
||||||
fdStream->unref();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
stream = buildSkMemoryStream(fdStream);
|
|
||||||
fdStream->unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore our offset when we leave, so we can be called more than once
|
|
||||||
with the same descriptor. This is only required if we didn't dup the
|
|
||||||
file descriptor, but it is OK to do it all the time.
|
|
||||||
*/
|
|
||||||
AutoFDSeek as(descriptor);
|
|
||||||
|
|
||||||
return doBuildTileIndex(env, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
|
|
||||||
jobject is, // InputStream
|
|
||||||
jbyteArray storage, // byte[]
|
|
||||||
jboolean isShareable) {
|
|
||||||
jobject largeBitmap = NULL;
|
|
||||||
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
|
|
||||||
|
|
||||||
if (stream) {
|
|
||||||
// for now we don't allow shareable with java inputstreams
|
|
||||||
SkMemoryStream *mStream = buildSkMemoryStream(stream);
|
|
||||||
largeBitmap = doBuildTileIndex(env, mStream);
|
|
||||||
stream->unref();
|
|
||||||
}
|
|
||||||
return largeBitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
|
|
||||||
jint native_asset, // Asset
|
|
||||||
jboolean isShareable) {
|
|
||||||
SkStream* stream, *assStream;
|
|
||||||
Asset* asset = reinterpret_cast<Asset*>(native_asset);
|
|
||||||
assStream = new AssetStreamAdaptor(asset);
|
|
||||||
stream = buildSkMemoryStream(assStream);
|
|
||||||
assStream->unref();
|
|
||||||
return doBuildTileIndex(env, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
static JNINativeMethod gMethods[] = {
|
||||||
@ -717,26 +516,6 @@ static JNINativeMethod gMethods[] = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
|
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
|
||||||
|
|
||||||
{ "nativeCreateLargeBitmap",
|
|
||||||
"([BIIZ)Landroid/graphics/LargeBitmap;",
|
|
||||||
(void*)nativeCreateLargeBitmapFromByteArray
|
|
||||||
},
|
|
||||||
|
|
||||||
{ "nativeCreateLargeBitmap",
|
|
||||||
"(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
|
|
||||||
(void*)nativeCreateLargeBitmapFromStream
|
|
||||||
},
|
|
||||||
|
|
||||||
{ "nativeCreateLargeBitmap",
|
|
||||||
"(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
|
|
||||||
(void*)nativeCreateLargeBitmapFromFileDescriptor
|
|
||||||
},
|
|
||||||
|
|
||||||
{ "nativeCreateLargeBitmap",
|
|
||||||
"(IZ)Landroid/graphics/LargeBitmap;",
|
|
||||||
(void*)nativeCreateLargeBitmapFromAsset
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static JNINativeMethod gOptionsMethods[] = {
|
static JNINativeMethod gOptionsMethods[] = {
|
||||||
|
305
core/jni/android/graphics/BitmapRegionDecoder.cpp
Normal file
305
core/jni/android/graphics/BitmapRegionDecoder.cpp
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "BitmapRegionDecoder"
|
||||||
|
|
||||||
|
#include "SkBitmap.h"
|
||||||
|
#include "SkImageEncoder.h"
|
||||||
|
#include "GraphicsJNI.h"
|
||||||
|
#include "SkUtils.h"
|
||||||
|
#include "SkTemplates.h"
|
||||||
|
#include "SkPixelRef.h"
|
||||||
|
#include "SkStream.h"
|
||||||
|
#include "BitmapFactory.h"
|
||||||
|
#include "AutoDecodeCancel.h"
|
||||||
|
#include "SkBitmapRegionDecoder.h"
|
||||||
|
#include "CreateJavaOutputStreamAdaptor.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include "android_util_Binder.h"
|
||||||
|
#include "android_nio_utils.h"
|
||||||
|
#include "CreateJavaOutputStreamAdaptor.h"
|
||||||
|
|
||||||
|
#include <binder/Parcel.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <utils/Asset.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static jclass gFileDescriptor_class;
|
||||||
|
static jfieldID gFileDescriptor_descriptor;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define TRACE_BITMAP(code) code
|
||||||
|
#else
|
||||||
|
#define TRACE_BITMAP(code)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
|
||||||
|
size_t bufferSize = 4096;
|
||||||
|
size_t streamLen = 0;
|
||||||
|
size_t len;
|
||||||
|
char* data = (char*)sk_malloc_throw(bufferSize);
|
||||||
|
|
||||||
|
while ((len = stream->read(data + streamLen,
|
||||||
|
bufferSize - streamLen)) != 0) {
|
||||||
|
streamLen += len;
|
||||||
|
if (streamLen == bufferSize) {
|
||||||
|
bufferSize *= 2;
|
||||||
|
data = (char*)sk_realloc_throw(data, bufferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = (char*)sk_realloc_throw(data, streamLen);
|
||||||
|
SkMemoryStream* streamMem = new SkMemoryStream();
|
||||||
|
streamMem->setMemoryOwned(data, streamLen);
|
||||||
|
return streamMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
|
||||||
|
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
|
||||||
|
int width, height;
|
||||||
|
if (NULL == decoder) {
|
||||||
|
doThrowIOE(env, "Image format not supported");
|
||||||
|
return nullObjectReturn("SkImageDecoder::Factory returned null");
|
||||||
|
}
|
||||||
|
|
||||||
|
JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
|
||||||
|
decoder->setAllocator(javaAllocator);
|
||||||
|
JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
|
||||||
|
decoder->setReporter(javaMemoryReporter);
|
||||||
|
javaAllocator->unref();
|
||||||
|
javaMemoryReporter->unref();
|
||||||
|
|
||||||
|
if (!decoder->buildTileIndex(stream, &width, &height)) {
|
||||||
|
char msg[100];
|
||||||
|
snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
|
||||||
|
decoder->getFormatName());
|
||||||
|
doThrowIOE(env, msg);
|
||||||
|
return nullObjectReturn("decoder->buildTileIndex returned false");
|
||||||
|
}
|
||||||
|
|
||||||
|
SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
|
||||||
|
|
||||||
|
return GraphicsJNI::createBitmapRegionDecoder(env, bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
|
||||||
|
int offset, int length, jboolean isShareable) {
|
||||||
|
/* If isShareable we could decide to just wrap the java array and
|
||||||
|
share it, but that means adding a globalref to the java array object
|
||||||
|
For now we just always copy the array's data if isShareable.
|
||||||
|
*/
|
||||||
|
AutoJavaByteArray ar(env, byteArray);
|
||||||
|
SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
|
||||||
|
return doBuildTileIndex(env, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
|
||||||
|
jobject fileDescriptor, jboolean isShareable) {
|
||||||
|
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
|
||||||
|
|
||||||
|
jint descriptor = env->GetIntField(fileDescriptor,
|
||||||
|
gFileDescriptor_descriptor);
|
||||||
|
SkStream *stream = NULL;
|
||||||
|
struct stat fdStat;
|
||||||
|
int newFD;
|
||||||
|
if (fstat(descriptor, &fdStat) == -1) {
|
||||||
|
doThrowIOE(env, "broken file descriptor");
|
||||||
|
return nullObjectReturn("fstat return -1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isShareable &&
|
||||||
|
S_ISREG(fdStat.st_mode) &&
|
||||||
|
(newFD = ::dup(descriptor)) != -1) {
|
||||||
|
SkFDStream* fdStream = new SkFDStream(newFD, true);
|
||||||
|
if (!fdStream->isValid()) {
|
||||||
|
fdStream->unref();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
stream = fdStream;
|
||||||
|
} else {
|
||||||
|
SkFDStream* fdStream = new SkFDStream(descriptor, false);
|
||||||
|
if (!fdStream->isValid()) {
|
||||||
|
fdStream->unref();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
stream = buildSkMemoryStream(fdStream);
|
||||||
|
fdStream->unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore our offset when we leave, so we can be called more than once
|
||||||
|
with the same descriptor. This is only required if we didn't dup the
|
||||||
|
file descriptor, but it is OK to do it all the time.
|
||||||
|
*/
|
||||||
|
AutoFDSeek as(descriptor);
|
||||||
|
|
||||||
|
return doBuildTileIndex(env, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
|
||||||
|
jobject is, // InputStream
|
||||||
|
jbyteArray storage, // byte[]
|
||||||
|
jboolean isShareable) {
|
||||||
|
jobject largeBitmap = NULL;
|
||||||
|
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
// for now we don't allow shareable with java inputstreams
|
||||||
|
SkMemoryStream *mStream = buildSkMemoryStream(stream);
|
||||||
|
largeBitmap = doBuildTileIndex(env, mStream);
|
||||||
|
stream->unref();
|
||||||
|
}
|
||||||
|
return largeBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
|
||||||
|
jint native_asset, // Asset
|
||||||
|
jboolean isShareable) {
|
||||||
|
SkStream* stream, *assStream;
|
||||||
|
Asset* asset = reinterpret_cast<Asset*>(native_asset);
|
||||||
|
assStream = new AssetStreamAdaptor(asset);
|
||||||
|
stream = buildSkMemoryStream(assStream);
|
||||||
|
assStream->unref();
|
||||||
|
return doBuildTileIndex(env, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nine patch not supported
|
||||||
|
*
|
||||||
|
* purgeable not supported
|
||||||
|
* reportSizeToVM not supported
|
||||||
|
*/
|
||||||
|
static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
|
||||||
|
int start_x, int start_y, int width, int height, jobject options) {
|
||||||
|
SkImageDecoder *decoder = brd->getDecoder();
|
||||||
|
int sampleSize = 1;
|
||||||
|
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
|
||||||
|
bool doDither = true;
|
||||||
|
|
||||||
|
if (NULL != options) {
|
||||||
|
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
|
||||||
|
// initialize these, in case we fail later on
|
||||||
|
env->SetIntField(options, gOptions_widthFieldID, -1);
|
||||||
|
env->SetIntField(options, gOptions_heightFieldID, -1);
|
||||||
|
env->SetObjectField(options, gOptions_mimeFieldID, 0);
|
||||||
|
|
||||||
|
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
|
||||||
|
prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
|
||||||
|
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder->setDitherImage(doDither);
|
||||||
|
SkBitmap* bitmap = new SkBitmap;
|
||||||
|
SkAutoTDelete<SkBitmap> adb(bitmap);
|
||||||
|
AutoDecoderCancel adc(options, decoder);
|
||||||
|
|
||||||
|
// To fix the race condition in case "requestCancelDecode"
|
||||||
|
// happens earlier than AutoDecoderCancel object is added
|
||||||
|
// to the gAutoDecoderCancelMutex linked list.
|
||||||
|
if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
|
||||||
|
return nullObjectReturn("gOptions_mCancelID");;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkIRect region;
|
||||||
|
region.fLeft = start_x;
|
||||||
|
region.fTop = start_y;
|
||||||
|
region.fRight = start_x + width;
|
||||||
|
region.fBottom = start_y + height;
|
||||||
|
|
||||||
|
if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
|
||||||
|
return nullObjectReturn("decoder->decodeRegion returned false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// update options (if any)
|
||||||
|
if (NULL != options) {
|
||||||
|
env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
|
||||||
|
env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
|
||||||
|
// TODO: set the mimeType field with the data from the codec.
|
||||||
|
// but how to reuse a set of strings, rather than allocating new one
|
||||||
|
// each time?
|
||||||
|
env->SetObjectField(options, gOptions_mimeFieldID,
|
||||||
|
getMimeTypeString(env, decoder->getFormat()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// detach bitmap from its autotdeleter, since we want to own it now
|
||||||
|
adb.detach();
|
||||||
|
|
||||||
|
SkPixelRef* pr;
|
||||||
|
pr = bitmap->pixelRef();
|
||||||
|
// promise we will never change our pixels (great for sharing and pictures)
|
||||||
|
pr->setImmutable();
|
||||||
|
// now create the java bitmap
|
||||||
|
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
|
||||||
|
return brd->getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
|
||||||
|
return brd->getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
|
||||||
|
delete brd;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
|
||||||
|
static JNINativeMethod gBitmapRegionDecoderMethods[] = {
|
||||||
|
{ "nativeDecodeRegion",
|
||||||
|
"(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
|
||||||
|
(void*)nativeDecodeRegion},
|
||||||
|
|
||||||
|
{ "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
|
||||||
|
|
||||||
|
{ "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
|
||||||
|
|
||||||
|
{ "nativeClean", "(I)V", (void*)nativeClean},
|
||||||
|
|
||||||
|
{ "nativeNewInstance",
|
||||||
|
"([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
|
||||||
|
(void*)nativeNewInstanceFromByteArray
|
||||||
|
},
|
||||||
|
|
||||||
|
{ "nativeNewInstance",
|
||||||
|
"(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
|
||||||
|
(void*)nativeNewInstanceFromStream
|
||||||
|
},
|
||||||
|
|
||||||
|
{ "nativeNewInstance",
|
||||||
|
"(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
|
||||||
|
(void*)nativeNewInstanceFromFileDescriptor
|
||||||
|
},
|
||||||
|
|
||||||
|
{ "nativeNewInstance",
|
||||||
|
"(IZ)Landroid/graphics/BitmapRegionDecoder;",
|
||||||
|
(void*)nativeNewInstanceFromAsset
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define kClassPathName "android/graphics/BitmapRegionDecoder"
|
||||||
|
|
||||||
|
int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
|
||||||
|
int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
|
||||||
|
{
|
||||||
|
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
|
||||||
|
gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
|
||||||
|
}
|
@ -168,8 +168,8 @@ static jmethodID gBitmap_allocBufferMethodID;
|
|||||||
static jclass gBitmapConfig_class;
|
static jclass gBitmapConfig_class;
|
||||||
static jfieldID gBitmapConfig_nativeInstanceID;
|
static jfieldID gBitmapConfig_nativeInstanceID;
|
||||||
|
|
||||||
static jclass gLargeBitmap_class;
|
static jclass gBitmapRegionDecoder_class;
|
||||||
static jmethodID gLargeBitmap_constructorMethodID;
|
static jmethodID gBitmapRegionDecoder_constructorMethodID;
|
||||||
|
|
||||||
static jclass gCanvas_class;
|
static jclass gCanvas_class;
|
||||||
static jfieldID gCanvas_nativeInstanceID;
|
static jfieldID gCanvas_nativeInstanceID;
|
||||||
@ -376,17 +376,18 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
|
|
||||||
|
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
|
||||||
{
|
{
|
||||||
SkASSERT(bitmap != NULL);
|
SkASSERT(bitmap != NULL);
|
||||||
|
|
||||||
jobject obj = env->AllocObject(gLargeBitmap_class);
|
jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
|
||||||
if (hasException(env)) {
|
if (hasException(env)) {
|
||||||
obj = NULL;
|
obj = NULL;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (obj) {
|
if (obj) {
|
||||||
env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
|
env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
|
||||||
if (hasException(env)) {
|
if (hasException(env)) {
|
||||||
obj = NULL;
|
obj = NULL;
|
||||||
}
|
}
|
||||||
@ -612,8 +613,8 @@ int register_android_graphics_Graphics(JNIEnv* env)
|
|||||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
|
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
|
||||||
"(IZ[BI)V");
|
"(IZ[BI)V");
|
||||||
|
|
||||||
gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
|
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
|
||||||
gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
|
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
|
||||||
|
|
||||||
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
|
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
|
||||||
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
|
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
|
||||||
@ -651,4 +652,3 @@ int register_android_graphics_Graphics(JNIEnv* env)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "SkPoint.h"
|
#include "SkPoint.h"
|
||||||
#include "SkRect.h"
|
#include "SkRect.h"
|
||||||
#include "SkBitmap.h"
|
#include "SkBitmap.h"
|
||||||
#include "../images/SkLargeBitmap.h"
|
#include "../images/SkBitmapRegionDecoder.h"
|
||||||
#include "../images/SkImageDecoder.h"
|
#include "../images/SkImageDecoder.h"
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ public:
|
|||||||
|
|
||||||
static jobject createRegion(JNIEnv* env, SkRegion* region);
|
static jobject createRegion(JNIEnv* env, SkRegion* region);
|
||||||
|
|
||||||
static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
|
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
|
||||||
|
|
||||||
/** Set a pixelref for the bitmap (needs setConfig to already be called)
|
/** Set a pixelref for the bitmap (needs setConfig to already be called)
|
||||||
Returns true on success. If it returns false, then it failed, and the
|
Returns true on success. If it returns false, then it failed, and the
|
||||||
@ -181,4 +181,3 @@ void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
|
|||||||
do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0)
|
do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
#define LOG_TAG "LargeBitmap"
|
|
||||||
|
|
||||||
#include "SkBitmap.h"
|
|
||||||
#include "SkImageEncoder.h"
|
|
||||||
#include "SkColorPriv.h"
|
|
||||||
#include "GraphicsJNI.h"
|
|
||||||
#include "SkDither.h"
|
|
||||||
#include "SkUnPreMultiply.h"
|
|
||||||
#include "SkUtils.h"
|
|
||||||
#include "SkTemplates.h"
|
|
||||||
#include "SkPixelRef.h"
|
|
||||||
#include "BitmapFactory.h"
|
|
||||||
#include "AutoDecodeCancel.h"
|
|
||||||
#include "SkLargeBitmap.h"
|
|
||||||
|
|
||||||
#include <binder/Parcel.h>
|
|
||||||
#include "android_util_Binder.h"
|
|
||||||
#include "android_nio_utils.h"
|
|
||||||
#include "CreateJavaOutputStreamAdaptor.h"
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#define TRACE_BITMAP(code) code
|
|
||||||
#else
|
|
||||||
#define TRACE_BITMAP(code)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static jobject nullObjectReturn(const char msg[]) {
|
|
||||||
if (msg) {
|
|
||||||
SkDebugf("--- %s\n", msg);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* nine patch not supported
|
|
||||||
*
|
|
||||||
* purgeable not supported
|
|
||||||
* reportSizeToVM not supported
|
|
||||||
*/
|
|
||||||
static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
|
|
||||||
int start_x, int start_y, int width, int height, jobject options) {
|
|
||||||
SkImageDecoder *decoder = bm->getDecoder();
|
|
||||||
int sampleSize = 1;
|
|
||||||
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
|
|
||||||
bool doDither = true;
|
|
||||||
|
|
||||||
if (NULL != options) {
|
|
||||||
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
|
|
||||||
// initialize these, in case we fail later on
|
|
||||||
env->SetIntField(options, gOptions_widthFieldID, -1);
|
|
||||||
env->SetIntField(options, gOptions_heightFieldID, -1);
|
|
||||||
env->SetObjectField(options, gOptions_mimeFieldID, 0);
|
|
||||||
|
|
||||||
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
|
|
||||||
prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
|
|
||||||
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder->setDitherImage(doDither);
|
|
||||||
SkBitmap* bitmap = new SkBitmap;
|
|
||||||
SkAutoTDelete<SkBitmap> adb(bitmap);
|
|
||||||
AutoDecoderCancel adc(options, decoder);
|
|
||||||
|
|
||||||
// To fix the race condition in case "requestCancelDecode"
|
|
||||||
// happens earlier than AutoDecoderCancel object is added
|
|
||||||
// to the gAutoDecoderCancelMutex linked list.
|
|
||||||
if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
|
|
||||||
return nullObjectReturn("gOptions_mCancelID");;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkIRect region;
|
|
||||||
region.fLeft = start_x;
|
|
||||||
region.fTop = start_y;
|
|
||||||
region.fRight = start_x + width;
|
|
||||||
region.fBottom = start_y + height;
|
|
||||||
|
|
||||||
if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
|
|
||||||
return nullObjectReturn("decoder->decodeRegion returned false");
|
|
||||||
}
|
|
||||||
|
|
||||||
// update options (if any)
|
|
||||||
if (NULL != options) {
|
|
||||||
env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
|
|
||||||
env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
|
|
||||||
// TODO: set the mimeType field with the data from the codec.
|
|
||||||
// but how to reuse a set of strings, rather than allocating new one
|
|
||||||
// each time?
|
|
||||||
env->SetObjectField(options, gOptions_mimeFieldID,
|
|
||||||
getMimeTypeString(env, decoder->getFormat()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// detach bitmap from its autotdeleter, since we want to own it now
|
|
||||||
adb.detach();
|
|
||||||
|
|
||||||
SkPixelRef* pr;
|
|
||||||
pr = bitmap->pixelRef();
|
|
||||||
// promise we will never change our pixels (great for sharing and pictures)
|
|
||||||
pr->setImmutable();
|
|
||||||
// now create the java bitmap
|
|
||||||
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
|
||||||
return bm->getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
|
||||||
return bm->getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
|
||||||
delete bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <android_runtime/AndroidRuntime.h>
|
|
||||||
|
|
||||||
static JNINativeMethod gLargeBitmapMethods[] = {
|
|
||||||
{ "nativeDecodeRegion",
|
|
||||||
"(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
|
|
||||||
(void*)nativeDecodeRegion},
|
|
||||||
{ "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
|
|
||||||
{ "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
|
|
||||||
{ "nativeClean", "(I)V", (void*)nativeClean},
|
|
||||||
};
|
|
||||||
|
|
||||||
#define kClassPathName "android/graphics/LargeBitmap"
|
|
||||||
|
|
||||||
int register_android_graphics_LargeBitmap(JNIEnv* env);
|
|
||||||
int register_android_graphics_LargeBitmap(JNIEnv* env)
|
|
||||||
{
|
|
||||||
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
|
|
||||||
gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
|
|
||||||
}
|
|
||||||
|
|
70
core/jni/android/graphics/Utils.cpp
Normal file
70
core/jni/android/graphics/Utils.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "SkUtils.h"
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
bool AssetStreamAdaptor::rewind() {
|
||||||
|
off_t pos = fAsset->seek(0, SEEK_SET);
|
||||||
|
if (pos == (off_t)-1) {
|
||||||
|
SkDebugf("----- fAsset->seek(rewind) failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
|
||||||
|
ssize_t amount;
|
||||||
|
|
||||||
|
if (NULL == buffer) {
|
||||||
|
if (0 == size) { // caller is asking us for our total length
|
||||||
|
return fAsset->getLength();
|
||||||
|
}
|
||||||
|
// asset->seek returns new total offset
|
||||||
|
// we want to return amount that was skipped
|
||||||
|
|
||||||
|
off_t oldOffset = fAsset->seek(0, SEEK_CUR);
|
||||||
|
if (-1 == oldOffset) {
|
||||||
|
SkDebugf("---- fAsset->seek(oldOffset) failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
off_t newOffset = fAsset->seek(size, SEEK_CUR);
|
||||||
|
if (-1 == newOffset) {
|
||||||
|
SkDebugf("---- fAsset->seek(%d) failed\n", size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
amount = newOffset - oldOffset;
|
||||||
|
} else {
|
||||||
|
amount = fAsset->read(buffer, size);
|
||||||
|
if (amount <= 0) {
|
||||||
|
SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount < 0) {
|
||||||
|
amount = 0;
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject android::nullObjectReturn(const char msg[]) {
|
||||||
|
if (msg) {
|
||||||
|
SkDebugf("--- %s\n", msg);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
61
core/jni/android/graphics/Utils.h
Normal file
61
core/jni/android/graphics/Utils.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2006 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILS_DEFINED
|
||||||
|
#define UTILS_DEFINED
|
||||||
|
|
||||||
|
#include "SkStream.h"
|
||||||
|
|
||||||
|
#include "android_util_Binder.h"
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <utils/Asset.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class AssetStreamAdaptor : public SkStream {
|
||||||
|
public:
|
||||||
|
AssetStreamAdaptor(Asset* a) : fAsset(a) {}
|
||||||
|
virtual bool rewind();
|
||||||
|
virtual size_t read(void* buffer, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Asset* fAsset;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Restore the file descriptor's offset in our destructor
|
||||||
|
*/
|
||||||
|
class AutoFDSeek {
|
||||||
|
public:
|
||||||
|
AutoFDSeek(int fd) : fFD(fd) {
|
||||||
|
fCurr = ::lseek(fd, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
~AutoFDSeek() {
|
||||||
|
if (fCurr >= 0) {
|
||||||
|
::lseek(fFD, fCurr, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
int fFD;
|
||||||
|
off_t fCurr;
|
||||||
|
};
|
||||||
|
|
||||||
|
jobject nullObjectReturn(const char msg[]);
|
||||||
|
|
||||||
|
}; // namespace android
|
||||||
|
|
||||||
|
#endif
|
@ -581,132 +581,6 @@ public class BitmapFactory {
|
|||||||
nativeSetDefaultConfig(config.nativeInt);
|
nativeSetDefaultConfig(config.nativeInt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a LargeBitmap from the specified byte array.
|
|
||||||
* Currently only the Jpeg format is supported.
|
|
||||||
*
|
|
||||||
* @param data byte array of compressed image data.
|
|
||||||
* @param offset offset into data for where the decoder should begin
|
|
||||||
* parsing.
|
|
||||||
* @param length the number of bytes, beginning at offset, to parse
|
|
||||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
|
||||||
* shallow reference to the input. If this is false,
|
|
||||||
* then the LargeBitmap will explicitly make a copy of the
|
|
||||||
* input data, and keep that. Even if sharing is allowed,
|
|
||||||
* the implementation may still decide to make a deep
|
|
||||||
* copy of the input data. If an image is progressively encoded,
|
|
||||||
* allowing sharing may degrade the decoding speed.
|
|
||||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
|
||||||
* @throws IOException if the image format is not supported or can not be decoded.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static LargeBitmap createLargeBitmap(byte[] data,
|
|
||||||
int offset, int length, boolean isShareable) throws IOException {
|
|
||||||
if ((offset | length) < 0 || data.length < offset + length) {
|
|
||||||
throw new ArrayIndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
return nativeCreateLargeBitmap(data, offset, length, isShareable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a LargeBitmap from the file descriptor.
|
|
||||||
* The position within the descriptor will not be changed when
|
|
||||||
* this returns, so the descriptor can be used again as is.
|
|
||||||
* Currently only the Jpeg format is supported.
|
|
||||||
*
|
|
||||||
* @param fd The file descriptor containing the data to decode
|
|
||||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
|
||||||
* shallow reference to the input. If this is false,
|
|
||||||
* then the LargeBitmap will explicitly make a copy of the
|
|
||||||
* input data, and keep that. Even if sharing is allowed,
|
|
||||||
* the implementation may still decide to make a deep
|
|
||||||
* copy of the input data. If an image is progressively encoded,
|
|
||||||
* allowing sharing may degrade the decoding speed.
|
|
||||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
|
||||||
* @throws IOException if the image format is not supported or can not be decoded.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static LargeBitmap createLargeBitmap(
|
|
||||||
FileDescriptor fd, boolean isShareable) throws IOException {
|
|
||||||
return nativeCreateLargeBitmap(fd, isShareable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a LargeBitmap from an input stream.
|
|
||||||
* The stream's position will be where ever it was after the encoded data
|
|
||||||
* was read.
|
|
||||||
* Currently only the Jpeg format is supported.
|
|
||||||
*
|
|
||||||
* @param is The input stream that holds the raw data to be decoded into a
|
|
||||||
* LargeBitmap.
|
|
||||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
|
||||||
* shallow reference to the input. If this is false,
|
|
||||||
* then the LargeBitmap will explicitly make a copy of the
|
|
||||||
* input data, and keep that. Even if sharing is allowed,
|
|
||||||
* the implementation may still decide to make a deep
|
|
||||||
* copy of the input data. If an image is progressively encoded,
|
|
||||||
* allowing sharing may degrade the decoding speed.
|
|
||||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
|
||||||
* @throws IOException if the image format is not supported or can not be decoded.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static LargeBitmap createLargeBitmap(InputStream is,
|
|
||||||
boolean isShareable) throws IOException {
|
|
||||||
// we need mark/reset to work properly in JNI
|
|
||||||
|
|
||||||
if (!is.markSupported()) {
|
|
||||||
is = new BufferedInputStream(is, 16 * 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is instanceof AssetManager.AssetInputStream) {
|
|
||||||
return nativeCreateLargeBitmap(
|
|
||||||
((AssetManager.AssetInputStream) is).getAssetInt(),
|
|
||||||
isShareable);
|
|
||||||
} else {
|
|
||||||
// pass some temp storage down to the native code. 1024 is made up,
|
|
||||||
// but should be large enough to avoid too many small calls back
|
|
||||||
// into is.read(...).
|
|
||||||
byte [] tempStorage = new byte[16 * 1024];
|
|
||||||
return nativeCreateLargeBitmap(is, tempStorage, isShareable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a LargeBitmap from a file path.
|
|
||||||
* Currently only the Jpeg format is supported.
|
|
||||||
*
|
|
||||||
* @param pathName complete path name for the file to be decoded.
|
|
||||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
|
||||||
* shallow reference to the input. If this is false,
|
|
||||||
* then the LargeBitmap will explicitly make a copy of the
|
|
||||||
* input data, and keep that. Even if sharing is allowed,
|
|
||||||
* the implementation may still decide to make a deep
|
|
||||||
* copy of the input data. If an image is progressively encoded,
|
|
||||||
* allowing sharing may degrade the decoding speed.
|
|
||||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
|
||||||
* @throws IOException if the image format is not supported or can not be decoded.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static LargeBitmap createLargeBitmap(String pathName, boolean isShareable)
|
|
||||||
throws IOException {
|
|
||||||
LargeBitmap bm = null;
|
|
||||||
InputStream stream = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
stream = new FileInputStream(pathName);
|
|
||||||
bm = createLargeBitmap(stream, isShareable);
|
|
||||||
} finally {
|
|
||||||
if (stream != null) {
|
|
||||||
try {
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// do nothing here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native void nativeSetDefaultConfig(int nativeConfig);
|
private static native void nativeSetDefaultConfig(int nativeConfig);
|
||||||
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
|
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
|
||||||
Rect padding, Options opts);
|
Rect padding, Options opts);
|
||||||
@ -716,14 +590,4 @@ public class BitmapFactory {
|
|||||||
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
|
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
|
||||||
int length, Options opts);
|
int length, Options opts);
|
||||||
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
|
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
|
||||||
|
|
||||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
|
||||||
byte[] data, int offset, int length, boolean isShareable);
|
|
||||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
|
||||||
FileDescriptor fd, boolean isShareable);
|
|
||||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
|
||||||
InputStream is, byte[] storage, boolean isShareable);
|
|
||||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
|
||||||
int asset, boolean isShareable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
263
graphics/java/android/graphics/BitmapRegionDecoder.java
Normal file
263
graphics/java/android/graphics/BitmapRegionDecoder.java
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.graphics;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BitmapRegionDecoder can be used to decode a rectangle region from an image.
|
||||||
|
* BitmapRegionDecoder is particularly useful when an original image is large and
|
||||||
|
* you only need parts of the image.
|
||||||
|
*
|
||||||
|
* <p>To create a BitmapRegionDecoder, call newInstance(...).
|
||||||
|
* Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
|
||||||
|
* to get a decoded Bitmap of the specified region.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class BitmapRegionDecoder {
|
||||||
|
private int mNativeBitmapRegionDecoder;
|
||||||
|
private boolean mRecycled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BitmapRegionDecoder from the specified byte array.
|
||||||
|
* Currently only the Jpeg format is supported.
|
||||||
|
*
|
||||||
|
* @param data byte array of compressed image data.
|
||||||
|
* @param offset offset into data for where the decoder should begin
|
||||||
|
* parsing.
|
||||||
|
* @param length the number of bytes, beginning at offset, to parse
|
||||||
|
* @param isShareable If this is true, then the BitmapRegionDecoder may keep a
|
||||||
|
* shallow reference to the input. If this is false,
|
||||||
|
* then the BitmapRegionDecoder will explicitly make a copy of the
|
||||||
|
* input data, and keep that. Even if sharing is allowed,
|
||||||
|
* the implementation may still decide to make a deep
|
||||||
|
* copy of the input data. If an image is progressively encoded,
|
||||||
|
* allowing sharing may degrade the decoding speed.
|
||||||
|
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
|
||||||
|
* @throws IOException if the image format is not supported or can not be decoded.
|
||||||
|
*/
|
||||||
|
public static BitmapRegionDecoder newInstance(byte[] data,
|
||||||
|
int offset, int length, boolean isShareable) throws IOException {
|
||||||
|
if ((offset | length) < 0 || data.length < offset + length) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
return nativeNewInstance(data, offset, length, isShareable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BitmapRegionDecoder from the file descriptor.
|
||||||
|
* The position within the descriptor will not be changed when
|
||||||
|
* this returns, so the descriptor can be used again as is.
|
||||||
|
* Currently only the Jpeg format is supported.
|
||||||
|
*
|
||||||
|
* @param fd The file descriptor containing the data to decode
|
||||||
|
* @param isShareable If this is true, then the BitmapRegionDecoder may keep a
|
||||||
|
* shallow reference to the input. If this is false,
|
||||||
|
* then the BitmapRegionDecoder will explicitly make a copy of the
|
||||||
|
* input data, and keep that. Even if sharing is allowed,
|
||||||
|
* the implementation may still decide to make a deep
|
||||||
|
* copy of the input data. If an image is progressively encoded,
|
||||||
|
* allowing sharing may degrade the decoding speed.
|
||||||
|
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
|
||||||
|
* @throws IOException if the image format is not supported or can not be decoded.
|
||||||
|
*/
|
||||||
|
public static BitmapRegionDecoder newInstance(
|
||||||
|
FileDescriptor fd, boolean isShareable) throws IOException {
|
||||||
|
return nativeNewInstance(fd, isShareable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BitmapRegionDecoder from an input stream.
|
||||||
|
* The stream's position will be where ever it was after the encoded data
|
||||||
|
* was read.
|
||||||
|
* Currently only the Jpeg format is supported.
|
||||||
|
*
|
||||||
|
* @param is The input stream that holds the raw data to be decoded into a
|
||||||
|
* BitmapRegionDecoder.
|
||||||
|
* @param isShareable If this is true, then the BitmapRegionDecoder may keep a
|
||||||
|
* shallow reference to the input. If this is false,
|
||||||
|
* then the BitmapRegionDecoder will explicitly make a copy of the
|
||||||
|
* input data, and keep that. Even if sharing is allowed,
|
||||||
|
* the implementation may still decide to make a deep
|
||||||
|
* copy of the input data. If an image is progressively encoded,
|
||||||
|
* allowing sharing may degrade the decoding speed.
|
||||||
|
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
|
||||||
|
* @throws IOException if the image format is not supported or can not be decoded.
|
||||||
|
*/
|
||||||
|
public static BitmapRegionDecoder newInstance(InputStream is,
|
||||||
|
boolean isShareable) throws IOException {
|
||||||
|
// we need mark/reset to work properly in JNI
|
||||||
|
|
||||||
|
if (!is.markSupported()) {
|
||||||
|
is = new BufferedInputStream(is, 16 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is instanceof AssetManager.AssetInputStream) {
|
||||||
|
return nativeNewInstance(
|
||||||
|
((AssetManager.AssetInputStream) is).getAssetInt(),
|
||||||
|
isShareable);
|
||||||
|
} else {
|
||||||
|
// pass some temp storage down to the native code. 1024 is made up,
|
||||||
|
// but should be large enough to avoid too many small calls back
|
||||||
|
// into is.read(...).
|
||||||
|
byte [] tempStorage = new byte[16 * 1024];
|
||||||
|
return nativeNewInstance(is, tempStorage, isShareable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BitmapRegionDecoder from a file path.
|
||||||
|
* Currently only the Jpeg format is supported.
|
||||||
|
*
|
||||||
|
* @param pathName complete path name for the file to be decoded.
|
||||||
|
* @param isShareable If this is true, then the BitmapRegionDecoder may keep a
|
||||||
|
* shallow reference to the input. If this is false,
|
||||||
|
* then the BitmapRegionDecoder will explicitly make a copy of the
|
||||||
|
* input data, and keep that. Even if sharing is allowed,
|
||||||
|
* the implementation may still decide to make a deep
|
||||||
|
* copy of the input data. If an image is progressively encoded,
|
||||||
|
* allowing sharing may degrade the decoding speed.
|
||||||
|
* @return BitmapRegionDecoder, or null if the image data could not be decoded.
|
||||||
|
* @throws IOException if the image format is not supported or can not be decoded.
|
||||||
|
*/
|
||||||
|
public static BitmapRegionDecoder newInstance(String pathName,
|
||||||
|
boolean isShareable) throws IOException {
|
||||||
|
BitmapRegionDecoder decoder = null;
|
||||||
|
InputStream stream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = new FileInputStream(pathName);
|
||||||
|
decoder = newInstance(stream, isShareable);
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// do nothing here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private constructor that must receive an already allocated native
|
||||||
|
region decoder int (pointer).
|
||||||
|
|
||||||
|
This can be called from JNI code.
|
||||||
|
*/
|
||||||
|
private BitmapRegionDecoder(int decoder) {
|
||||||
|
mNativeBitmapRegionDecoder = decoder;
|
||||||
|
mRecycled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a rectangle region in the image specified by rect.
|
||||||
|
*
|
||||||
|
* @param rect The rectangle that specified the region to be decode.
|
||||||
|
* @param options null-ok; Options that control downsampling.
|
||||||
|
* inPurgeable is not supported.
|
||||||
|
* @return The decoded bitmap, or null if the image data could not be
|
||||||
|
* decoded.
|
||||||
|
*/
|
||||||
|
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
|
||||||
|
checkRecycled("decodeRegion called on recycled region decoder");
|
||||||
|
if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
|
||||||
|
|| rect.bottom > getHeight())
|
||||||
|
throw new IllegalArgumentException("rectangle is not inside the image");
|
||||||
|
return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
|
||||||
|
rect.right - rect.left, rect.bottom - rect.top, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the original image's width */
|
||||||
|
public int getWidth() {
|
||||||
|
checkRecycled("getWidth called on recycled region decoder");
|
||||||
|
return nativeGetWidth(mNativeBitmapRegionDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the original image's height */
|
||||||
|
public int getHeight() {
|
||||||
|
checkRecycled("getHeight called on recycled region decoder");
|
||||||
|
return nativeGetHeight(mNativeBitmapRegionDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees up the memory associated with this region decoder, and mark the
|
||||||
|
* region decoder as "dead", meaning it will throw an exception if decodeRegion(),
|
||||||
|
* getWidth() or getHeight() is called.
|
||||||
|
*
|
||||||
|
* <p>This operation cannot be reversed, so it should only be called if you are
|
||||||
|
* sure there are no further uses for the region decoder. This is an advanced call,
|
||||||
|
* and normally need not be called, since the normal GC process will free up this
|
||||||
|
* memory when there are no more references to this region decoder.
|
||||||
|
*/
|
||||||
|
public void recycle() {
|
||||||
|
if (!mRecycled) {
|
||||||
|
nativeClean(mNativeBitmapRegionDecoder);
|
||||||
|
mRecycled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this region decoder has been recycled.
|
||||||
|
* If so, then it is an error to try use its method.
|
||||||
|
*
|
||||||
|
* @return true if the region decoder has been recycled
|
||||||
|
*/
|
||||||
|
public final boolean isRecycled() {
|
||||||
|
return mRecycled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by methods that want to throw an exception if the region decoder
|
||||||
|
* has already been recycled.
|
||||||
|
*/
|
||||||
|
private void checkRecycled(String errorMessage) {
|
||||||
|
if (mRecycled) {
|
||||||
|
throw new IllegalStateException(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
recycle();
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native Bitmap nativeDecodeRegion(int lbm,
|
||||||
|
int start_x, int start_y, int width, int height,
|
||||||
|
BitmapFactory.Options options);
|
||||||
|
private static native int nativeGetWidth(int lbm);
|
||||||
|
private static native int nativeGetHeight(int lbm);
|
||||||
|
private static native void nativeClean(int lbm);
|
||||||
|
|
||||||
|
private static native BitmapRegionDecoder nativeNewInstance(
|
||||||
|
byte[] data, int offset, int length, boolean isShareable);
|
||||||
|
private static native BitmapRegionDecoder nativeNewInstance(
|
||||||
|
FileDescriptor fd, boolean isShareable);
|
||||||
|
private static native BitmapRegionDecoder nativeNewInstance(
|
||||||
|
InputStream is, byte[] storage, boolean isShareable);
|
||||||
|
private static native BitmapRegionDecoder nativeNewInstance(
|
||||||
|
int asset, boolean isShareable);
|
||||||
|
}
|
Reference in New Issue
Block a user