Merge changes Ie2ec98d9,I81ce2bef

* changes:
  Make ImageDecoder return animated Drawables
  Ensure that PostProcess Canvas is released
This commit is contained in:
TreeHugger Robot
2018-01-18 15:34:28 +00:00
committed by Android (Google) Code Review
12 changed files with 503 additions and 88 deletions

View File

@@ -119,6 +119,7 @@ cc_library_shared {
"android_util_jar_StrictJarFile.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_Picture.cpp",
"android/graphics/AnimatedImageDrawable.cpp",
"android/graphics/Bitmap.cpp",
"android/graphics/BitmapFactory.cpp",
"android/graphics/ByteBufferStreamAdaptor.cpp",

View File

@@ -63,6 +63,7 @@ extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
@@ -1396,6 +1397,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_FontFamily),
REG_JNI(register_android_graphics_GraphicBuffer),
REG_JNI(register_android_graphics_ImageDecoder),
REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
REG_JNI(register_android_graphics_Interpolator),
REG_JNI(register_android_graphics_MaskFilter),
REG_JNI(register_android_graphics_Matrix),

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2018 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 "GraphicsJNI.h"
#include "ImageDecoder.h"
#include "core_jni_helpers.h"
#include <hwui/Canvas.h>
#include <SkAndroidCodec.h>
#include <SkAnimatedImage.h>
#include <SkColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
using namespace android;
struct AnimatedImageDrawable {
sk_sp<SkAnimatedImage> mDrawable;
SkPaint mPaint;
};
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
jlong nativeImageDecoder, jobject jpostProcess,
jint width, jint height, jobject jsubset) {
if (nativeImageDecoder == 0) {
doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
return 0;
}
auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
auto info = imageDecoder->mCodec->getInfo();
const SkISize scaledSize = SkISize::Make(width, height);
SkIRect subset;
if (jsubset) {
GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
} else {
subset = SkIRect::MakeWH(width, height);
}
sk_sp<SkPicture> picture;
if (jpostProcess) {
SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
SkPictureRecorder recorder;
SkCanvas* skcanvas = recorder.beginRecording(bounds);
std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(),
bounds.height());
if (env->ExceptionCheck()) {
return 0;
}
picture = recorder.finishRecordingAsPicture();
}
std::unique_ptr<AnimatedImageDrawable> drawable(new AnimatedImageDrawable);
drawable->mDrawable = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
scaledSize, subset, std::move(picture));
if (!drawable->mDrawable) {
doThrowIOE(env, "Failed to create drawable");
return 0;
}
drawable->mDrawable->start();
return reinterpret_cast<jlong>(drawable.release());
}
static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
delete drawable;
}
static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
}
static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jlong canvasPtr, jlong msecs) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
double timeToNextUpdate = drawable->mDrawable->update(msecs);
auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
canvas->drawAnimatedImage(drawable->mDrawable.get(), 0, 0, &drawable->mPaint);
return (jlong) timeToNextUpdate;
}
static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jint alpha) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
drawable->mPaint.setAlpha(alpha);
}
static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
return drawable->mPaint.getAlpha();
}
static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jlong nativeFilter) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
drawable->mPaint.setColorFilter(sk_ref_sp(filter));
}
static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
return drawable->mDrawable->isRunning();
}
static void AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
drawable->mDrawable->start();
}
static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
drawable->mDrawable->stop();
}
static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
// FIXME: Report the size of the internal SkBitmap etc.
return sizeof(drawable);
}
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{ "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate },
{ "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
{ "nDraw", "(JJJ)J", (void*) AnimatedImageDrawable_nDraw },
{ "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha },
{ "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha },
{ "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)V", (void*) AnimatedImageDrawable_nStart },
{ "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
}

View File

@@ -19,12 +19,11 @@
#include "ByteBufferStreamAdaptor.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include "GraphicsJNI.h"
#include "NinePatchPeeker.h"
#include "ImageDecoder.h"
#include "Utils.h"
#include "core_jni_helpers.h"
#include <hwui/Bitmap.h>
#include <hwui/Canvas.h>
#include <SkAndroidCodec.h>
#include <SkEncodedImageFormat.h>
@@ -43,34 +42,14 @@ static jclass gIncomplete_class;
static jclass gCorrupt_class;
static jclass gCanvas_class;
static jmethodID gImageDecoder_constructorMethodID;
static jmethodID gImageDecoder_postProcessMethodID;
static jmethodID gPoint_constructorMethodID;
static jmethodID gIncomplete_constructorMethodID;
static jmethodID gCorrupt_constructorMethodID;
static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gPostProcess_postProcessMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;
struct ImageDecoder {
// These need to stay in sync with ImageDecoder.java's Allocator constants.
enum Allocator {
kDefault_Allocator = 0,
kSoftware_Allocator = 1,
kSharedMemory_Allocator = 2,
kHardware_Allocator = 3,
};
// These need to stay in sync with PixelFormat.java's Format constants.
enum PixelFormat {
kUnknown = 0,
kTranslucent = -3,
kOpaque = -1,
};
NinePatchPeeker mPeeker;
std::unique_ptr<SkAndroidCodec> mCodec;
};
static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
if (!stream.get()) {
doThrowIOE(env, "Failed to create a stream");
@@ -78,7 +57,7 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
SkCodec::Result result;
auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker);
auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
if (!codec) {
switch (result) {
case SkCodec::kIncompleteInput:
@@ -90,22 +69,24 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
SkCodec::ResultToString(result));
doThrowIOE(env, msg.c_str());
break;
}
}
return nullptr;
}
// FIXME: Avoid parsing the whole image?
const bool animated = codec->getFrameCount() > 1;
decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!decoder->mCodec.get()) {
doThrowIOE(env, "Could not create AndroidCodec");
return nullptr;
}
const auto& info = decoder->mCodec->getInfo();
const int width = info.width();
const int height = info.height();
return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
reinterpret_cast<jlong>(decoder.release()), width, height);
reinterpret_cast<jlong>(decoder.release()), width, height,
animated);
}
static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
@@ -176,6 +157,24 @@ static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jby
return native_create(env, std::move(stream));
}
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
int width, int height) {
jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
reinterpret_cast<jlong>(canvas.get()));
if (!jcanvas) {
doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
return ImageDecoder::kUnknown;
}
// jcanvas now owns canvas.
canvas.release();
return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID,
jcanvas, width, height);
}
// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
// otherwise.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject jcallback, jobject jpostProcess,
jint desiredWidth, jint desiredHeight, jobject jsubset,
@@ -322,23 +321,23 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
// Ignore ninepatch when post-processing.
if (!jpostProcess) {
// FIXME: Share more code with BitmapFactory.cpp.
if (decoder->mPeeker.mPatch != nullptr) {
if (decoder->mPeeker->mPatch != nullptr) {
if (scale) {
decoder->mPeeker.scale(scaleX, scaleY, desiredWidth, desiredHeight);
decoder->mPeeker->scale(scaleX, scaleY, desiredWidth, desiredHeight);
}
size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize();
size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize();
ninePatchChunk = env->NewByteArray(ninePatchArraySize);
if (ninePatchChunk == nullptr) {
doThrowOOME(env, "Failed to allocate nine patch chunk.");
return nullptr;
}
env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize,
reinterpret_cast<jbyte*>(decoder->mPeeker.mPatch));
env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize,
reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch));
}
if (decoder->mPeeker.mHasInsets) {
ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f);
if (decoder->mPeeker->mHasInsets) {
ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f);
if (ninePatchInsets == nullptr) {
doThrowOOME(env, "Failed to allocate nine patch insets.");
return nullptr;
@@ -399,23 +398,9 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
if (jpostProcess) {
std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
reinterpret_cast<jlong>(canvas.get()));
if (!jcanvas) {
doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
return nullptr;
}
// jcanvas will now own canvas.
canvas.release();
jint pixelFormat = env->CallIntMethod(jpostProcess, gPostProcess_postProcessMethodID,
jcanvas, bm.width(), bm.height());
if (env->ExceptionCheck()) {
return nullptr;
}
// The Canvas objects are no longer needed, and will not remain valid.
env->CallVoidMethod(jcanvas, gCanvas_releaseMethodID);
jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas),
bm.width(), bm.height());
if (env->ExceptionCheck()) {
return nullptr;
}
@@ -493,7 +478,7 @@ static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlon
static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject outPadding) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
decoder->mPeeker.getPadding(env, outPadding);
decoder->mPeeker->getPadding(env, outPadding);
}
static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
@@ -511,7 +496,7 @@ static const JNINativeMethod gImageDecoderMethods[] = {
{ "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
{ "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
{ "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
{ "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
{ "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
@@ -521,7 +506,8 @@ static const JNINativeMethod gImageDecoderMethods[] = {
int register_android_graphics_ImageDecoder(JNIEnv* env) {
gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JII)V");
gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V");
gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I");
gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V");
@@ -535,9 +521,6 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) {
jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener");
gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z");
jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess");
gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I");
gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V");

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 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 "NinePatchPeeker.h"
#include <hwui/Canvas.h>
#include <jni.h>
class SkAndroidCodec;
using namespace android;
struct ImageDecoder {
// These need to stay in sync with ImageDecoder.java's Allocator constants.
enum Allocator {
kDefault_Allocator = 0,
kSoftware_Allocator = 1,
kSharedMemory_Allocator = 2,
kHardware_Allocator = 3,
};
// These need to stay in sync with PixelFormat.java's Format constants.
enum PixelFormat {
kUnknown = 0,
kTranslucent = -3,
kOpaque = -1,
};
std::unique_ptr<SkAndroidCodec> mCodec;
sk_sp<NinePatchPeeker> mPeeker;
ImageDecoder()
:mPeeker(new NinePatchPeeker)
{}
};
// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
// releases the Canvas.
// Caller needs to check for exceptions.
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
int width, int height);

View File

@@ -26,6 +26,7 @@ import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.NinePatchDrawable;
@@ -294,9 +295,10 @@ public final class ImageDecoder implements AutoCloseable {
};
// Fields
private long mNativePtr;
private final int mWidth;
private final int mHeight;
private long mNativePtr;
private final int mWidth;
private final int mHeight;
private final boolean mAnimated;
private int mDesiredWidth;
private int mDesiredHeight;
@@ -322,12 +324,14 @@ public final class ImageDecoder implements AutoCloseable {
* called after decoding to delete native resources.
*/
@SuppressWarnings("unused")
private ImageDecoder(long nativePtr, int width, int height) {
private ImageDecoder(long nativePtr, int width, int height,
boolean animated) {
mNativePtr = nativePtr;
mWidth = width;
mHeight = height;
mDesiredWidth = width;
mDesiredHeight = height;
mAnimated = animated;
mCloseGuard.open("close");
}
@@ -677,6 +681,18 @@ public final class ImageDecoder implements AutoCloseable {
}
}
private Bitmap decodeBitmap() throws IOException {
checkState();
// nDecodeBitmap calls postProcessAndRelease only if mPostProcess
// exists.
ImageDecoder postProcessPtr = mPostProcess == null ? null : this;
return nDecodeBitmap(mNativePtr, mOnPartialImageListener,
postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
mPreferRamOverQuality, mAsAlphaMask);
}
/**
* Create a {@link Drawable} from a {@code Source}.
*
@@ -702,8 +718,6 @@ public final class ImageDecoder implements AutoCloseable {
}
}
decoder.checkState();
if (decoder.mRequireUnpremultiplied) {
// Though this could be supported (ignored) for opaque images,
// it seems better to always report this error.
@@ -716,17 +730,22 @@ public final class ImageDecoder implements AutoCloseable {
"Drawable!");
}
Bitmap bm = nDecodeBitmap(decoder.mNativePtr,
decoder.mOnPartialImageListener,
decoder.mPostProcess,
decoder.mDesiredWidth,
decoder.mDesiredHeight,
decoder.mCropRect,
false, // mMutable
decoder.mAllocator,
false, // mRequireUnpremultiplied
decoder.mPreferRamOverQuality,
decoder.mAsAlphaMask);
if (decoder.mAnimated) {
// AnimatedImageDrawable calls postProcessAndRelease only if
// mPostProcess exists.
ImageDecoder postProcessPtr = decoder.mPostProcess == null ?
null : decoder;
Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
postProcessPtr, decoder.mDesiredWidth,
decoder.mDesiredHeight, decoder.mCropRect,
decoder.mInputStream, decoder.mAssetFd);
// d has taken ownership of these objects.
decoder.mInputStream = null;
decoder.mAssetFd = null;
return d;
}
Bitmap bm = decoder.decodeBitmap();
Resources res = src.getResources();
if (res == null) {
bm.setDensity(Bitmap.DENSITY_NONE);
@@ -742,7 +761,6 @@ public final class ImageDecoder implements AutoCloseable {
opticalInsets, null);
}
// TODO: Handle animation.
return new BitmapDrawable(res, bm);
}
}
@@ -781,19 +799,7 @@ public final class ImageDecoder implements AutoCloseable {
}
}
decoder.checkState();
return nDecodeBitmap(decoder.mNativePtr,
decoder.mOnPartialImageListener,
decoder.mPostProcess,
decoder.mDesiredWidth,
decoder.mDesiredHeight,
decoder.mCropRect,
decoder.mMutable,
decoder.mAllocator,
decoder.mRequireUnpremultiplied,
decoder.mPreferRamOverQuality,
decoder.mAsAlphaMask);
return decoder.decodeBitmap();
}
}
@@ -809,6 +815,18 @@ public final class ImageDecoder implements AutoCloseable {
return decodeBitmap(src, null);
}
/**
* Private method called by JNI.
*/
@SuppressWarnings("unused")
private int postProcessAndRelease(@NonNull Canvas canvas, int width, int height) {
try {
return mPostProcess.postProcess(canvas, width, height);
} finally {
canvas.release();
}
}
private static native ImageDecoder nCreate(long asset) throws IOException;
private static native ImageDecoder nCreate(ByteBuffer buffer,
int position,
@@ -820,7 +838,7 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
private static native Bitmap nDecodeBitmap(long nativePtr,
OnPartialImageListener listener,
PostProcess postProcess,
@Nullable ImageDecoder decoder, // Only used if mPostProcess != null
int width, int height,
Rect cropRect, boolean mutable,
int allocator, boolean requireUnpremul,

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2018 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.drawable;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
import libcore.io.IoUtils;
import libcore.util.NativeAllocationRegistry;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Runnable;
/**
* @hide
*/
public class AnimatedImageDrawable extends Drawable implements Animatable {
private final long mNativePtr;
private final InputStream mInputStream;
private final AssetFileDescriptor mAssetFd;
private final int mIntrinsicWidth;
private final int mIntrinsicHeight;
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
invalidateSelf();
}
};
/**
* @hide
* This should only be called by ImageDecoder.
*
* decoder is only non-null if it has a PostProcess
*/
public AnimatedImageDrawable(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect,
InputStream inputStream, AssetFileDescriptor afd)
throws IOException {
mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
mInputStream = inputStream;
mAssetFd = afd;
if (cropRect == null) {
mIntrinsicWidth = width;
mIntrinsicHeight = height;
} else {
mIntrinsicWidth = cropRect.width();
mIntrinsicHeight = cropRect.height();
}
long nativeSize = nNativeByteSize(mNativePtr);
NativeAllocationRegistry registry = new NativeAllocationRegistry(
AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
registry.registerNativeAllocation(this, mNativePtr);
}
@Override
protected void finalize() throws Throwable {
// FIXME: It's a shame that we have *both* a native finalizer and a Java
// one. The native one is necessary to report how much memory is being
// used natively, and this one is necessary to close the input. An
// alternative might be to read the entire stream ahead of time, so we
// can eliminate the Java finalizer.
try {
IoUtils.closeQuietly(mInputStream);
IoUtils.closeQuietly(mAssetFd);
} finally {
super.finalize();
}
}
@Override
public int getIntrinsicWidth() {
return mIntrinsicWidth;
}
@Override
public int getIntrinsicHeight() {
return mIntrinsicHeight;
}
@Override
public void draw(@NonNull Canvas canvas) {
long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper(),
SystemClock.uptimeMillis());
scheduleSelf(mRunnable, nextUpdate);
}
@Override
public void setAlpha(@IntRange(from=0,to=255) int alpha) {
if (alpha < 0 || alpha > 255) {
throw new IllegalArgumentException("Alpha must be between 0 and"
+ " 255! provided " + alpha);
}
nSetAlpha(mNativePtr, alpha);
}
@Override
public int getAlpha() {
return nGetAlpha(mNativePtr);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance();
nSetColorFilter(mNativePtr, nativeFilter);
}
@Override
public @PixelFormat.Opacity int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
// TODO: Add a Constant State?
// @Override
// public @Nullable ConstantState getConstantState() {}
// Animatable overrides
@Override
public boolean isRunning() {
return nIsRunning(mNativePtr);
}
@Override
public void start() {
nStart(mNativePtr);
}
@Override
public void stop() {
nStop(mNativePtr);
}
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
throws IOException;
private static native long nGetNativeFinalizer();
private static native long nDraw(long nativePtr, long canvasNativePtr, long msecs);
private static native void nSetAlpha(long nativePtr, int alpha);
private static native int nGetAlpha(long nativePtr);
private static native void nSetColorFilter(long nativePtr, long nativeFilter);
private static native boolean nIsRunning(long nativePtr);
private static native void nStart(long nativePtr);
private static native void nStop(long nativePtr);
private static native long nNativeByteSize(long nativePtr);
}

View File

@@ -495,6 +495,11 @@ void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patc
refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
}
void RecordingCanvas::drawAnimatedImage(SkAnimatedImage*, float left, float top,
const SkPaint*) {
// Unimplemented
}
// Text
void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint,
float x, float y, float boundsLeft, float boundsTop,

View File

@@ -183,6 +183,8 @@ public:
virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft,
float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top,
const SkPaint* paint) override;
// Text
virtual bool drawTextAbsolutePos() const override { return false; }

View File

@@ -23,6 +23,7 @@
#include "hwui/MinikinUtils.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
#include <SkColorSpaceXformCanvas.h>
@@ -32,6 +33,7 @@
#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImagePriv.h>
#include <SkPicture.h>
#include <SkRSXform.h>
#include <SkShader.h>
#include <SkTemplates.h>
@@ -723,6 +725,20 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa
mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter));
}
void SkiaCanvas::drawAnimatedImage(SkAnimatedImage* image, float left, float top,
const SkPaint* paint) {
sk_sp<SkPicture> pic(image->newPictureSnapshot());
SkMatrix matrixStorage;
SkMatrix* matrix;
if (left == 0.0f && top == 0.0f) {
matrix = nullptr;
} else {
matrixStorage = SkMatrix::MakeTrans(left, top);
matrix = &matrixStorage;
}
mCanvas->drawPicture(pic.get(), matrix, paint);
}
void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
vectorDrawable->drawStaging(this);
}

View File

@@ -124,6 +124,8 @@ public:
virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft,
float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top,
const SkPaint* paint) override;
virtual bool drawTextAbsolutePos() const override { return true; }
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;

View File

@@ -28,6 +28,7 @@
#include <SkCanvas.h>
#include <SkMatrix.h>
class SkAnimatedImage;
class SkCanvasState;
class SkVertices;
@@ -237,6 +238,9 @@ public:
float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) = 0;
virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top,
const SkPaint* paint) = 0;
/**
* Specifies if the positions passed to ::drawText are absolute or relative
* to the (x,y) value provided.