![]() Server : Apache/2.4.62 (Unix) OpenSSL/1.0.2k-fips System : Linux server1.richineiserver.com 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64 User : fdshops ( 1003) PHP Version : 8.2.21 Disable Function : NONE Directory : /usr/local/src/libavif-0.11.1/android_jni/avifandroidjni/src/main/jni/ |
// Copyright 2022 Google LLC. All rights reserved. // SPDX-License-Identifier: BSD-2-Clause #include <android/bitmap.h> #include <android/log.h> #include <cpu-features.h> #include <jni.h> #include "avif/avif.h" #define LOG_TAG "avif_jni" #define LOGE(...) \ ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) #define FUNC(RETURN_TYPE, NAME, ...) \ extern "C" { \ JNIEXPORT RETURN_TYPE Java_org_aomedia_avif_android_AvifDecoder_##NAME( \ JNIEnv* env, jobject /*thiz*/, ##__VA_ARGS__); \ } \ JNIEXPORT RETURN_TYPE Java_org_aomedia_avif_android_AvifDecoder_##NAME( \ JNIEnv* env, jobject /*thiz*/, ##__VA_ARGS__) namespace { jfieldID global_info_width; jfieldID global_info_height; jfieldID global_info_depth; // RAII wrapper class that properly frees the decoder related objects on // destruction. struct AvifDecoderWrapper { public: AvifDecoderWrapper() = default; // Not copyable or movable. AvifDecoderWrapper(const AvifDecoderWrapper&) = delete; AvifDecoderWrapper& operator=(const AvifDecoderWrapper&) = delete; ~AvifDecoderWrapper() { if (decoder != nullptr) { avifDecoderDestroy(decoder); } } avifDecoder* decoder = nullptr; }; bool CreateDecoderAndParse(AvifDecoderWrapper* const decoder, const uint8_t* const buffer, int length, int threads) { decoder->decoder = avifDecoderCreate(); if (decoder->decoder == nullptr) { LOGE("Failed to create AVIF Decoder."); return false; } decoder->decoder->maxThreads = threads; decoder->decoder->ignoreXMP = AVIF_TRUE; decoder->decoder->ignoreExif = AVIF_TRUE; // Turn off 'clap' (clean aperture) property validation. The JNI wrapper // ignores the 'clap' property. decoder->decoder->strictFlags &= ~AVIF_STRICT_CLAP_VALID; // Allow 'pixi' (pixel information) property to be missing. Older versions of // libheif did not add the 'pixi' item property to AV1 image items (See // crbug.com/1198455). decoder->decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; avifResult res = avifDecoderSetIOMemory(decoder->decoder, buffer, length); if (res != AVIF_RESULT_OK) { LOGE("Failed to set AVIF IO to a memory reader."); return false; } res = avifDecoderParse(decoder->decoder); if (res != AVIF_RESULT_OK) { LOGE("Failed to parse AVIF image: %s.", avifResultToString(res)); return false; } return true; } } // namespace jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { return -1; } const jclass info_class = env->FindClass("org/aomedia/avif/android/AvifDecoder$Info"); global_info_width = env->GetFieldID(info_class, "width", "I"); global_info_height = env->GetFieldID(info_class, "height", "I"); global_info_depth = env->GetFieldID(info_class, "depth", "I"); return JNI_VERSION_1_6; } FUNC(jboolean, isAvifImage, jobject encoded, int length) { const uint8_t* const buffer = static_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded)); const avifROData avif = {buffer, static_cast<size_t>(length)}; return avifPeekCompatibleFileType(&avif); } FUNC(jboolean, getInfo, jobject encoded, int length, jobject info) { const uint8_t* const buffer = static_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded)); AvifDecoderWrapper decoder; if (!CreateDecoderAndParse(&decoder, buffer, length, /*threads=*/ 1)) { return false; } env->SetIntField(info, global_info_width, decoder.decoder->image->width); env->SetIntField(info, global_info_height, decoder.decoder->image->height); env->SetIntField(info, global_info_depth, decoder.decoder->image->depth); return true; } FUNC(jboolean, decode, jobject encoded, int length, jobject bitmap) { const uint8_t* const buffer = static_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded)); AvifDecoderWrapper decoder; if (!CreateDecoderAndParse(&decoder, buffer, length, android_getCpuCount())) { return false; } avifResult res = avifDecoderNextImage(decoder.decoder); if (res != AVIF_RESULT_OK) { LOGE("Failed to decode AVIF image. Status: %d", res); return false; } AndroidBitmapInfo bitmap_info; if (AndroidBitmap_getInfo(env, bitmap, &bitmap_info) < 0) { LOGE("AndroidBitmap_getInfo failed."); return false; } // Ensure that the bitmap is large enough to store the decoded image. if (bitmap_info.width < decoder.decoder->image->width || bitmap_info.height < decoder.decoder->image->height) { LOGE( "Bitmap is not large enough to fit the image. Bitmap %dx%d Image " "%dx%d.", bitmap_info.width, bitmap_info.height, decoder.decoder->image->width, decoder.decoder->image->height); return false; } // Ensure that the bitmap format is RGBA_8888, RGB_565 or RGBA_F16. if (bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888 && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGB_565 && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_F16) { LOGE("Bitmap format (%d) is not supported.", bitmap_info.format); return false; } void* bitmap_pixels = nullptr; if (AndroidBitmap_lockPixels(env, bitmap, &bitmap_pixels) != ANDROID_BITMAP_RESULT_SUCCESS) { LOGE("Failed to lock Bitmap."); return false; } avifRGBImage rgb_image; avifRGBImageSetDefaults(&rgb_image, decoder.decoder->image); if (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_F16) { rgb_image.depth = 16; rgb_image.isFloat = AVIF_TRUE; } else if (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGB_565) { rgb_image.format = AVIF_RGB_FORMAT_RGB_565; rgb_image.depth = 8; } else { rgb_image.depth = 8; } rgb_image.pixels = static_cast<uint8_t*>(bitmap_pixels); rgb_image.rowBytes = bitmap_info.stride; res = avifImageYUVToRGB(decoder.decoder->image, &rgb_image); AndroidBitmap_unlockPixels(env, bitmap); if (res != AVIF_RESULT_OK) { LOGE("Failed to convert YUV Pixels to RGB. Status: %d", res); return false; } return true; }