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 26static struct { 27 jclass clazz; 28 jmethodID ctor; 29} gFrameSequenceClassInfo; 30 31//////////////////////////////////////////////////////////////////////////////// 32// Frame sequence 33//////////////////////////////////////////////////////////////////////////////// 34 35static 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 48static 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 62static 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 77static 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 84static 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 94static 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 104static void nativeDestroyState( 105 JNIEnv* env, jobject clazz, jlong frameSequenceStateLong) { 106 FrameSequenceState* frameSequenceState = 107 reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong); 108 delete frameSequenceState; 109} 110 111void 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 117static 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 144static 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 175jint 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