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