From a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Mon, 6 Jan 2014 12:43:42 -0800 Subject: Import FrameSequence Change-Id: I09b668925366a22e8e7e80e4abeae24b3a98c639 (cherry picked from commit a1265c3d8a20e805e0c45083d5c7d728d4b70009) --- framesequence/Android.mk | 26 ++ framesequence/AndroidManifest.xml | 19 ++ framesequence/build.xml | 115 +++++++ framesequence/jni/Android.mk | 41 +++ framesequence/jni/Application.mk | 6 + framesequence/jni/BitmapDecoderJNI.cpp | 47 +++ framesequence/jni/Color.h | 31 ++ framesequence/jni/FrameSequence.cpp | 24 ++ framesequence/jni/FrameSequence.h | 55 ++++ framesequence/jni/FrameSequenceJNI.cpp | 165 ++++++++++ framesequence/jni/FrameSequenceJNI.h | 24 ++ framesequence/jni/FrameSequence_gif.cpp | 345 +++++++++++++++++++++ framesequence/jni/FrameSequence_gif.h | 84 +++++ framesequence/jni/JNIHelpers.cpp | 33 ++ framesequence/jni/JNIHelpers.h | 29 ++ framesequence/jni/Registry.cpp | 48 +++ framesequence/jni/Registry.h | 49 +++ framesequence/jni/Stream.cpp | 128 ++++++++ framesequence/jni/Stream.h | 86 +++++ framesequence/jni/utils/log.h | 274 ++++++++++++++++ framesequence/jni/utils/math.h | 30 ++ framesequence/project.properties | 15 + framesequence/samples/RastermillSamples/Android.mk | 40 +++ .../samples/RastermillSamples/AndroidManifest.xml | 26 ++ framesequence/samples/RastermillSamples/build.xml | 99 ++++++ .../samples/RastermillSamples/proguard.flags | 3 + .../samples/RastermillSamples/project.properties | 14 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes .../res/layout/basic_test_activity.xml | 38 +++ .../samples/RastermillSamples/res/raw/animated.gif | Bin 0 -> 34978 bytes .../RastermillSamples/res/values/strings.xml | 12 + .../RastermillSamples/res/values/styles.xml | 7 + .../rastermill/samples/AnimatedGifTest.java | 66 ++++ .../android/rastermill/samples/SamplesList.java | 61 ++++ .../android/support/rastermill/FrameSequence.java | 135 ++++++++ .../support/rastermill/FrameSequenceDrawable.java | 246 +++++++++++++++ 38 files changed, 2421 insertions(+) create mode 100644 framesequence/Android.mk create mode 100644 framesequence/AndroidManifest.xml create mode 100644 framesequence/build.xml create mode 100644 framesequence/jni/Android.mk create mode 100644 framesequence/jni/Application.mk create mode 100644 framesequence/jni/BitmapDecoderJNI.cpp create mode 100644 framesequence/jni/Color.h create mode 100644 framesequence/jni/FrameSequence.cpp create mode 100644 framesequence/jni/FrameSequence.h create mode 100644 framesequence/jni/FrameSequenceJNI.cpp create mode 100644 framesequence/jni/FrameSequenceJNI.h create mode 100644 framesequence/jni/FrameSequence_gif.cpp create mode 100644 framesequence/jni/FrameSequence_gif.h create mode 100644 framesequence/jni/JNIHelpers.cpp create mode 100644 framesequence/jni/JNIHelpers.h create mode 100644 framesequence/jni/Registry.cpp create mode 100644 framesequence/jni/Registry.h create mode 100644 framesequence/jni/Stream.cpp create mode 100644 framesequence/jni/Stream.h create mode 100644 framesequence/jni/utils/log.h create mode 100644 framesequence/jni/utils/math.h create mode 100644 framesequence/project.properties create mode 100644 framesequence/samples/RastermillSamples/Android.mk create mode 100644 framesequence/samples/RastermillSamples/AndroidManifest.xml create mode 100644 framesequence/samples/RastermillSamples/build.xml create mode 100644 framesequence/samples/RastermillSamples/proguard.flags create mode 100644 framesequence/samples/RastermillSamples/project.properties create mode 100644 framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png create mode 100644 framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png create mode 100644 framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png create mode 100644 framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml create mode 100644 framesequence/samples/RastermillSamples/res/raw/animated.gif create mode 100644 framesequence/samples/RastermillSamples/res/values/strings.xml create mode 100644 framesequence/samples/RastermillSamples/res/values/styles.xml create mode 100644 framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/AnimatedGifTest.java create mode 100644 framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/SamplesList.java create mode 100644 framesequence/src/android/support/rastermill/FrameSequence.java create mode 100644 framesequence/src/android/support/rastermill/FrameSequenceDrawable.java (limited to 'framesequence') diff --git a/framesequence/Android.mk b/framesequence/Android.mk new file mode 100644 index 0000000..efce18d --- /dev/null +++ b/framesequence/Android.mk @@ -0,0 +1,26 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := android-common-framesequence +#LOCAL_SDK_VERSION := 8 +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +include $(BUILD_STATIC_JAVA_LIBRARY) + +include $(call all-makefiles-under, $(LOCAL_PATH)) \ No newline at end of file diff --git a/framesequence/AndroidManifest.xml b/framesequence/AndroidManifest.xml new file mode 100644 index 0000000..f815643 --- /dev/null +++ b/framesequence/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/framesequence/build.xml b/framesequence/build.xml new file mode 100644 index 0000000..b977ef7 --- /dev/null +++ b/framesequence/build.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framesequence/jni/Android.mk b/framesequence/jni/Android.mk new file mode 100644 index 0000000..7cc0af6 --- /dev/null +++ b/framesequence/jni/Android.mk @@ -0,0 +1,41 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +## Main library + +LOCAL_SHARED_LIBRARIES += liblog libjnigraphics +LOCAL_STATIC_LIBRARIES += libgif + +LOCAL_C_INCLUDES := \ + external/giflib + +LOCAL_MODULE := libframesequence +LOCAL_SRC_FILES := \ + BitmapDecoderJNI.cpp \ + FrameSequence.cpp \ + FrameSequenceJNI.cpp \ + FrameSequence_gif.cpp \ + JNIHelpers.cpp \ + Registry.cpp \ + Stream.cpp + +LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wno-unused-variable -Wno-overloaded-virtual +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_SHARED_LIBRARY) diff --git a/framesequence/jni/Application.mk b/framesequence/jni/Application.mk new file mode 100644 index 0000000..eb51358 --- /dev/null +++ b/framesequence/jni/Application.mk @@ -0,0 +1,6 @@ +APP_PLATFORM := android-8 +APP_ABI := armeabi-v7a +LOCAL_ARM_NEON=true +ARCH_ARM_HAVE_NEON=true +# TODO: Have libjpeg do this +APP_CFLAGS := -D__ARM_HAVE_NEON=1 diff --git a/framesequence/jni/BitmapDecoderJNI.cpp b/framesequence/jni/BitmapDecoderJNI.cpp new file mode 100644 index 0000000..5fe04b4 --- /dev/null +++ b/framesequence/jni/BitmapDecoderJNI.cpp @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#define LOG_TAG "FancyDecoding" + +#include +#include +#include +#include "FrameSequenceJNI.h" +#include "JNIHelpers.h" +#include "Stream.h" +#include "utils/log.h" + +void throwException(JNIEnv* env, const char* error) { + jclass clazz = env->FindClass("java/lang/RuntimeException"); + env->ThrowNew(clazz, error); +} + +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + if (FrameSequence_OnLoad(env)) { + ALOGE("Failed to load FrameSequence"); + return -1; + } + if (JavaStream_OnLoad(env)) { + ALOGE("Failed to load JavaStream"); + return -1; + } + + return JNI_VERSION_1_6; +} diff --git a/framesequence/jni/Color.h b/framesequence/jni/Color.h new file mode 100644 index 0000000..e49c64a --- /dev/null +++ b/framesequence/jni/Color.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_COLOR_H +#define RASTERMILL_COLOR_H + +#include + +typedef uint32_t Color8888; + +static const Color8888 COLOR_8888_ALPHA_MASK = 0xff000000; // TODO: handle endianness +static const Color8888 TRANSPARENT = 0x0; + +// TODO: handle endianness +#define ARGB_TO_COLOR8888(a, r, g, b) \ + ((a) << 24 | (b) << 16 | (g) << 8 | (r)) + +#endif // RASTERMILL_COLOR_H diff --git a/framesequence/jni/FrameSequence.cpp b/framesequence/jni/FrameSequence.cpp new file mode 100644 index 0000000..5c34425 --- /dev/null +++ b/framesequence/jni/FrameSequence.cpp @@ -0,0 +1,24 @@ +/* + * 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 "FrameSequence.h" + +#include "Registry.h" + +FrameSequence* FrameSequence::create(Stream* stream) { + const RegistryEntry* entry = Registry::Find(stream); + return entry ? entry->createFrameSequence(stream) : 0; +} diff --git a/framesequence/jni/FrameSequence.h b/framesequence/jni/FrameSequence.h new file mode 100644 index 0000000..781b7c6 --- /dev/null +++ b/framesequence/jni/FrameSequence.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_FRAME_SEQUENCE_H +#define RASTERMILL_FRAME_SEQUENCE_H + +#include "Stream.h" +#include "Color.h" + +class FrameSequenceState { +public: + /** + * Produces a frame of animation in the output buffer, drawing (at minimum) the delta since + * previousFrameNr (the current contents of the buffer), or from scratch if previousFrameNr is + * negative + * + * Returns frame's delay time in milliseconds. + */ + virtual long drawFrame(int frameNr, + Color8888* outputPtr, int outputPixelStride, int previousFrameNr) = 0; + virtual ~FrameSequenceState() {} +}; + +class FrameSequence { +public: + /** + * Creates a FrameSequence using data from the data stream + * + * Type determined by header information in the stream + */ + static FrameSequence* create(Stream* stream); + + virtual ~FrameSequence() {} + virtual int getWidth() const = 0; + virtual int getHeight() const = 0; + virtual int getFrameCount() const = 0; + virtual bool isOpaque() const = 0; + + virtual FrameSequenceState* createState() const = 0; +}; + +#endif //RASTERMILL_FRAME_SEQUENCE_H diff --git a/framesequence/jni/FrameSequenceJNI.cpp b/framesequence/jni/FrameSequenceJNI.cpp new file mode 100644 index 0000000..90d0465 --- /dev/null +++ b/framesequence/jni/FrameSequenceJNI.cpp @@ -0,0 +1,165 @@ +/* + * 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 +#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(frameSequence), + frameSequence->getWidth(), + frameSequence->getHeight(), + frameSequence->getFrameCount(), + frameSequence->isOpaque()); +} + +static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz, + jbyteArray byteArray, jint offset, jint length) { + jbyte* bytes = reinterpret_cast(env->GetPrimitiveArrayCritical(byteArray, NULL)); + if (bytes == NULL) { + jniThrowException(env, ILLEGAL_STATE_EXEPTION, + "couldn't read array bytes"); + return NULL; + } + bytes += offset; + MemoryStream stream(bytes, length); + FrameSequence* frameSequence = FrameSequence::create(&stream); + env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0); + return createJavaFrameSequence(env, frameSequence); +} + +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, + jint frameSequenceInt) { + FrameSequence* frameSequence = reinterpret_cast(frameSequenceInt); + delete frameSequence; +} + +static jint nativeCreateState(JNIEnv* env, jobject clazz, jint frameSequenceInt) { + FrameSequence* frameSequence = reinterpret_cast(frameSequenceInt); + FrameSequenceState* state = frameSequence->createState(); + return reinterpret_cast(state); +} + +//////////////////////////////////////////////////////////////////////////////// +// Frame sequence state +//////////////////////////////////////////////////////////////////////////////// + +static void nativeDestroyState( + JNIEnv* env, jobject clazz, jint frameSequenceStateInt) { + FrameSequenceState* frameSequenceState = + reinterpret_cast(frameSequenceStateInt); + delete frameSequenceState; +} + +static jlong JNICALL nativeGetFrame( + JNIEnv* env, jobject clazz, jint frameSequenceStateInt, jint frameNr, + jobject bitmap, jint previousFrameNr) { + FrameSequenceState* frameSequenceState = + reinterpret_cast(frameSequenceStateInt); + 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 + }, + { "nativeDecodeStream", + "(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;", + (void*) nativeDecodeStream + }, + { "nativeDestroyFrameSequence", + "(I)V", + (void*) nativeDestroyFrameSequence + }, + { "nativeCreateState", + "(I)I", + (void*) nativeCreateState + }, + { "nativeGetFrame", + "(IILandroid/graphics/Bitmap;I)J", + (void*) nativeGetFrame + }, + { "nativeDestroyFrameSequence", + "(I)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, "", "(IIIIZ)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)); +} diff --git a/framesequence/jni/FrameSequenceJNI.h b/framesequence/jni/FrameSequenceJNI.h new file mode 100644 index 0000000..a52df8a --- /dev/null +++ b/framesequence/jni/FrameSequenceJNI.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_FRAMESEQUENCE_JNI +#define RASTERMILL_FRAMESEQUENCE_JNI + +#include + +jint FrameSequence_OnLoad(JNIEnv* env); + +#endif // RASTERMILL_FRAMESEQUENCE_JNI diff --git a/framesequence/jni/FrameSequence_gif.cpp b/framesequence/jni/FrameSequence_gif.cpp new file mode 100644 index 0000000..e9f3ace --- /dev/null +++ b/framesequence/jni/FrameSequence_gif.cpp @@ -0,0 +1,345 @@ +/* + * 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 +#include "JNIHelpers.h" +#include "utils/log.h" +#include "utils/math.h" + +#include "FrameSequence_gif.h" + +#define GIF_DEBUG 0 + +// These constants are chosen to imitate common browser behavior +// Note that 0 delay is undefined behavior in the gif standard +static const long MIN_DELAY_MS = 20; +static const long DEFAULT_DELAY_MS = 100; + +static int streamReader(GifFileType* fileType, GifByteType* out, int size) { + Stream* stream = (Stream*) fileType->UserData; + return (int) stream->read(out, size); +} + +static Color8888 gifColorToColor8888(const GifColorType& color) { + return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue); +} + +static long getDelayMs(GraphicsControlBlock& gcb) { + long delayMs = gcb.DelayTime * 10; + if (delayMs < MIN_DELAY_MS) { + return DEFAULT_DELAY_MS; + } + return delayMs; +} + +static bool willBeCleared(const GraphicsControlBlock& gcb) { + return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS; +} + +//////////////////////////////////////////////////////////////////////////////// +// Frame sequence +//////////////////////////////////////////////////////////////////////////////// + +FrameSequence_gif::FrameSequence_gif(Stream* stream) : + mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) { + mGif = DGifOpen(stream, streamReader, NULL); + if (!mGif) { + ALOGW("Gif load failed"); + return; + } + + if (DGifSlurp(mGif) != GIF_OK) { + ALOGW("Gif slurp failed"); + DGifCloseFile(mGif); + mGif = NULL; + return; + } + + long durationMs = 0; + int lastUnclearedFrame = -1; + mPreservedFrames = new bool[mGif->ImageCount]; + mRestoringFrames = new int[mGif->ImageCount]; + + GraphicsControlBlock gcb; + for (int i = 0; i < mGif->ImageCount; i++) { + const SavedImage& image = mGif->SavedImages[i]; + DGifSavedExtensionToGCB(mGif, i, &gcb); + + // timing + durationMs += getDelayMs(gcb); + + // preserve logic + mPreservedFrames[i] = false; + mRestoringFrames[i] = -1; + if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) { + mPreservedFrames[lastUnclearedFrame] = true; + mRestoringFrames[i] = lastUnclearedFrame; + } + if (!willBeCleared(gcb)) { + lastUnclearedFrame = i; + } + } + +#if GIF_DEBUG + ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld", + mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs); + for (int i = 0; i < mGif->ImageCount; i++) { + DGifSavedExtensionToGCB(mGif, i, &gcb); + ALOGD(" Frame %d - must preserve %d, restore point %d, trans color %d", + i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor); + } +#endif + + if (mGif->SColorMap) { + // calculate bg color + GraphicsControlBlock gcb; + DGifSavedExtensionToGCB(mGif, 0, &gcb); + if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) { + mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]); + } + } +} + +FrameSequence_gif::~FrameSequence_gif() { + if (mGif) { + DGifCloseFile(mGif); + } + delete[] mPreservedFrames; + delete[] mRestoringFrames; +} + +FrameSequenceState* FrameSequence_gif::createState() const { + return new FrameSequenceState_gif(*this); +} + +//////////////////////////////////////////////////////////////////////////////// +// draw helpers +//////////////////////////////////////////////////////////////////////////////// + +// return true if area of 'target' is completely covers area of 'covered' +static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) { + return target.Left <= covered.Left + && covered.Left + covered.Width <= target.Left + target.Width + && target.Top <= covered.Top + && covered.Top + covered.Height <= target.Top + target.Height; +} + +static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap, + int transparent, int width) { + for (; width > 0; width--, src++, dst++) { + if (*src != transparent) { + *dst = gifColorToColor8888(cmap->Colors[*src]); + } + } +} + +static void setLineColor(Color8888* dst, Color8888 color, int width) { + for (; width > 0; width--, dst++) { + *dst = color; + } +} + +static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight, + GifWord& copyWidth, GifWord& copyHeight) { + copyWidth = imageDesc.Width; + if (imageDesc.Left + copyWidth > maxWidth) { + copyWidth = maxWidth - imageDesc.Left; + } + copyHeight = imageDesc.Height; + if (imageDesc.Top + copyHeight > maxHeight) { + copyHeight = maxHeight - imageDesc.Top; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Frame sequence state +//////////////////////////////////////////////////////////////////////////////// + +FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) : + mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) { +} + +FrameSequenceState_gif::~FrameSequenceState_gif() { + delete[] mPreserveBuffer; +} + +void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) { + if (frameNr == mPreserveBufferFrame) return; + + mPreserveBufferFrame = frameNr; + const int width = mFrameSequence.getWidth(); + const int height = mFrameSequence.getHeight(); + if (!mPreserveBuffer) { + mPreserveBuffer = new Color8888[width * height]; + } + for (int y = 0; y < height; y++) { + memcpy(mPreserveBuffer + width * y, + outputPtr + outputPixelStride * y, + width * 4); + } +} + +void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) { + const int width = mFrameSequence.getWidth(); + const int height = mFrameSequence.getHeight(); + if (!mPreserveBuffer) { + ALOGD("preserve buffer not allocated! ah!"); + return; + } + for (int y = 0; y < height; y++) { + memcpy(outputPtr + outputPixelStride * y, + mPreserveBuffer + width * y, + width * 4); + } +} + +long FrameSequenceState_gif::drawFrame(int frameNr, + Color8888* outputPtr, int outputPixelStride, int previousFrameNr) { + + GifFileType* gif = mFrameSequence.getGif(); + if (!gif) { + ALOGD("Cannot drawFrame, mGif is NULL"); + return -1; + } + +#if GIF_DEBUG + ALOGD(" drawFrame on %p nr %d on addr %p, previous frame nr %d", + this, frameNr, outputPtr, previousFrameNr); +#endif + + const int height = mFrameSequence.getHeight(); + const int width = mFrameSequence.getWidth(); + + GraphicsControlBlock gcb; + + int start = max(previousFrameNr + 1, 0); + + for (int i = max(start - 1, 0); i < frameNr; i++) { + int neededPreservedFrame = mFrameSequence.getRestoringFrame(i); + if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) { +#if GIF_DEBUG + ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch", + i, neededPreservedFrame, mPreserveBufferFrame); +#endif + start = 0; + } + } + + for (int i = start; i <= frameNr; i++) { + DGifSavedExtensionToGCB(gif, i, &gcb); + const SavedImage& frame = gif->SavedImages[i]; + +#if GIF_DEBUG + bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; + ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)", + frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime); +#endif + if (i == 0) { + //clear bitmap + Color8888 bgColor = mFrameSequence.getBackgroundColor(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + outputPtr[y * outputPixelStride + x] = bgColor; + } + } + } else { + GraphicsControlBlock prevGcb; + DGifSavedExtensionToGCB(gif, i - 1, &prevGcb); + const SavedImage& prevFrame = gif->SavedImages[i - 1]; + bool prevFrameDisposed = willBeCleared(prevGcb); + + bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR; + bool prevFrameCompletelyCovered = newFrameOpaque + && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc); + + if (prevFrameDisposed && !prevFrameCompletelyCovered) { + switch (prevGcb.DisposalMode) { + case DISPOSE_BACKGROUND: { + Color8888* dst = outputPtr + prevFrame.ImageDesc.Left + + prevFrame.ImageDesc.Top * outputPixelStride; + + GifWord copyWidth, copyHeight; + getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight); + for (; copyHeight > 0; copyHeight--) { + setLineColor(dst, TRANSPARENT, copyWidth); + dst += outputPixelStride; + } + } break; + case DISPOSE_PREVIOUS: { + restorePreserveBuffer(outputPtr, outputPixelStride); + } break; + } + } + + if (mFrameSequence.getPreservedFrame(i - 1)) { + // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so + // we preserve it + savePreserveBuffer(outputPtr, outputPixelStride, i - 1); + } + } + + bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND + || gcb.DisposalMode == DISPOSE_PREVIOUS; + if (i == frameNr || !willBeCleared) { + const ColorMapObject* cmap = gif->SColorMap; + if (frame.ImageDesc.ColorMap) { + cmap = frame.ImageDesc.ColorMap; + } + + if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + ALOGW("Warning: potentially corrupt color map"); + } + + const unsigned char* src = (unsigned char*)frame.RasterBits; + Color8888* dst = outputPtr + frame.ImageDesc.Left + + frame.ImageDesc.Top * outputPixelStride; + GifWord copyWidth, copyHeight; + getCopySize(frame.ImageDesc, width, height, copyWidth, copyHeight); + for (; copyHeight > 0; copyHeight--) { + copyLine(dst, src, cmap, gcb.TransparentColor, copyWidth); + src += frame.ImageDesc.Width; + dst += outputPixelStride; + } + } + } + + return getDelayMs(gcb); +} + +//////////////////////////////////////////////////////////////////////////////// +// Registry +//////////////////////////////////////////////////////////////////////////////// + +#include "Registry.h" + +static bool isGif(void* header, int header_size) { + return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN) + || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN) + || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN); +} + +static FrameSequence* createFramesequence(Stream* stream) { + return new FrameSequence_gif(stream); +} + +static RegistryEntry gEntry = { + GIF_STAMP_LEN, + isGif, + createFramesequence, + NULL, +}; +static Registry gRegister(gEntry); + diff --git a/framesequence/jni/FrameSequence_gif.h b/framesequence/jni/FrameSequence_gif.h new file mode 100644 index 0000000..fbc4959 --- /dev/null +++ b/framesequence/jni/FrameSequence_gif.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_FRAMESQUENCE_GIF_H +#define RASTERMILL_FRAMESQUENCE_GIF_H + +#include "config.h" +#include "gif_lib.h" + +#include "Stream.h" +#include "Color.h" +#include "FrameSequence.h" + +class FrameSequence_gif : public FrameSequence { +public: + FrameSequence_gif(Stream* stream); + virtual ~FrameSequence_gif(); + + virtual int getWidth() const { + return mGif ? mGif->SWidth : 0; + } + + virtual int getHeight() const { + return mGif ? mGif->SHeight : 0; + } + + virtual int getFrameCount() const { + return mGif ? mGif->ImageCount : 0; + } + + virtual bool isOpaque() const { + return (mBgColor & COLOR_8888_ALPHA_MASK) == COLOR_8888_ALPHA_MASK; + } + + virtual FrameSequenceState* createState() const; + + GifFileType* getGif() const { return mGif; } + Color8888 getBackgroundColor() const { return mBgColor; } + bool getPreservedFrame(int frameIndex) const { return mPreservedFrames[frameIndex]; } + int getRestoringFrame(int frameIndex) const { return mRestoringFrames[frameIndex]; } + +private: + GifFileType* mGif; + Color8888 mBgColor; + + // array of bool per frame - if true, frame data is used by a later DISPOSE_PREVIOUS frame + bool* mPreservedFrames; + + // array of ints per frame - if >= 0, points to the index of the preserve that frame needs + int* mRestoringFrames; +}; + +class FrameSequenceState_gif : public FrameSequenceState { +public: + FrameSequenceState_gif(const FrameSequence_gif& frameSequence); + virtual ~FrameSequenceState_gif(); + + // returns frame's delay time in ms + virtual long drawFrame(int frameNr, + Color8888* outputPtr, int outputPixelStride, int previousFrameNr); + +private: + void savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr); + void restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride); + + const FrameSequence_gif& mFrameSequence; + Color8888* mPreserveBuffer; + int mPreserveBufferFrame; +}; + +#endif //RASTERMILL_FRAMESQUENCE_GIF_H diff --git a/framesequence/jni/JNIHelpers.cpp b/framesequence/jni/JNIHelpers.cpp new file mode 100644 index 0000000..dd0c818 --- /dev/null +++ b/framesequence/jni/JNIHelpers.cpp @@ -0,0 +1,33 @@ +/* + * 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 "JNIHelpers.h" +#include "utils/log.h" + +void jniThrowException(JNIEnv* env, const char* className, const char* msg) { + jclass clazz = env->FindClass(className); + if (!clazz) { + ALOGE("Unable to find exception class %s", className); + /* ClassNotFoundException now pending */ + return; + } + + if (env->ThrowNew(clazz, msg) != JNI_OK) { + ALOGE("Failed throwing '%s' '%s'", className, msg); + /* an exception, most likely OOM, will now be pending */ + } + env->DeleteLocalRef(clazz); +} diff --git a/framesequence/jni/JNIHelpers.h b/framesequence/jni/JNIHelpers.h new file mode 100644 index 0000000..bb850d2 --- /dev/null +++ b/framesequence/jni/JNIHelpers.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_JNIHELPERS_H +#define RASTERMILL_JNIHELPERS_H + +#include + +#define METHOD_COUNT(methodArray) (sizeof(methodArray) / sizeof(methodArray[0])) + +#define ILLEGAL_STATE_EXEPTION "java/lang/IllegalStateException" + +void jniThrowException(JNIEnv* env, const char* className, const char* msg); + + +#endif //RASTERMILL_JNIHELPERS_H diff --git a/framesequence/jni/Registry.cpp b/framesequence/jni/Registry.cpp new file mode 100644 index 0000000..125ac66 --- /dev/null +++ b/framesequence/jni/Registry.cpp @@ -0,0 +1,48 @@ +/* + * 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 "Registry.h" + +#include "Stream.h" + +static Registry* gHead = 0; +static int gHeaderBytesRequired = 0; + +Registry::Registry(const RegistryEntry& entry) { + mImpl = entry; + + mNext = gHead; + gHead = this; + + if (gHeaderBytesRequired < entry.requiredHeaderBytes) { + gHeaderBytesRequired = entry.requiredHeaderBytes; + } +} + +const RegistryEntry* Registry::Find(Stream* stream) { + Registry* registry = gHead; + int headerSize = gHeaderBytesRequired; + char header[headerSize]; + headerSize = stream->peek(header, headerSize); + while (registry) { + if (headerSize >= registry->mImpl.requiredHeaderBytes + && registry->mImpl.checkHeader(header, headerSize)) { + return &(registry->mImpl); + } + registry = registry->mNext; + } + return 0; +} diff --git a/framesequence/jni/Registry.h b/framesequence/jni/Registry.h new file mode 100644 index 0000000..571c611 --- /dev/null +++ b/framesequence/jni/Registry.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_REGISTRY_H +#define RASTERMILL_REGISTRY_H + +class FrameSequence; +class Decoder; +class Stream; + +struct RegistryEntry { + int requiredHeaderBytes; + bool (*checkHeader)(void* header, int header_size); + FrameSequence* (*createFrameSequence)(Stream* stream); + Decoder* (*createDecoder)(Stream* stream); +}; + +/** + * Template class for registering subclasses that can produce instances of themselves given a + * DataStream pointer. + * + * The super class / root constructable type only needs to define a single static construction + * meathod that creates an instance by iterating through all factory methods. + */ +class Registry { +public: + Registry(const RegistryEntry& entry); + + static const RegistryEntry* Find(Stream* stream); + +private: + RegistryEntry mImpl; + Registry* mNext; +}; + +#endif // RASTERMILL_REGISTRY_H diff --git a/framesequence/jni/Stream.cpp b/framesequence/jni/Stream.cpp new file mode 100644 index 0000000..b2e0c39 --- /dev/null +++ b/framesequence/jni/Stream.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#define LOG_TAG "Stream" + +#include "Stream.h" + +#include + +#include "JNIHelpers.h" +#include "utils/log.h" +#include "utils/math.h" + +static struct { + jmethodID read; + jmethodID reset; +} gInputStreamClassInfo; + +Stream::Stream() + : mPeekBuffer(0) + , mPeekSize(0) + , mPeekOffset(0) { +} + +Stream::~Stream() { + delete mPeekBuffer; +} + +size_t Stream::peek(void* buffer, size_t size) { + size_t peek_remaining = mPeekSize - mPeekOffset; + if (size > peek_remaining) { + char* old_peek = mPeekBuffer; + mPeekBuffer = new char[size]; + if (old_peek) { + memcpy(mPeekBuffer, old_peek + mPeekOffset, peek_remaining); + delete old_peek; + } + size_t read = doRead(mPeekBuffer + mPeekOffset, size - peek_remaining); + mPeekOffset = 0; + mPeekSize = peek_remaining + read; + } + size = min(size, mPeekSize - mPeekOffset); + memcpy(buffer, mPeekBuffer + mPeekOffset, size); + return size; +} + +size_t Stream::read(void* buffer, size_t size) { + size_t bytes_read = 0; + size_t peek_remaining = mPeekSize - mPeekOffset; + if (peek_remaining) { + bytes_read = min(size, peek_remaining); + memcpy(buffer, mPeekBuffer + mPeekOffset, bytes_read); + mPeekOffset += bytes_read; + if (mPeekOffset == mPeekSize) { + delete mPeekBuffer; + mPeekBuffer = 0; + mPeekOffset = 0; + mPeekSize = 0; + } + size -= bytes_read; + buffer = ((char*) buffer) + bytes_read; + } + if (size) { + bytes_read += doRead(buffer, size); + } + return bytes_read; +} + +size_t MemoryStream::doRead(void* buffer, size_t size) { + size = min(size, mRemaining); + memcpy(buffer, mBuffer, size); + mBuffer += size; + mRemaining -= size; + return size; +} + +size_t FileStream::doRead(void* buffer, size_t size) { + return fread(buffer, 1, size, mFd); +} + +size_t JavaInputStream::doRead(void* dstBuffer, size_t size) { + size_t totalBytesRead = 0; + + do { + size_t requested = min(size, mByteArrayLength); + + jint bytesRead = mEnv->CallIntMethod(mInputStream, + gInputStreamClassInfo.read, mByteArray, 0, requested); + if (mEnv->ExceptionCheck() || bytesRead < 0) { + return 0; + } + + mEnv->GetByteArrayRegion(mByteArray, 0, bytesRead, (jbyte*)dstBuffer); + dstBuffer = (char*)dstBuffer + bytesRead; + totalBytesRead += bytesRead; + size -= bytesRead; + } while (size > 0); + + return totalBytesRead; +} + +jint JavaStream_OnLoad(JNIEnv* env) { + // Skip the verbose logging on error for these, as they won't be subject + // to obfuscators or similar and are thus unlikely to ever fail + jclass inputStreamClazz = env->FindClass("java/io/InputStream"); + if (!inputStreamClazz) { + return -1; + } + gInputStreamClassInfo.read = env->GetMethodID(inputStreamClazz, "read", "([BII)I"); + gInputStreamClassInfo.reset = env->GetMethodID(inputStreamClazz, "reset", "()V"); + if (!gInputStreamClassInfo.read || !gInputStreamClassInfo.reset) { + return -1; + } + return 0; +} diff --git a/framesequence/jni/Stream.h b/framesequence/jni/Stream.h new file mode 100644 index 0000000..f8f2427 --- /dev/null +++ b/framesequence/jni/Stream.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#ifndef RASTERMILL_STREAM_H +#define RASTERMILL_STREAM_H + +#include +#include +#include + +class Stream { +public: + Stream(); + virtual ~Stream(); + + size_t peek(void* buffer, size_t size); + size_t read(void* buffer, size_t size); + +protected: + virtual size_t doRead(void* buffer, size_t size) = 0; + +private: + char* mPeekBuffer; + size_t mPeekSize; + size_t mPeekOffset; +}; + +class MemoryStream : public Stream { +public: + MemoryStream(void* buffer, size_t size) : + mBuffer((char*)buffer), + mRemaining(size) {} + +protected: + virtual size_t doRead(void* buffer, size_t size); + +private: + char* mBuffer; + size_t mRemaining; +}; + +class FileStream : public Stream { +public: + FileStream(FILE* fd) : mFd(fd) {} + +protected: + virtual size_t doRead(void* buffer, size_t size); + +private: + FILE* mFd; +}; + +class JavaInputStream : public Stream { +public: + JavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray byteArray) : + mEnv(env), + mInputStream(inputStream), + mByteArray(byteArray), + mByteArrayLength(env->GetArrayLength(byteArray)) {} + +protected: + virtual size_t doRead(void* buffer, size_t size); + +private: + JNIEnv* mEnv; + const jobject mInputStream; + const jbyteArray mByteArray; + const size_t mByteArrayLength; +}; + +jint JavaStream_OnLoad(JNIEnv* env); + +#endif //RASTERMILL_STREAM_H diff --git a/framesequence/jni/utils/log.h b/framesequence/jni/utils/log.h new file mode 100644 index 0000000..5e15f30 --- /dev/null +++ b/framesequence/jni/utils/log.h @@ -0,0 +1,274 @@ +/* + * 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. + */ + +#ifndef LOG_H_ +#define LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------- + +/* + * Normally we strip ALOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#ifndef LOG_NDEBUG +#ifdef NDEBUG +#define LOG_NDEBUG 1 +#else +#define LOG_NDEBUG 0 +#endif +#endif + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG +#define LOG_TAG "RasterMill" +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ +#ifndef ALOGV +#if LOG_NDEBUG +#define ALOGV(...) ((void)0) +#else +#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef ALOGV_IF +#if LOG_NDEBUG +#define ALOGV_IF(cond, ...) ((void)0) +#else +#define ALOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug log message using the current LOG_TAG. + */ +#ifndef ALOGD +#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGD_IF +#define ALOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info log message using the current LOG_TAG. + */ +#ifndef ALOGI +#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGI_IF +#define ALOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning log message using the current LOG_TAG. + */ +#ifndef ALOGW +#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGW_IF +#define ALOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error log message using the current LOG_TAG. + */ +#ifndef ALOGE +#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGE_IF +#define ALOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * verbose priority. + */ +#ifndef IF_ALOGV +#if LOG_NDEBUG +#define IF_ALOGV() if (false) +#else +#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) +#endif +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * debug priority. + */ +#ifndef IF_ALOGD +#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * info priority. + */ +#ifndef IF_ALOGI +#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * warn priority. + */ +#ifndef IF_ALOGW +#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * error priority. + */ +#ifndef IF_ALOGE +#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) +#endif + +// --------------------------------------------------------------------- + +/* + * Log a fatal error. If the given condition fails, this stops program + * execution like a normal assertion, but also generating the given message. + * It is NOT stripped from release builds. Note that the condition test + * is -inverted- from the normal assert() semantics. + */ +#ifndef LOG_ALWAYS_FATAL_IF +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ + : (void)0 ) +#endif + +#ifndef LOG_ALWAYS_FATAL +#define LOG_ALWAYS_FATAL(...) \ + ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) +#endif + +/* + * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that + * are stripped out of release builds. + */ +#if LOG_NDEBUG + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) ((void)0) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) ((void)0) +#endif + +#else + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) +#endif + +#endif + +/* + * Assertion that generates a log message when the assertion fails. + * Stripped out of release builds. Uses the current LOG_TAG. + */ +#ifndef ALOG_ASSERT +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) +//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif + +// --------------------------------------------------------------------- + +/* + * Basic log message macro. + * + * Example: + * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); + * + * The second argument may be NULL or "" to indicate the "global" tag. + */ +#ifndef ALOG +#define ALOG(priority, tag, ...) \ + LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to specify a number for the priority. + */ +#ifndef LOG_PRI +#define LOG_PRI(priority, tag, ...) \ + __android_log_print(priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to pass in a varargs ("args" is a va_list). + */ +#ifndef LOG_PRI_VA +#define LOG_PRI_VA(priority, tag, fmt, args) \ + __android_log_vprint(priority, NULL, tag, fmt, args) +#endif + +/* + * Conditional given a desired logging priority and tag. + */ +#ifndef IF_ALOG +#define IF_ALOG(priority, tag) \ + if (__android_log_assert(ANDROID_##priority, tag)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LOG_H_ */ diff --git a/framesequence/jni/utils/math.h b/framesequence/jni/utils/math.h new file mode 100644 index 0000000..87f100b --- /dev/null +++ b/framesequence/jni/utils/math.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef MATH_H_ +#define MATH_H_ + +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#endif /* MATH_H_ */ diff --git a/framesequence/project.properties b/framesequence/project.properties new file mode 100644 index 0000000..db721fd --- /dev/null +++ b/framesequence/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-8 +android.library=true diff --git a/framesequence/samples/RastermillSamples/Android.mk b/framesequence/samples/RastermillSamples/Android.mk new file mode 100644 index 0000000..bb8920f --- /dev/null +++ b/framesequence/samples/RastermillSamples/Android.mk @@ -0,0 +1,40 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := FrameSequenceSample + +# java dependency +LOCAL_STATIC_JAVA_LIBRARIES += android-common-framesequence + +# native dependency +ifneq (,$(TARGET_BUILD_APPS)) + LOCAL_JNI_SHARED_LIBRARIES := libframesequence +else + LOCAL_REQUIRED_MODULES := libframesequence +endif + +# proguard for framesequence library code +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +LOCAL_SDK_VERSION := 19 + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res) +LOCAL_AAPT_FLAGS := --auto-add-overlay +LOCAL_AAPT_FLAGS += --extra-packages com.android.rastermill.samples + +include $(BUILD_PACKAGE) diff --git a/framesequence/samples/RastermillSamples/AndroidManifest.xml b/framesequence/samples/RastermillSamples/AndroidManifest.xml new file mode 100644 index 0000000..b554021 --- /dev/null +++ b/framesequence/samples/RastermillSamples/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/framesequence/samples/RastermillSamples/build.xml b/framesequence/samples/RastermillSamples/build.xml new file mode 100644 index 0000000..5e55b4e --- /dev/null +++ b/framesequence/samples/RastermillSamples/build.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framesequence/samples/RastermillSamples/proguard.flags b/framesequence/samples/RastermillSamples/proguard.flags new file mode 100644 index 0000000..4acde2d --- /dev/null +++ b/framesequence/samples/RastermillSamples/proguard.flags @@ -0,0 +1,3 @@ +-keep class android.support.rastermill.** { + *; +} diff --git a/framesequence/samples/RastermillSamples/project.properties b/framesequence/samples/RastermillSamples/project.properties new file mode 100644 index 0000000..ce39f2d --- /dev/null +++ b/framesequence/samples/RastermillSamples/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-18 diff --git a/framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png b/framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png differ diff --git a/framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png b/framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png differ diff --git a/framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png b/framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png differ diff --git a/framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml b/framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml new file mode 100644 index 0000000..0b9a2df --- /dev/null +++ b/framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml @@ -0,0 +1,38 @@ + + + + + +