1 #include "CreateJavaOutputStreamAdaptor.h"
2 #include "FrontBufferedStream.h"
3 #include "GraphicsJNI.h"
4 #include <nativehelper/ScopedLocalRef.h>
5 #include "Movie.h"
6 #include "SkRefCnt.h"
7 #include "SkStream.h"
8 #include "Utils.h"
9 
10 #include <androidfw/Asset.h>
11 #include <androidfw/ResourceTypes.h>
12 #include <hwui/Canvas.h>
13 #include <hwui/Paint.h>
14 #include <netinet/in.h>
15 
16 static jclass       gMovie_class;
17 static jmethodID    gMovie_constructorMethodID;
18 static jfieldID     gMovie_nativeInstanceID;
19 
create_jmovie(JNIEnv * env,Movie * moov)20 jobject create_jmovie(JNIEnv* env, Movie* moov) {
21     if (NULL == moov) {
22         return NULL;
23     }
24     return env->NewObject(gMovie_class, gMovie_constructorMethodID,
25             static_cast<jlong>(reinterpret_cast<uintptr_t>(moov)));
26 }
27 
J2Movie(JNIEnv * env,jobject movie)28 static Movie* J2Movie(JNIEnv* env, jobject movie) {
29     SkASSERT(env);
30     SkASSERT(movie);
31     SkASSERT(env->IsInstanceOf(movie, gMovie_class));
32     Movie* m = (Movie*)env->GetLongField(movie, gMovie_nativeInstanceID);
33     SkASSERT(m);
34     return m;
35 }
36 
37 ///////////////////////////////////////////////////////////////////////////////
38 
movie_width(JNIEnv * env,jobject movie)39 static jint movie_width(JNIEnv* env, jobject movie) {
40     NPE_CHECK_RETURN_ZERO(env, movie);
41     return static_cast<jint>(J2Movie(env, movie)->width());
42 }
43 
movie_height(JNIEnv * env,jobject movie)44 static jint movie_height(JNIEnv* env, jobject movie) {
45     NPE_CHECK_RETURN_ZERO(env, movie);
46     return static_cast<jint>(J2Movie(env, movie)->height());
47 }
48 
movie_isOpaque(JNIEnv * env,jobject movie)49 static jboolean movie_isOpaque(JNIEnv* env, jobject movie) {
50     NPE_CHECK_RETURN_ZERO(env, movie);
51     return J2Movie(env, movie)->isOpaque() ? JNI_TRUE : JNI_FALSE;
52 }
53 
movie_duration(JNIEnv * env,jobject movie)54 static jint movie_duration(JNIEnv* env, jobject movie) {
55     NPE_CHECK_RETURN_ZERO(env, movie);
56     return static_cast<jint>(J2Movie(env, movie)->duration());
57 }
58 
movie_setTime(JNIEnv * env,jobject movie,jint ms)59 static jboolean movie_setTime(JNIEnv* env, jobject movie, jint ms) {
60     NPE_CHECK_RETURN_ZERO(env, movie);
61     return J2Movie(env, movie)->setTime(ms) ? JNI_TRUE : JNI_FALSE;
62 }
63 
movie_draw(JNIEnv * env,jobject movie,jlong canvasHandle,jfloat fx,jfloat fy,jlong paintHandle)64 static void movie_draw(JNIEnv* env, jobject movie, jlong canvasHandle,
65                        jfloat fx, jfloat fy, jlong paintHandle) {
66     NPE_CHECK_RETURN_VOID(env, movie);
67 
68     android::Canvas* c = reinterpret_cast<android::Canvas*>(canvasHandle);
69     const android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
70 
71     // Canvas should never be NULL. However paint is an optional parameter and
72     // therefore may be NULL.
73     SkASSERT(c != NULL);
74 
75     Movie* m = J2Movie(env, movie);
76     const SkBitmap& b = m->bitmap();
77     sk_sp<android::Bitmap> wrapper = android::Bitmap::createFrom(b.info(), *b.pixelRef());
78     c->drawBitmap(*wrapper, fx, fy, p);
79 }
80 
movie_decodeAsset(JNIEnv * env,jobject clazz,jlong native_asset)81 static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) {
82     android::Asset* asset = reinterpret_cast<android::Asset*>(native_asset);
83     if (asset == NULL) return NULL;
84     android::AssetStreamAdaptor stream(asset);
85     Movie* moov = Movie::DecodeStream(&stream);
86     return create_jmovie(env, moov);
87 }
88 
movie_decodeStream(JNIEnv * env,jobject clazz,jobject istream)89 static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
90 
91     NPE_CHECK_RETURN_ZERO(env, istream);
92 
93     jbyteArray byteArray = env->NewByteArray(16*1024);
94     ScopedLocalRef<jbyteArray> scoper(env, byteArray);
95     SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray);
96     if (NULL == strm) {
97         return 0;
98     }
99 
100     // Need to buffer enough input to be able to rewind as much as might be read by a decoder
101     // trying to determine the stream's format. The only decoder for movies is GIF, which
102     // will only read 6.
103     std::unique_ptr<SkStreamRewindable> bufferedStream(
104             android::skia::FrontBufferedStream::Make(std::unique_ptr<SkStream>(strm), 6));
105     SkASSERT(bufferedStream.get() != NULL);
106 
107     Movie* moov = Movie::DecodeStream(bufferedStream.get());
108     return create_jmovie(env, moov);
109 }
110 
movie_decodeByteArray(JNIEnv * env,jobject clazz,jbyteArray byteArray,jint offset,jint length)111 static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz,
112                                      jbyteArray byteArray,
113                                      jint offset, jint length) {
114 
115     NPE_CHECK_RETURN_ZERO(env, byteArray);
116 
117     int totalLength = env->GetArrayLength(byteArray);
118     if ((offset | length) < 0 || offset + length > totalLength) {
119         doThrowAIOOBE(env);
120         return 0;
121     }
122 
123     AutoJavaByteArray   ar(env, byteArray);
124     Movie* moov = Movie::DecodeMemory(ar.ptr() + offset, length);
125     return create_jmovie(env, moov);
126 }
127 
movie_destructor(JNIEnv * env,jobject,jlong movieHandle)128 static void movie_destructor(JNIEnv* env, jobject, jlong movieHandle) {
129     Movie* movie = (Movie*) movieHandle;
130     delete movie;
131 }
132 
133 //////////////////////////////////////////////////////////////////////////////////////////////
134 
135 static const JNINativeMethod gMethods[] = {
136     {   "width",    "()I",  (void*)movie_width  },
137     {   "height",   "()I",  (void*)movie_height  },
138     {   "isOpaque", "()Z",  (void*)movie_isOpaque  },
139     {   "duration", "()I",  (void*)movie_duration  },
140     {   "setTime",  "(I)Z", (void*)movie_setTime  },
141     {   "nDraw",    "(JFFJ)V",
142                             (void*)movie_draw  },
143     { "nativeDecodeAsset", "(J)Landroid/graphics/Movie;",
144                             (void*)movie_decodeAsset },
145     { "nativeDecodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
146                             (void*)movie_decodeStream },
147     { "nativeDestructor","(J)V", (void*)movie_destructor },
148     { "decodeByteArray", "([BII)Landroid/graphics/Movie;",
149                             (void*)movie_decodeByteArray },
150 };
151 
register_android_graphics_Movie(JNIEnv * env)152 int register_android_graphics_Movie(JNIEnv* env)
153 {
154     gMovie_class = android::FindClassOrDie(env, "android/graphics/Movie");
155     gMovie_class = android::MakeGlobalRefOrDie(env, gMovie_class);
156 
157     gMovie_constructorMethodID = android::GetMethodIDOrDie(env, gMovie_class, "<init>", "(J)V");
158 
159     gMovie_nativeInstanceID = android::GetFieldIDOrDie(env, gMovie_class, "mNativeMovie", "J");
160 
161     return android::RegisterMethodsOrDie(env, "android/graphics/Movie", gMethods, NELEM(gMethods));
162 }
163