1 /*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android/bitmap.h>
18 #include "JNIHelpers.h"
19 #include "utils/log.h"
20 #include "FrameSequence.h"
21
22 #include "FrameSequenceJNI.h"
23
24 #define JNI_PACKAGE "android/support/rastermill"
25
26 static struct {
27 jclass clazz;
28 jmethodID ctor;
29 } gFrameSequenceClassInfo;
30
31 ////////////////////////////////////////////////////////////////////////////////
32 // Frame sequence
33 ////////////////////////////////////////////////////////////////////////////////
34
createJavaFrameSequence(JNIEnv * env,FrameSequence * frameSequence)35 static jobject createJavaFrameSequence(JNIEnv* env, FrameSequence* frameSequence) {
36 if (!frameSequence) {
37 return NULL;
38 }
39 return env->NewObject(gFrameSequenceClassInfo.clazz, gFrameSequenceClassInfo.ctor,
40 reinterpret_cast<jlong>(frameSequence),
41 frameSequence->getWidth(),
42 frameSequence->getHeight(),
43 frameSequence->isOpaque(),
44 frameSequence->getFrameCount(),
45 frameSequence->getDefaultLoopCount());
46 }
47
nativeDecodeByteArray(JNIEnv * env,jobject clazz,jbyteArray byteArray,jint offset,jint length)48 static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz,
49 jbyteArray byteArray, jint offset, jint length) {
50 jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(byteArray, NULL));
51 if (bytes == NULL) {
52 jniThrowException(env, ILLEGAL_STATE_EXEPTION,
53 "couldn't read array bytes");
54 return NULL;
55 }
56 MemoryStream stream(bytes + offset, length, NULL);
57 FrameSequence* frameSequence = FrameSequence::create(&stream);
58 env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0);
59 return createJavaFrameSequence(env, frameSequence);
60 }
61
nativeDecodeByteBuffer(JNIEnv * env,jobject clazz,jobject buf,jint offset,jint limit)62 static jobject nativeDecodeByteBuffer(JNIEnv* env, jobject clazz,
63 jobject buf, jint offset, jint limit) {
64 jobject globalBuf = env->NewGlobalRef(buf);
65 JavaVM* vm;
66 env->GetJavaVM(&vm);
67 MemoryStream stream(
68 (reinterpret_cast<uint8_t*>(
69 env->GetDirectBufferAddress(globalBuf))) + offset,
70 limit,
71 globalBuf);
72 FrameSequence* frameSequence = FrameSequence::create(&stream);
73 jobject finalSequence = createJavaFrameSequence(env, frameSequence);
74 return finalSequence;
75 }
76
nativeDecodeStream(JNIEnv * env,jobject clazz,jobject istream,jbyteArray byteArray)77 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
78 jobject istream, jbyteArray byteArray) {
79 JavaInputStream stream(env, istream, byteArray);
80 FrameSequence* frameSequence = FrameSequence::create(&stream);
81 return createJavaFrameSequence(env, frameSequence);
82 }
83
nativeDestroyFrameSequence(JNIEnv * env,jobject clazz,jlong frameSequenceLong)84 static void nativeDestroyFrameSequence(JNIEnv* env, jobject clazz,
85 jlong frameSequenceLong) {
86 FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong);
87 jobject buf = frameSequence->getRawByteBuffer();
88 if (buf != NULL) {
89 env->DeleteGlobalRef(buf);
90 }
91 delete frameSequence;
92 }
93
nativeCreateState(JNIEnv * env,jobject clazz,jlong frameSequenceLong)94 static jlong nativeCreateState(JNIEnv* env, jobject clazz, jlong frameSequenceLong) {
95 FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong);
96 FrameSequenceState* state = frameSequence->createState();
97 return reinterpret_cast<jlong>(state);
98 }
99
100 ////////////////////////////////////////////////////////////////////////////////
101 // Frame sequence state
102 ////////////////////////////////////////////////////////////////////////////////
103
nativeDestroyState(JNIEnv * env,jobject clazz,jlong frameSequenceStateLong)104 static void nativeDestroyState(
105 JNIEnv* env, jobject clazz, jlong frameSequenceStateLong) {
106 FrameSequenceState* frameSequenceState =
107 reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong);
108 delete frameSequenceState;
109 }
110
throwIae(JNIEnv * env,const char * message,int errorCode)111 void throwIae(JNIEnv* env, const char* message, int errorCode) {
112 char buf[256];
113 snprintf(buf, sizeof(buf), "%s, error %d", message, errorCode);
114 jniThrowException(env, ILLEGAL_STATE_EXEPTION, buf);
115 }
116
nativeGetFrame(JNIEnv * env,jobject clazz,jlong frameSequenceStateLong,jint frameNr,jobject bitmap,jint previousFrameNr)117 static jlong JNICALL nativeGetFrame(
118 JNIEnv* env, jobject clazz, jlong frameSequenceStateLong, jint frameNr,
119 jobject bitmap, jint previousFrameNr) {
120 FrameSequenceState* frameSequenceState =
121 reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong);
122 int ret;
123 AndroidBitmapInfo info;
124 void* pixels;
125
126 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
127 throwIae(env, "Couldn't get info from Bitmap", ret);
128 return 0;
129 }
130
131 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
132 throwIae(env, "Bitmap pixels couldn't be locked", ret);
133 return 0;
134 }
135
136 int pixelStride = info.stride >> 2;
137 jlong delayMs = frameSequenceState->drawFrame(frameNr,
138 (Color8888*) pixels, pixelStride, previousFrameNr);
139
140 AndroidBitmap_unlockPixels(env, bitmap);
141 return delayMs;
142 }
143
144 static JNINativeMethod gMethods[] = {
145 { "nativeDecodeByteArray",
146 "([BII)L" JNI_PACKAGE "/FrameSequence;",
147 (void*) nativeDecodeByteArray
148 },
149 { "nativeDecodeByteBuffer",
150 "(Ljava/nio/ByteBuffer;II)L" JNI_PACKAGE "/FrameSequence;",
151 (void*) nativeDecodeByteBuffer
152 },
153 { "nativeDecodeStream",
154 "(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;",
155 (void*) nativeDecodeStream
156 },
157 { "nativeDestroyFrameSequence",
158 "(J)V",
159 (void*) nativeDestroyFrameSequence
160 },
161 { "nativeCreateState",
162 "(J)J",
163 (void*) nativeCreateState
164 },
165 { "nativeGetFrame",
166 "(JILandroid/graphics/Bitmap;I)J",
167 (void*) nativeGetFrame
168 },
169 { "nativeDestroyState",
170 "(J)V",
171 (void*) nativeDestroyState
172 },
173 };
174
FrameSequence_OnLoad(JNIEnv * env)175 jint FrameSequence_OnLoad(JNIEnv* env) {
176 // Get jclass with env->FindClass.
177 // Register methods with env->RegisterNatives.
178 gFrameSequenceClassInfo.clazz = env->FindClass(JNI_PACKAGE "/FrameSequence");
179 if (!gFrameSequenceClassInfo.clazz) {
180 ALOGW("Failed to find " JNI_PACKAGE "/FrameSequence");
181 return -1;
182 }
183 gFrameSequenceClassInfo.clazz = (jclass)env->NewGlobalRef(gFrameSequenceClassInfo.clazz);
184
185 gFrameSequenceClassInfo.ctor = env->GetMethodID(gFrameSequenceClassInfo.clazz, "<init>", "(JIIZII)V");
186 if (!gFrameSequenceClassInfo.ctor) {
187 ALOGW("Failed to find constructor for FrameSequence - was it stripped?");
188 return -1;
189 }
190
191 return env->RegisterNatives(gFrameSequenceClassInfo.clazz, gMethods, METHOD_COUNT(gMethods));
192 }
193