/*
* Copyright (C) 2013 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 <android/bitmap.h>
#include "JNIHelpers.h"
#include "utils/log.h"
#include "FrameSequence.h"
#include "FrameSequenceJNI.h"
#define JNI_PACKAGE "android/support/rastermill"
static struct {
jclass clazz;
jmethodID ctor;
} gFrameSequenceClassInfo;
////////////////////////////////////////////////////////////////////////////////
// Frame sequence
////////////////////////////////////////////////////////////////////////////////
static jobject createJavaFrameSequence(JNIEnv* env, FrameSequence* frameSequence) {
if (!frameSequence) {
return NULL;
}
return env->NewObject(gFrameSequenceClassInfo.clazz, gFrameSequenceClassInfo.ctor,
reinterpret_cast<jlong>(frameSequence),
frameSequence->getWidth(),
frameSequence->getHeight(),
frameSequence->isOpaque(),
frameSequence->getFrameCount(),
frameSequence->getDefaultLoopCount());
}
static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz,
jbyteArray byteArray, jint offset, jint length) {
jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(byteArray, NULL));
if (bytes == NULL) {
jniThrowException(env, ILLEGAL_STATE_EXEPTION,
"couldn't read array bytes");
return NULL;
}
MemoryStream stream(bytes + offset, length, NULL);
FrameSequence* frameSequence = FrameSequence::create(&stream);
env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0);
return createJavaFrameSequence(env, frameSequence);
}
static jobject nativeDecodeByteBuffer(JNIEnv* env, jobject clazz,
jobject buf, jint offset, jint limit) {
jobject globalBuf = env->NewGlobalRef(buf);
JavaVM* vm;
env->GetJavaVM(&vm);
MemoryStream stream(
(reinterpret_cast<uint8_t*>(
env->GetDirectBufferAddress(globalBuf))) + offset,
limit,
globalBuf);
FrameSequence* frameSequence = FrameSequence::create(&stream);
jobject finalSequence = createJavaFrameSequence(env, frameSequence);
return finalSequence;
}
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
jobject istream, jbyteArray byteArray) {
JavaInputStream stream(env, istream, byteArray);
FrameSequence* frameSequence = FrameSequence::create(&stream);
return createJavaFrameSequence(env, frameSequence);
}
static void nativeDestroyFrameSequence(JNIEnv* env, jobject clazz,
jlong frameSequenceLong) {
FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong);
jobject buf = frameSequence->getRawByteBuffer();
if (buf != NULL) {
env->DeleteGlobalRef(buf);
}
delete frameSequence;
}
static jlong nativeCreateState(JNIEnv* env, jobject clazz, jlong frameSequenceLong) {
FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong);
FrameSequenceState* state = frameSequence->createState();
return reinterpret_cast<jlong>(state);
}
////////////////////////////////////////////////////////////////////////////////
// Frame sequence state
////////////////////////////////////////////////////////////////////////////////
static void nativeDestroyState(
JNIEnv* env, jobject clazz, jlong frameSequenceStateLong) {
FrameSequenceState* frameSequenceState =
reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong);
delete frameSequenceState;
}
static jlong JNICALL nativeGetFrame(
JNIEnv* env, jobject clazz, jlong frameSequenceStateLong, jint frameNr,
jobject bitmap, jint previousFrameNr) {
FrameSequenceState* frameSequenceState =
reinterpret_cast<FrameSequenceState*>(frameSequenceStateLong);
int ret;
AndroidBitmapInfo info;
void* pixels;
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
jniThrowException(env, ILLEGAL_STATE_EXEPTION,
"Couldn't get info from Bitmap ");
return 0;
}
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
jniThrowException(env, ILLEGAL_STATE_EXEPTION,
"Bitmap pixels couldn't be locked");
return 0;
}
int pixelStride = info.stride >> 2;
jlong delayMs = frameSequenceState->drawFrame(frameNr,
(Color8888*) pixels, pixelStride, previousFrameNr);
AndroidBitmap_unlockPixels(env, bitmap);
return delayMs;
}
static JNINativeMethod gMethods[] = {
{ "nativeDecodeByteArray",
"([BII)L" JNI_PACKAGE "/FrameSequence;",
(void*) nativeDecodeByteArray
},
{ "nativeDecodeByteBuffer",
"(Ljava/nio/ByteBuffer;II)L" JNI_PACKAGE "/FrameSequence;",
(void*) nativeDecodeByteBuffer
},
{ "nativeDecodeStream",
"(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;",
(void*) nativeDecodeStream
},
{ "nativeDestroyFrameSequence",
"(J)V",
(void*) nativeDestroyFrameSequence
},
{ "nativeCreateState",
"(J)J",
(void*) nativeCreateState
},
{ "nativeGetFrame",
"(JILandroid/graphics/Bitmap;I)J",
(void*) nativeGetFrame
},
{ "nativeDestroyState",
"(J)V",
(void*) nativeDestroyState
},
};
jint FrameSequence_OnLoad(JNIEnv* env) {
// Get jclass with env->FindClass.
// Register methods with env->RegisterNatives.
gFrameSequenceClassInfo.clazz = env->FindClass(JNI_PACKAGE "/FrameSequence");
if (!gFrameSequenceClassInfo.clazz) {
ALOGW("Failed to find " JNI_PACKAGE "/FrameSequence");
return -1;
}
gFrameSequenceClassInfo.clazz = (jclass)env->NewGlobalRef(gFrameSequenceClassInfo.clazz);
gFrameSequenceClassInfo.ctor = env->GetMethodID(gFrameSequenceClassInfo.clazz, "<init>", "(JIIZII)V");
if (!gFrameSequenceClassInfo.ctor) {
ALOGW("Failed to find constructor for FrameSequence - was it stripped?");
return -1;
}
return env->RegisterNatives(gFrameSequenceClassInfo.clazz, gMethods, METHOD_COUNT(gMethods));
}