Refactor android.graphics.Picture JNI bindings.

This is the first CL in a series of CLs to refactor the Graphics JNI bindings.

bug: 15672762
Change-Id: I1455fa1330c7426407c06eeaad81ad37a57373b1
This commit is contained in:
Derek Sollenberger
2014-06-12 12:31:10 -04:00
parent 88b0078468
commit 4b0959d8db
9 changed files with 247 additions and 293 deletions

View File

@@ -801,20 +801,12 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPicture(Picture picture) {
if (picture.createdFromStream) {
return;
}
picture.endRecording();
// TODO: Implement rendering
}
@Override
public void drawPicture(Picture picture, Rect dst) {
if (picture.createdFromStream) {
return;
}
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -826,10 +818,6 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPicture(Picture picture, RectF dst) {
if (picture.createdFromStream) {
return;
}
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {

View File

@@ -89,7 +89,7 @@ LOCAL_SRC_FILES:= \
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
android/graphics/AndroidPicture.cpp \
android_graphics_Picture.cpp \
android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \

View File

@@ -1,112 +0,0 @@
/*
* Copyright (C) 2014 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 "AndroidPicture.h"
#include "SkCanvas.h"
#include "SkStream.h"
AndroidPicture::AndroidPicture(const AndroidPicture* src) {
if (NULL != src) {
mWidth = src->width();
mHeight = src->height();
if (NULL != src->mPicture.get()) {
mPicture.reset(SkRef(src->mPicture.get()));
} if (NULL != src->mRecorder.get()) {
mPicture.reset(src->makePartialCopy());
}
} else {
mWidth = 0;
mHeight = 0;
}
}
SkCanvas* AndroidPicture::beginRecording(int width, int height) {
mPicture.reset(NULL);
mRecorder.reset(new SkPictureRecorder);
mWidth = width;
mHeight = height;
return mRecorder->beginRecording(width, height, NULL, 0);
}
void AndroidPicture::endRecording() {
if (NULL != mRecorder.get()) {
mPicture.reset(mRecorder->endRecording());
mRecorder.reset(NULL);
}
}
int AndroidPicture::width() const {
if (NULL != mPicture.get()) {
SkASSERT(mPicture->width() == mWidth);
SkASSERT(mPicture->height() == mHeight);
}
return mWidth;
}
int AndroidPicture::height() const {
if (NULL != mPicture.get()) {
SkASSERT(mPicture->width() == mWidth);
SkASSERT(mPicture->height() == mHeight);
}
return mHeight;
}
AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) {
AndroidPicture* newPict = new AndroidPicture;
newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
if (NULL != newPict->mPicture.get()) {
newPict->mWidth = newPict->mPicture->width();
newPict->mHeight = newPict->mPicture->height();
}
return newPict;
}
void AndroidPicture::serialize(SkWStream* stream) const {
if (NULL != mRecorder.get()) {
SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
tempPict->serialize(stream);
} else if (NULL != mPicture.get()) {
mPicture->serialize(stream);
} else {
SkPicture empty;
empty.serialize(stream);
}
}
void AndroidPicture::draw(SkCanvas* canvas) {
if (NULL != mRecorder.get()) {
this->endRecording();
SkASSERT(NULL != mPicture.get());
}
if (NULL != mPicture.get()) {
// TODO: remove this const_cast once pictures are immutable
const_cast<SkPicture*>(mPicture.get())->draw(canvas);
}
}
SkPicture* AndroidPicture::makePartialCopy() const {
SkASSERT(NULL != mRecorder.get());
SkPictureRecorder reRecorder;
SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
mRecorder->partialReplay(canvas);
return reRecorder.endRecording();
}

View File

@@ -3,7 +3,6 @@
#include "jni.h"
#include "JNIHelp.h"
#include "GraphicsJNI.h"
#include "AndroidPicture.h"
#include "SkCanvas.h"
#include "SkDevice.h"
@@ -346,17 +345,6 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint
return p;
}
AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
{
SkASSERT(env);
SkASSERT(picture);
SkASSERT(env->IsInstanceOf(picture, gPicture_class));
jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID);
AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(p);
return p;
}
SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
{
SkASSERT(env);

View File

@@ -14,7 +14,6 @@
class SkBitmapRegionDecoder;
class SkCanvas;
class SkPaint;
class AndroidPicture;
class GraphicsJNI {
public:
@@ -50,7 +49,6 @@ public:
static SkPaint* getNativePaint(JNIEnv*, jobject paint);
static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
static AndroidPicture* getNativePicture(JNIEnv*, jobject picture);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
// Given the 'native' long held by the Rasterizer.java object, return a

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2014 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.
@@ -14,123 +14,102 @@
* limitations under the License.
*/
#include "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
#include "AndroidPicture.h"
#include "Picture.h"
#include "SkCanvas.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "CreateJavaOutputStreamAdaptor.h"
namespace android {
using namespace android;
class SkPictureGlue {
public:
static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) {
const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle);
return reinterpret_cast<jlong>(new AndroidPicture(src));
}
static jlong deserialize(JNIEnv* env, jobject, jobject jstream,
jbyteArray jstorage) {
AndroidPicture* picture = NULL;
SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
if (strm) {
picture = AndroidPicture::CreateFromStream(strm);
delete strm;
Picture::Picture(const Picture* src) {
if (NULL != src) {
mWidth = src->width();
mHeight = src->height();
if (NULL != src->mPicture.get()) {
mPicture.reset(SkRef(src->mPicture.get()));
} if (NULL != src->mRecorder.get()) {
mPicture.reset(src->makePartialCopy());
}
return reinterpret_cast<jlong>(picture);
} else {
mWidth = 0;
mHeight = 0;
}
static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(picture);
delete picture;
}
static void draw(JNIEnv* env, jobject, jlong canvasHandle,
jlong pictureHandle) {
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(canvas);
SkASSERT(picture);
picture->draw(canvas);
}
static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle,
jobject jstream, jbyteArray jstorage) {
AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
if (NULL != strm) {
picture->serialize(strm);
delete strm;
return JNI_TRUE;
}
return JNI_FALSE;
}
static jint getWidth(JNIEnv* env, jobject jpic) {
NPE_CHECK_RETURN_ZERO(env, jpic);
AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
int width = pict->width();
return static_cast<jint>(width);
}
static jint getHeight(JNIEnv* env, jobject jpic) {
NPE_CHECK_RETURN_ZERO(env, jpic);
AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
int height = pict->height();
return static_cast<jint>(height);
}
static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle,
jint w, jint h) {
AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
// beginRecording does not ref its return value, it just returns it.
SkCanvas* canvas = pict->beginRecording(w, h);
// the java side will wrap this guy in a Canvas.java, which will call
// unref in its finalizer, so we have to ref it here, so that both that
// Canvas.java and our picture can both be owners
canvas->ref();
return reinterpret_cast<jlong>(canvas);
}
static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
pict->endRecording();
}
};
static JNINativeMethod gPictureMethods[] = {
{"getWidth", "()I", (void*) SkPictureGlue::getWidth},
{"getHeight", "()I", (void*) SkPictureGlue::getHeight},
{"nativeConstructor", "(J)J", (void*) SkPictureGlue::newPicture},
{"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)SkPictureGlue::deserialize},
{"nativeBeginRecording", "(JII)J", (void*) SkPictureGlue::beginRecording},
{"nativeEndRecording", "(J)V", (void*) SkPictureGlue::endRecording},
{"nativeDraw", "(JJ)V", (void*) SkPictureGlue::draw},
{"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize},
{"nativeDestructor","(J)V", (void*) SkPictureGlue::killPicture}
};
#include <android_runtime/AndroidRuntime.h>
#define REG(env, name, array) \
result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
SK_ARRAY_COUNT(array)); \
if (result < 0) return result
int register_android_graphics_Picture(JNIEnv* env) {
int result;
REG(env, "android/graphics/Picture", gPictureMethods);
return result;
}
SkCanvas* Picture::beginRecording(int width, int height) {
mPicture.reset(NULL);
mRecorder.reset(new SkPictureRecorder);
mWidth = width;
mHeight = height;
return mRecorder->beginRecording(width, height, NULL, 0);
}
void Picture::endRecording() {
if (NULL != mRecorder.get()) {
mPicture.reset(mRecorder->endRecording());
mRecorder.reset(NULL);
}
}
int Picture::width() const {
if (NULL != mPicture.get()) {
SkASSERT(mPicture->width() == mWidth);
SkASSERT(mPicture->height() == mHeight);
}
return mWidth;
}
int Picture::height() const {
if (NULL != mPicture.get()) {
SkASSERT(mPicture->width() == mWidth);
SkASSERT(mPicture->height() == mHeight);
}
return mHeight;
}
Picture* Picture::CreateFromStream(SkStream* stream) {
Picture* newPict = new Picture;
newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
if (NULL != newPict->mPicture.get()) {
newPict->mWidth = newPict->mPicture->width();
newPict->mHeight = newPict->mPicture->height();
}
return newPict;
}
void Picture::serialize(SkWStream* stream) const {
if (NULL != mRecorder.get()) {
SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
tempPict->serialize(stream);
} else if (NULL != mPicture.get()) {
mPicture->serialize(stream);
} else {
SkPicture empty;
empty.serialize(stream);
}
}
void Picture::draw(SkCanvas* canvas) {
if (NULL != mRecorder.get()) {
this->endRecording();
SkASSERT(NULL != mPicture.get());
}
if (NULL != mPicture.get()) {
// TODO: remove this const_cast once pictures are immutable
const_cast<SkPicture*>(mPicture.get())->draw(canvas);
}
}
SkPicture* Picture::makePartialCopy() const {
SkASSERT(NULL != mRecorder.get());
SkPictureRecorder reRecorder;
SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
mRecorder->partialReplay(canvas);
return reRecorder.endRecording();
}

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef ANDROID_PICTURE_H
#define ANDROID_PICTURE_H
#ifndef ANDROID_GRAPHICS_PICTURE_H
#define ANDROID_GRAPHICS_PICTURE_H
#include "SkPicture.h"
#include "SkPictureRecorder.h"
@@ -28,13 +28,15 @@ class SkPictureRecorder;
class SkStream;
class SkWStream;
namespace android {
// Skia's SkPicture class has been split into an SkPictureRecorder
// and an SkPicture. AndroidPicture recreates the functionality
// of the old SkPicture interface by flip-flopping between the two
// new classes.
class AndroidPicture {
class Picture {
public:
explicit AndroidPicture(const AndroidPicture* src = NULL);
explicit Picture(const Picture* src = NULL);
SkCanvas* beginRecording(int width, int height);
@@ -44,7 +46,7 @@ public:
int height() const;
static AndroidPicture* CreateFromStream(SkStream* stream);
static Picture* CreateFromStream(SkStream* stream);
void serialize(SkWStream* stream) const;
@@ -60,4 +62,6 @@ private:
// resulting picture will have balanced saves and restores.
SkPicture* makePartialCopy() const;
};
#endif // ANDROID_PICTURE_H
}; // namespace android
#endif // ANDROID_GRAPHICS_PICTURE_H

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2008 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 "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
#include "Picture.h"
#include "SkCanvas.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "CreateJavaOutputStreamAdaptor.h"
namespace android {
static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) {
const Picture* src = reinterpret_cast<Picture*>(srcHandle);
return reinterpret_cast<jlong>(new Picture(src));
}
static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream,
jbyteArray jstorage) {
Picture* picture = NULL;
SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
if (strm) {
picture = Picture::CreateFromStream(strm);
delete strm;
}
return reinterpret_cast<jlong>(picture);
}
static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
SkASSERT(picture);
delete picture;
}
static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
jlong pictureHandle) {
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
SkASSERT(canvas);
SkASSERT(picture);
picture->draw(canvas);
}
static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle,
jobject jstream, jbyteArray jstorage) {
Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
if (NULL != strm) {
picture->serialize(strm);
delete strm;
return JNI_TRUE;
}
return JNI_FALSE;
}
static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) {
Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
return static_cast<jint>(pict->width());
}
static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) {
Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
return static_cast<jint>(pict->height());
}
static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
jint w, jint h) {
Picture* pict = reinterpret_cast<Picture*>(pictHandle);
// beginRecording does not ref its return value, it just returns it.
SkCanvas* canvas = pict->beginRecording(w, h);
// the java side will wrap this guy in a Canvas.java, which will call
// unref in its finalizer, so we have to ref it here, so that both that
// Canvas.java and our picture can both be owners
canvas->ref();
return reinterpret_cast<jlong>(canvas);
}
static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) {
Picture* pict = reinterpret_cast<Picture*>(pictHandle);
pict->endRecording();
}
static JNINativeMethod gMethods[] = {
{"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth},
{"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight},
{"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture},
{"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)android_graphics_Picture_deserialize},
{"nativeBeginRecording", "(JII)J", (void*) android_graphics_Picture_beginRecording},
{"nativeEndRecording", "(J)V", (void*) android_graphics_Picture_endRecording},
{"nativeDraw", "(JJ)V", (void*) android_graphics_Picture_draw},
{"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)android_graphics_Picture_serialize},
{"nativeDestructor","(J)V", (void*) android_graphics_Picture_killPicture}
};
int register_android_graphics_Picture(JNIEnv* env) {
return AndroidRuntime::registerNativeMethods(env, "android/graphics/Picture", gMethods, NELEM(gMethods));
}
}; // namespace android

View File

@@ -31,18 +31,13 @@ public class Picture {
private Canvas mRecordingCanvas;
private final long mNativePicture;
/**
* @hide
*/
public final boolean createdFromStream;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
/**
* Creates an empty picture that is ready to record.
*/
public Picture() {
this(nativeConstructor(0), false);
this(nativeConstructor(0));
}
/**
@@ -51,7 +46,23 @@ public class Picture {
* changes will not be reflected in this picture.
*/
public Picture(Picture src) {
this(nativeConstructor(src != null ? src.mNativePicture : 0), false);
this(nativeConstructor(src != null ? src.mNativePicture : 0));
}
private Picture(long nativePicture) {
if (nativePicture == 0) {
throw new RuntimeException();
}
mNativePicture = nativePicture;
}
@Override
protected void finalize() throws Throwable {
try {
nativeDestructor(mNativePicture);
} finally {
super.finalize();
}
}
/**
@@ -85,13 +96,17 @@ public class Picture {
* Get the width of the picture as passed to beginRecording. This
* does not reflect (per se) the content of the picture.
*/
public native int getWidth();
public int getWidth() {
return nativeGetWidth(mNativePicture);
}
/**
* Get the height of the picture as passed to beginRecording. This
* does not reflect (per se) the content of the picture.
*/
public native int getHeight();
public int getHeight() {
return nativeGetHeight(mNativePicture);
}
/**
* Draw this picture on the canvas.
@@ -130,7 +145,7 @@ public class Picture {
*/
@Deprecated
public static Picture createFromStream(InputStream stream) {
return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true);
return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]));
}
/**
@@ -159,32 +174,12 @@ public class Picture {
}
}
protected void finalize() throws Throwable {
try {
nativeDestructor(mNativePicture);
} finally {
super.finalize();
}
}
final long ni() {
return mNativePicture;
}
private Picture(long nativePicture, boolean fromStream) {
if (nativePicture == 0) {
throw new RuntimeException();
}
mNativePicture = nativePicture;
createdFromStream = fromStream;
}
// return empty picture if src is 0, or a copy of the native src
private static native long nativeConstructor(long nativeSrcOr0);
private static native long nativeCreateFromStream(InputStream stream,
byte[] storage);
private static native long nativeBeginRecording(long nativeCanvas,
int w, int h);
private static native long nativeCreateFromStream(InputStream stream, byte[] storage);
private static native int nativeGetWidth(long nativePicture);
private static native int nativeGetHeight(long nativePicture);
private static native long nativeBeginRecording(long nativeCanvas, int w, int h);
private static native void nativeEndRecording(long nativeCanvas);
private static native void nativeDraw(long nativeCanvas, long nativePicture);
private static native boolean nativeWriteToStream(long nativePicture,
@@ -201,18 +196,15 @@ public class Picture {
@Override
public void setBitmap(Bitmap bitmap) {
throw new RuntimeException(
"Cannot call setBitmap on a picture canvas");
throw new RuntimeException("Cannot call setBitmap on a picture canvas");
}
@Override
public void drawPicture(Picture picture) {
if (mPicture == picture) {
throw new RuntimeException(
"Cannot draw a picture into its recording canvas");
throw new RuntimeException("Cannot draw a picture into its recording canvas");
}
super.drawPicture(picture);
}
}
}