blob: be1d309fb617422c8312aa51dbb4c24cc60fc7a3 [file] [log] [blame]
/*
* Copyright 2020 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 "AImageDecoderTest"
#include <jni.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/bitmap.h>
#include <android/imagedecoder.h>
#include <android/rect.h>
#include "NativeTestHelpers.h"
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
static void testNullDecoder(JNIEnv* env, jobject) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_advanceFrame(nullptr));
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_rewind(nullptr));
AImageDecoder_setInternallyHandleDisposePrevious(nullptr, true);
AImageDecoder_setInternallyHandleDisposePrevious(nullptr, false);
#pragma clang diagnostic pop
}
static void testToString(JNIEnv* env, jobject) {
struct {
int resultCode;
const char* string;
} map[] = {
{ ANDROID_IMAGE_DECODER_SUCCESS, "ANDROID_IMAGE_DECODER_SUCCESS" },
{ ANDROID_IMAGE_DECODER_INCOMPLETE, "ANDROID_IMAGE_DECODER_INCOMPLETE" },
{ ANDROID_IMAGE_DECODER_ERROR, "ANDROID_IMAGE_DECODER_ERROR" },
{ ANDROID_IMAGE_DECODER_INVALID_CONVERSION, "ANDROID_IMAGE_DECODER_INVALID_CONVERSION" },
{ ANDROID_IMAGE_DECODER_INVALID_SCALE, "ANDROID_IMAGE_DECODER_INVALID_SCALE" },
{ ANDROID_IMAGE_DECODER_BAD_PARAMETER, "ANDROID_IMAGE_DECODER_BAD_PARAMETER" },
{ ANDROID_IMAGE_DECODER_INVALID_INPUT, "ANDROID_IMAGE_DECODER_INVALID_INPUT" },
{ ANDROID_IMAGE_DECODER_SEEK_ERROR, "ANDROID_IMAGE_DECODER_SEEK_ERROR" },
{ ANDROID_IMAGE_DECODER_INTERNAL_ERROR, "ANDROID_IMAGE_DECODER_INTERNAL_ERROR" },
{ ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT, "ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT" },
{ ANDROID_IMAGE_DECODER_FINISHED, "ANDROID_IMAGE_DECODER_FINISHED" },
{ ANDROID_IMAGE_DECODER_INVALID_STATE, "ANDROID_IMAGE_DECODER_INVALID_STATE" },
};
for (const auto& item : map) {
const char* str = AImageDecoder_resultToString(item.resultCode);
ASSERT_EQ(0, strcmp(item.string, str));
}
for (int i : { ANDROID_IMAGE_DECODER_SUCCESS + 1,
ANDROID_IMAGE_DECODER_INVALID_STATE - 1,
2, 7, 37, 42 }) {
ASSERT_EQ(nullptr, AImageDecoder_resultToString(i));
}
}
static jlong openAsset(JNIEnv* env, jobject, jobject jAssets, jstring jFile) {
AAssetManager* nativeManager = AAssetManager_fromJava(env, jAssets);
const char* file = env->GetStringUTFChars(jFile, nullptr);
AAsset* asset = AAssetManager_open(nativeManager, file, AASSET_MODE_UNKNOWN);
if (!asset) {
fail(env, "Could not open %s", file);
} else {
ALOGD("Testing %s", file);
}
env->ReleaseStringUTFChars(jFile, file);
return reinterpret_cast<jlong>(asset);
}
static void closeAsset(JNIEnv*, jobject, jlong asset) {
AAsset_close(reinterpret_cast<AAsset*>(asset));
}
static jlong createFromAsset(JNIEnv* env, jobject, jlong asset) {
AImageDecoder* decoder = nullptr;
int result = AImageDecoder_createFromAAsset(reinterpret_cast<AAsset*>(asset), &decoder);
if (ANDROID_IMAGE_DECODER_SUCCESS != result || !decoder) {
fail(env, "Failed to create AImageDecoder with %s!",
AImageDecoder_resultToString(result));
}
return reinterpret_cast<jlong>(decoder);
}
static jint getWidth(JNIEnv*, jobject, jlong decoder) {
const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
return AImageDecoderHeaderInfo_getWidth(info);
}
static jint getHeight(JNIEnv*, jobject, jlong decoder) {
const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
return AImageDecoderHeaderInfo_getHeight(info);
}
static void deleteDecoder(JNIEnv*, jobject, jlong decoder) {
AImageDecoder_delete(reinterpret_cast<AImageDecoder*>(decoder));
}
static jint setTargetSize(JNIEnv*, jobject, jlong decoder_ptr, jint width, jint height) {
return AImageDecoder_setTargetSize(reinterpret_cast<AImageDecoder*>(decoder_ptr),
width, height);
}
static jint setCrop(JNIEnv*, jobject, jlong decoder_ptr, jint left, jint top,
jint right, jint bottom) {
return AImageDecoder_setCrop(reinterpret_cast<AImageDecoder*>(decoder_ptr),
{left, top, right, bottom});
}
static void decode(JNIEnv* env, jobject, jlong decoder_ptr, jobject jBitmap, jint expected) {
auto* decoder = reinterpret_cast<AImageDecoder*>(decoder_ptr);
AndroidBitmapInfo info;
if (AndroidBitmap_getInfo(env, jBitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) {
fail(env, "Failed to getInfo on a Bitmap!");
return;
}
void* pixels;
if (AndroidBitmap_lockPixels(env, jBitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
fail(env, "Failed to lock pixels!");
return;
}
const int result = AImageDecoder_decodeImage(decoder, pixels, info.stride,
info.stride * info.height);
if (result != expected) {
fail(env, "Unexpected result from AImageDecoder_decodeImage: %s",
AImageDecoder_resultToString(result));
// Don't return yet, so we can unlockPixels.
}
if (AndroidBitmap_unlockPixels(env, jBitmap) != ANDROID_BITMAP_RESULT_SUCCESS) {
const char* msg = "Failed to unlock pixels!";
if (env->ExceptionCheck()) {
// Do not attempt to throw an Exception while one is pending.
ALOGE("%s", msg);
} else {
fail(env, msg);
}
}
}
static jint advanceFrame(JNIEnv*, jobject, jlong decoder_ptr) {
auto* decoder = reinterpret_cast<AImageDecoder*>(decoder_ptr);
return AImageDecoder_advanceFrame(decoder);
}
static jint rewind_decoder(JNIEnv*, jobject, jlong decoder) {
return AImageDecoder_rewind(reinterpret_cast<AImageDecoder*>(decoder));
}
static jint setUnpremultipliedRequired(JNIEnv*, jobject, jlong decoder, jboolean required) {
return AImageDecoder_setUnpremultipliedRequired(reinterpret_cast<AImageDecoder*>(decoder),
required);
}
static jint setAndroidBitmapFormat(JNIEnv*, jobject, jlong decoder, jint format) {
return AImageDecoder_setAndroidBitmapFormat(reinterpret_cast<AImageDecoder*>(decoder),
format);
}
static jint setDataSpace(JNIEnv*, jobject, jlong decoder, jint dataSpace) {
return AImageDecoder_setDataSpace(reinterpret_cast<AImageDecoder*>(decoder),
dataSpace);
}
static jlong createFrameInfo(JNIEnv*, jobject) {
return reinterpret_cast<jlong>(AImageDecoderFrameInfo_create());
}
static void deleteFrameInfo(JNIEnv*, jobject, jlong frameInfo) {
AImageDecoderFrameInfo_delete(reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static jint getFrameInfo(JNIEnv*, jobject, jlong decoder, jlong frameInfo) {
return AImageDecoder_getFrameInfo(reinterpret_cast<AImageDecoder*>(decoder),
reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static void testNullFrameInfo(JNIEnv* env, jobject, jobject jAssets, jstring jFile) {
AImageDecoderFrameInfo_delete(nullptr);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
{
auto* frameInfo = AImageDecoderFrameInfo_create();
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_getFrameInfo(nullptr,
frameInfo));
AImageDecoderFrameInfo_delete(frameInfo);
}
{
auto asset = openAsset(env, nullptr, jAssets, jFile);
auto decoder = createFromAsset(env, nullptr, asset);
AImageDecoderFrameInfo* info = nullptr;
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, getFrameInfo(env, nullptr, decoder,
reinterpret_cast<jlong>(info)));
deleteDecoder(env, nullptr, decoder);
closeAsset(env, nullptr, asset);
}
{
ARect rect = AImageDecoderFrameInfo_getFrameRect(nullptr);
ASSERT_EQ(0, rect.left);
ASSERT_EQ(0, rect.top);
ASSERT_EQ(0, rect.right);
ASSERT_EQ(0, rect.bottom);
}
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getDuration(nullptr));
ASSERT_FALSE(AImageDecoderFrameInfo_hasAlphaWithinBounds(nullptr));
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getDisposeOp(nullptr));
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoderFrameInfo_getBlendOp(nullptr));
#pragma clang diagnostic pop
}
static jlong getDuration(JNIEnv*, jobject, jlong frameInfo) {
return AImageDecoderFrameInfo_getDuration(reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static void testGetFrameRect(JNIEnv* env, jobject, jlong jFrameInfo, jint expectedLeft,
jint expectedTop, jint expectedRight, jint expectedBottom) {
auto* frameInfo = reinterpret_cast<AImageDecoderFrameInfo*>(jFrameInfo);
ARect rect = AImageDecoderFrameInfo_getFrameRect(frameInfo);
if (rect.left != expectedLeft || rect.top != expectedTop || rect.right != expectedRight
|| rect.bottom != expectedBottom) {
fail(env, "Mismatched frame rect! Expected: %i %i %i %i Actual: %i %i %i %i", expectedLeft,
expectedTop, expectedRight, expectedBottom, rect.left, rect.top, rect.right,
rect.bottom);
}
}
static jboolean getFrameAlpha(JNIEnv*, jobject, jlong frameInfo) {
return AImageDecoderFrameInfo_hasAlphaWithinBounds(
reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static jboolean getAlpha(JNIEnv*, jobject, jlong decoder) {
const auto* info = AImageDecoder_getHeaderInfo(reinterpret_cast<AImageDecoder*>(decoder));
return AImageDecoderHeaderInfo_getAlphaFlags(info) != ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
}
static jint getDisposeOp(JNIEnv*, jobject, jlong frameInfo) {
return AImageDecoderFrameInfo_getDisposeOp(
reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static jint getBlendOp(JNIEnv*, jobject, jlong frameInfo) {
return AImageDecoderFrameInfo_getBlendOp(
reinterpret_cast<AImageDecoderFrameInfo*>(frameInfo));
}
static jint getRepeatCount(JNIEnv*, jobject, jlong decoder) {
return AImageDecoder_getRepeatCount(reinterpret_cast<AImageDecoder*>(decoder));
}
static void setHandleDisposePrevious(JNIEnv*, jobject, jlong decoder, jboolean handle) {
AImageDecoder_setInternallyHandleDisposePrevious(reinterpret_cast<AImageDecoder*>(decoder),
handle);
}
#define ASSET_MANAGER "Landroid/content/res/AssetManager;"
#define STRING "Ljava/lang/String;"
#define BITMAP "Landroid/graphics/Bitmap;"
static JNINativeMethod gMethods[] = {
{ "nTestNullDecoder", "()V", (void*) testNullDecoder },
{ "nTestToString", "()V", (void*) testToString },
{ "nOpenAsset", "(" ASSET_MANAGER STRING ")J", (void*) openAsset },
{ "nCloseAsset", "(J)V", (void*) closeAsset },
{ "nCreateFromAsset", "(J)J", (void*) createFromAsset },
{ "nGetWidth", "(J)I", (void*) getWidth },
{ "nGetHeight", "(J)I", (void*) getHeight },
{ "nDeleteDecoder", "(J)V", (void*) deleteDecoder },
{ "nSetTargetSize", "(JII)I", (void*) setTargetSize },
{ "nSetCrop", "(JIIII)I", (void*) setCrop },
{ "nDecode", "(J" BITMAP "I)V", (void*) decode },
{ "nAdvanceFrame", "(J)I", (void*) advanceFrame },
{ "nRewind", "(J)I", (void*) rewind_decoder },
{ "nSetUnpremultipliedRequired", "(JZ)I", (void*) setUnpremultipliedRequired },
{ "nSetAndroidBitmapFormat", "(JI)I", (void*) setAndroidBitmapFormat },
{ "nSetDataSpace", "(JI)I", (void*) setDataSpace },
{ "nCreateFrameInfo", "()J", (void*) createFrameInfo },
{ "nDeleteFrameInfo", "(J)V", (void*) deleteFrameInfo },
{ "nGetFrameInfo", "(JJ)I", (void*) getFrameInfo },
{ "nTestNullFrameInfo", "(" ASSET_MANAGER STRING ")V", (void*) testNullFrameInfo },
{ "nGetDuration", "(J)J", (void*) getDuration },
{ "nTestGetFrameRect", "(JIIII)V", (void*) testGetFrameRect },
{ "nGetFrameAlpha", "(J)Z", (void*) getFrameAlpha },
{ "nGetAlpha", "(J)Z", (void*) getAlpha },
{ "nGetDisposeOp", "(J)I", (void*) getDisposeOp },
{ "nGetBlendOp", "(J)I", (void*) getBlendOp },
{ "nGetRepeatCount", "(J)I", (void*) getRepeatCount },
{ "nSetHandleDisposePrevious", "(JZ)V", (void*) setHandleDisposePrevious },
};
int register_android_uirendering_cts_AImageDecoderTest(JNIEnv* env) {
jclass clazz = env->FindClass("android/uirendering/cts/testclasses/AImageDecoderTest");
return env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(JNINativeMethod));
}