1 /*
2  * Copyright 2020 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 #define LOG_TAG "AImageDecoderTest"
18 
19 #include <jni.h>
20 #include <android/asset_manager.h>
21 #include <android/asset_manager_jni.h>
22 #include <android/bitmap.h>
23 #include <android/imagedecoder.h>
24 #include <android/rect.h>
25 
26 #include "NativeTestHelpers.h"
27 
28 #include <cstdlib>
29 #include <cstring>
30 #include <initializer_list>
31 
32 #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
33 
testNullDecoder(JNIEnv * env,jobject)34 static void testNullDecoder(JNIEnv* env, jobject) {
35 #pragma clang diagnostic push
36 #pragma clang diagnostic ignored "-Wnonnull"
37     ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_advanceFrame(nullptr));
38 
39     ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_rewind(nullptr));
40 
41     AImageDecoder_setInternallyHandleDisposePrevious(nullptr, true);
42     AImageDecoder_setInternallyHandleDisposePrevious(nullptr, false);
43 #pragma clang diagnostic pop
44 }
45 
testToString(JNIEnv * env,jobject)46 static void testToString(JNIEnv* env, jobject) {
47     struct {
48         int resultCode;
49         const char* string;
50     } map[] = {
51         { ANDROID_IMAGE_DECODER_SUCCESS,            "ANDROID_IMAGE_DECODER_SUCCESS" },
52         { ANDROID_IMAGE_DECODER_INCOMPLETE,         "ANDROID_IMAGE_DECODER_INCOMPLETE" },
53         { ANDROID_IMAGE_DECODER_ERROR,              "ANDROID_IMAGE_DECODER_ERROR" },
54         { ANDROID_IMAGE_DECODER_INVALID_CONVERSION, "ANDROID_IMAGE_DECODER_INVALID_CONVERSION" },
55         { ANDROID_IMAGE_DECODER_INVALID_SCALE,      "ANDROID_IMAGE_DECODER_INVALID_SCALE" },
56         { ANDROID_IMAGE_DECODER_BAD_PARAMETER,      "ANDROID_IMAGE_DECODER_BAD_PARAMETER" },
57         { ANDROID_IMAGE_DECODER_INVALID_INPUT,      "ANDROID_IMAGE_DECODER_INVALID_INPUT" },
58         { ANDROID_IMAGE_DECODER_SEEK_ERROR,         "ANDROID_IMAGE_DECODER_SEEK_ERROR" },
59         { ANDROID_IMAGE_DECODER_INTERNAL_ERROR,     "ANDROID_IMAGE_DECODER_INTERNAL_ERROR" },
60         { ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT, "ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT" },
61         { ANDROID_IMAGE_DECODER_FINISHED,           "ANDROID_IMAGE_DECODER_FINISHED" },
62         { ANDROID_IMAGE_DECODER_INVALID_STATE,      "ANDROID_IMAGE_DECODER_INVALID_STATE" },
63     };
64 
65     for (const auto& item : map) {
66         const char* str = AImageDecoder_resultToString(item.resultCode);
67         ASSERT_EQ(0, strcmp(item.string, str));
68     }
69 
70     for (int i : { ANDROID_IMAGE_DECODER_SUCCESS + 1,
71                    ANDROID_IMAGE_DECODER_INVALID_STATE - 1,
72                    2, 7, 37, 42 }) {
73         ASSERT_EQ(nullptr, AImageDecoder_resultToString(i));
74     }
75 }
76 
openAsset(JNIEnv * env,jobject,jobject jAssets,jstring jFile)77 static jlong openAsset(JNIEnv* env, jobject, jobject jAssets, jstring jFile) {
78     AAssetManager* nativeManager = AAssetManager_fromJava(env, jAssets);
79     const char* file = env->GetStringUTFChars(jFile, nullptr);
80     AAsset* asset = AAssetManager_open(nativeManager, file, AASSET_MODE_UNKNOWN);
81     if (!asset) {
82         fail(env, "Could not open %s", file);
83     } else {
84         ALOGD("Testing %s", file);
85     }
86     env->ReleaseStringUTFChars(jFile, file);
87     return reinterpret_cast<jlong>(asset);
88 }
89 
closeAsset(JNIEnv *,jobject,jlong asset)90 static void closeAsset(JNIEnv*, jobject, jlong asset) {
91     AAsset_close(reinterpret_cast<AAsset*>(asset));
92 }
93 
createFromAsset(JNIEnv * env,jobject,jlong asset)94 static jlong createFromAsset(JNIEnv* env, jobject, jlong asset) {
95     AImageDecoder* decoder = nullptr;
96     int result = AImageDecoder_createFromAAsset(reinterpret_cast<AAsset*>(asset), &decoder);
97     if (ANDROID_IMAGE_DECODER_SUCCESS != result || !decoder) {
98         fail(env, "Failed to create AImageDecoder with %s!",
99              AImageDecoder_resultToString(result));
100     }
101     return reinterpret_cast<jlong>(decoder);
102 }
103 
getWidth(JNIEnv *,jobject,jlong decoder)104 static jint getWidth(JNIEnv*, jobject, jlong decoder) {
105     const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
106     return AImageDecoderHeaderInfo_getWidth(info);
107 }
108 
getHeight(JNIEnv *,jobject,jlong decoder)109 static jint getHeight(JNIEnv*, jobject, jlong decoder) {
110     const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
111     return AImageDecoderHeaderInfo_getHeight(info);
112 }
113 
deleteDecoder(JNIEnv *,jobject,jlong decoder)114 static void deleteDecoder(JNIEnv*, jobject, jlong decoder) {
115     AImageDecoder_delete(reinterpret_cast<AImageDecoder*>(decoder));
116 }
117 
setTargetSize(JNIEnv *,jobject,jlong decoder_ptr,jint width,jint height)118 static jint setTargetSize(JNIEnv*, jobject, jlong decoder_ptr, jint width, jint height) {
119     return AImageDecoder_setTargetSize(reinterpret_cast<AImageDecoder*>(decoder_ptr),
120                                        width, height);
121 }
122 
setCrop(JNIEnv *,jobject,jlong decoder_ptr,jint left,jint top,jint right,jint bottom)123 static jint setCrop(JNIEnv*, jobject, jlong decoder_ptr, jint left, jint top,
124                     jint right, jint bottom) {
125     return AImageDecoder_setCrop(reinterpret_cast<AImageDecoder*>(decoder_ptr),
126                                  {left, top, right, bottom});
127 }
128 
decode(JNIEnv * env,jobject,jlong decoder_ptr,jobject jBitmap,jint expected)129 static void decode(JNIEnv* env, jobject, jlong decoder_ptr, jobject jBitmap, jint expected) {
130     auto* decoder = reinterpret_cast<AImageDecoder*>(decoder_ptr);
131     AndroidBitmapInfo info;
132     if (AndroidBitmap_getInfo(env, jBitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) {
133         fail(env, "Failed to getInfo on a Bitmap!");
134         return;
135     }
136 
137     void* pixels;
138     if (AndroidBitmap_lockPixels(env, jBitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
139         fail(env, "Failed to lock pixels!");
140         return;
141     }
142 
143     const int result = AImageDecoder_decodeImage(decoder, pixels, info.stride,
144                                                  info.stride * info.height);
145     if (result != expected) {
146         fail(env, "Unexpected result from AImageDecoder_decodeImage: %s",
147              AImageDecoder_resultToString(result));
148         // Don't return yet, so we can unlockPixels.
149     }
150 
151     if (AndroidBitmap_unlockPixels(env, jBitmap) != ANDROID_BITMAP_RESULT_SUCCESS) {
152         const char* msg = "Failed to unlock pixels!";
153         if (env->ExceptionCheck()) {
154             // Do not attempt to throw an Exception while one is pending.
155             ALOGE("%s", msg);
156         } else {
157             fail(env, msg);
158         }
159     }
160 }
161 
advanceFrame(JNIEnv *,jobject,jlong decoder_ptr)162 static jint advanceFrame(JNIEnv*, jobject, jlong decoder_ptr) {
163     auto* decoder = reinterpret_cast<AImageDecoder*>(decoder_ptr);
164     return AImageDecoder_advanceFrame(decoder);
165 }
166 
rewind_decoder(JNIEnv *,jobject,jlong decoder)167 static jint rewind_decoder(JNIEnv*, jobject, jlong decoder) {
168     return AImageDecoder_rewind(reinterpret_cast<AImageDecoder*>(decoder));
169 }
170 
setUnpremultipliedRequired(JNIEnv *,jobject,jlong decoder,jboolean required)171 static jint setUnpremultipliedRequired(JNIEnv*, jobject, jlong decoder, jboolean required) {
172     return AImageDecoder_setUnpremultipliedRequired(reinterpret_cast<AImageDecoder*>(decoder),
173                                                     required);
174 }
175 
setAndroidBitmapFormat(JNIEnv *,jobject,jlong decoder,jint format)176 static jint setAndroidBitmapFormat(JNIEnv*, jobject, jlong decoder, jint format) {
177     return AImageDecoder_setAndroidBitmapFormat(reinterpret_cast<AImageDecoder*>(decoder),
178                                                 format);
179 }
180 
setDataSpace(JNIEnv *,jobject,jlong decoder,jint dataSpace)181 static jint setDataSpace(JNIEnv*, jobject, jlong decoder, jint dataSpace) {
182     return AImageDecoder_setDataSpace(reinterpret_cast<AImageDecoder*>(decoder),
183                                       dataSpace);
184 }
185 
createFrameInfo(JNIEnv *,jobject)186 static jlong createFrameInfo(JNIEnv*, jobject) {
187     return reinterpret_cast<jlong>(AImageDecoderFrameInfo_create());
188 }
189 
deleteFrameInfo(JNIEnv *,jobject,jlong frameInfo)190 static void deleteFrameInfo(JNIEnv*, jobject, jlong frameInfo) {
191     AImageDecoderFrameInfo_delete(reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
192 }
193 
getFrameInfo(JNIEnv *,jobject,jlong decoder,jlong frameInfo)194 static jint getFrameInfo(JNIEnv*, jobject, jlong decoder, jlong frameInfo) {
195     return AImageDecoder_getFrameInfo(reinterpret_cast<AImageDecoder*>(decoder),
196                                       reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
197 }
198 
testNullFrameInfo(JNIEnv * env,jobject,jobject jAssets,jstring jFile)199 static void testNullFrameInfo(JNIEnv* env, jobject, jobject jAssets, jstring jFile) {
200     AImageDecoderFrameInfo_delete(nullptr);
201 
202 #pragma clang diagnostic push
203 #pragma clang diagnostic ignored "-Wnonnull"
204     {
205         auto* frameInfo = AImageDecoderFrameInfo_create();
206         ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_getFrameInfo(nullptr,
207                                                                                   frameInfo));
208         AImageDecoderFrameInfo_delete(frameInfo);
209     }
210     {
211         auto asset = openAsset(env, nullptr, jAssets, jFile);
212         auto decoder = createFromAsset(env, nullptr, asset);
213         AImageDecoderFrameInfo* info = nullptr;
214         ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, getFrameInfo(env, nullptr, decoder,
215                 reinterpret_cast<jlong>(info)));
216 
217         deleteDecoder(env, nullptr, decoder);
218         closeAsset(env, nullptr, asset);
219     }
220     {
221         ARect rect = AImageDecoderFrameInfo_getFrameRect(nullptr);
222         ASSERT_EQ(0, rect.left);
223         ASSERT_EQ(0, rect.top);
224         ASSERT_EQ(0, rect.right);
225         ASSERT_EQ(0, rect.bottom);
226     }
227 
228     ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getDuration(nullptr));
229     ASSERT_FALSE(AImageDecoderFrameInfo_hasAlphaWithinBounds(nullptr));
230     ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getDisposeOp(nullptr));
231     ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getBlendOp(nullptr));
232 #pragma clang diagnostic pop
233 }
234 
getDuration(JNIEnv *,jobject,jlong frameInfo)235 static jlong getDuration(JNIEnv*, jobject, jlong frameInfo) {
236     return AImageDecoderFrameInfo_getDuration(reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
237 }
238 
testGetFrameRect(JNIEnv * env,jobject,jlong jFrameInfo,jint expectedLeft,jint expectedTop,jint expectedRight,jint expectedBottom)239 static void testGetFrameRect(JNIEnv* env, jobject, jlong jFrameInfo, jint expectedLeft,
240                              jint expectedTop, jint expectedRight, jint expectedBottom) {
241     auto* frameInfo = reinterpret_cast<AImageDecoderFrameInfo*>(jFrameInfo);
242     ARect rect = AImageDecoderFrameInfo_getFrameRect(frameInfo);
243     if (rect.left != expectedLeft || rect.top != expectedTop || rect.right != expectedRight
244         || rect.bottom != expectedBottom) {
245         fail(env, "Mismatched frame rect! Expected: %i %i %i %i Actual: %i %i %i %i", expectedLeft,
246              expectedTop, expectedRight, expectedBottom, rect.left, rect.top, rect.right,
247              rect.bottom);
248     }
249 }
250 
getFrameAlpha(JNIEnv *,jobject,jlong frameInfo)251 static jboolean getFrameAlpha(JNIEnv*, jobject, jlong frameInfo) {
252     return AImageDecoderFrameInfo_hasAlphaWithinBounds(
253             reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
254 }
255 
getAlpha(JNIEnv *,jobject,jlong decoder)256 static jboolean getAlpha(JNIEnv*, jobject, jlong decoder) {
257     const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
258     return AImageDecoderHeaderInfo_getAlphaFlags(info) != ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
259 }
260 
getDisposeOp(JNIEnv *,jobject,jlong frameInfo)261 static jint getDisposeOp(JNIEnv*, jobject, jlong frameInfo) {
262     return AImageDecoderFrameInfo_getDisposeOp(
263             reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
264 }
265 
getBlendOp(JNIEnv *,jobject,jlong frameInfo)266 static jint getBlendOp(JNIEnv*, jobject, jlong frameInfo) {
267     return AImageDecoderFrameInfo_getBlendOp(
268             reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
269 }
270 
getRepeatCount(JNIEnv *,jobject,jlong decoder)271 static jint getRepeatCount(JNIEnv*, jobject, jlong decoder) {
272     return AImageDecoder_getRepeatCount(reinterpret_cast<AImageDecoder*>(decoder));
273 }
274 
setHandleDisposePrevious(JNIEnv *,jobject,jlong decoder,jboolean handle)275 static void setHandleDisposePrevious(JNIEnv*, jobject, jlong decoder, jboolean handle) {
276     AImageDecoder_setInternallyHandleDisposePrevious(reinterpret_cast<AImageDecoder*>(decoder),
277                                                      handle);
278 }
279 
280 #define ASSET_MANAGER "Landroid/content/res/AssetManager;"
281 #define STRING "Ljava/lang/String;"
282 #define BITMAP "Landroid/graphics/Bitmap;"
283 
284 static JNINativeMethod gMethods[] = {
285     { "nTestNullDecoder", "()V", (void*) testNullDecoder },
286     { "nTestToString", "()V", (void*) testToString },
287     { "nOpenAsset", "(" ASSET_MANAGER STRING ")J", (void*) openAsset },
288     { "nCloseAsset", "(J)V", (void*) closeAsset },
289     { "nCreateFromAsset", "(J)J", (void*) createFromAsset },
290     { "nGetWidth", "(J)I", (void*) getWidth },
291     { "nGetHeight", "(J)I", (void*) getHeight },
292     { "nDeleteDecoder", "(J)V", (void*) deleteDecoder },
293     { "nSetTargetSize", "(JII)I", (void*) setTargetSize },
294     { "nSetCrop", "(JIIII)I", (void*) setCrop },
295     { "nDecode", "(J" BITMAP "I)V", (void*) decode },
296     { "nAdvanceFrame", "(J)I", (void*) advanceFrame },
297     { "nRewind", "(J)I", (void*) rewind_decoder },
298     { "nSetUnpremultipliedRequired", "(JZ)I", (void*) setUnpremultipliedRequired },
299     { "nSetAndroidBitmapFormat", "(JI)I", (void*) setAndroidBitmapFormat },
300     { "nSetDataSpace", "(JI)I", (void*) setDataSpace },
301     { "nCreateFrameInfo", "()J", (void*) createFrameInfo },
302     { "nDeleteFrameInfo", "(J)V", (void*) deleteFrameInfo },
303     { "nGetFrameInfo", "(JJ)I", (void*) getFrameInfo },
304     { "nTestNullFrameInfo", "(" ASSET_MANAGER STRING ")V", (void*) testNullFrameInfo },
305     { "nGetDuration", "(J)J", (void*) getDuration },
306     { "nTestGetFrameRect", "(JIIII)V", (void*) testGetFrameRect },
307     { "nGetFrameAlpha", "(J)Z", (void*) getFrameAlpha },
308     { "nGetAlpha", "(J)Z", (void*) getAlpha },
309     { "nGetDisposeOp", "(J)I", (void*) getDisposeOp },
310     { "nGetBlendOp", "(J)I", (void*) getBlendOp },
311     { "nGetRepeatCount", "(J)I", (void*) getRepeatCount },
312     { "nSetHandleDisposePrevious", "(JZ)V", (void*) setHandleDisposePrevious },
313 };
314 
register_android_uirendering_cts_AImageDecoderTest(JNIEnv * env)315 int register_android_uirendering_cts_AImageDecoderTest(JNIEnv* env) {
316     jclass clazz = env->FindClass("android/uirendering/cts/testclasses/AImageDecoderTest");
317     return env->RegisterNatives(clazz, gMethods,
318             sizeof(gMethods) / sizeof(JNINativeMethod));
319 }
320 
321