Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5aa8489
Hardware Buffer対応
k2w4t4h Jul 11, 2026
b22a1bf
過去Verを考慮する
k2w4t4h Jul 11, 2026
a7cf17e
Animation Avifの対応は不要なので消す
k2w4t4h Jul 11, 2026
532ff8a
dlの取得元対応
k2w4t4h Jul 11, 2026
ef78ba8
レビューの指摘に対応
k2w4t4h Jul 11, 2026
824ed9c
Fork差分が小さくなるように
k2w4t4h Jul 11, 2026
dbee1dd
パスが解決できなかったので書き方変える
k2w4t4h Jul 12, 2026
63e4f0b
AHardwareBuffer_toHardwareBuffer の読み込み先の調整
k2w4t4h Jul 13, 2026
bb619ee
R8対応とりあえず
k2w4t4h Jul 13, 2026
2ee82f1
BugFix
k2w4t4h Jul 13, 2026
93a8b38
RGB565に切替
k2w4t4h Jul 14, 2026
c712826
Merge pull request #2 from link-u/feature/hardware_buffer_r8
k2w4t4h Jul 15, 2026
4974385
Performance
k2w4t4h Jul 15, 2026
a7fe677
dav1dのVerあげる
k2w4t4h Jul 15, 2026
3227f53
無駄コピーを減らす
k2w4t4h Jul 15, 2026
b9e7361
リサイズのフィルタを速いやつに
k2w4t4h Jul 15, 2026
efc42b3
Neon SIMD
k2w4t4h Jul 15, 2026
34540e0
ビルド変更
k2w4t4h Jul 17, 2026
4ce90c6
色々削る
k2w4t4h Jul 18, 2026
07b949f
Alpha の不要デコードを省く
k2w4t4h Jul 18, 2026
ac65468
不要なメモリの開放
k2w4t4h Jul 18, 2026
a4ede21
RGB565の真の方を消す
k2w4t4h Jul 18, 2026
c38b965
NDK27対応
k2w4t4h Jul 18, 2026
970bd6b
不要なAPIの絞り込み
k2w4t4h Jul 18, 2026
37d6b9e
BugFix
k2w4t4h Jul 19, 2026
2cddfd5
BugFix
k2w4t4h Jul 19, 2026
72d37ae
android_jni: GL ES R8 HardwareBuffer output for 8-bit monochrome AVIF
k2w4t4h Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions android_jni/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ $ ./dav1d_android.sh "${ANDROID_NDK_HOME}"
$ cd ..
```

The Android dav1d build is configured with `-Dbitdepths=8` (8-bit AV1 only). Re-run the
script after changing this option so that all ABIs are rebuilt.

If you want to use libgav1 instead:

```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.ColorSpace;
import android.hardware.HardwareBuffer;
import android.os.Build;
import androidx.test.platform.app.InstrumentationRegistry;
import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -256,6 +259,74 @@ public void testDecodeRegularClass() throws IOException {
decoder.release();
}

@Test
public void testDecodeToHardwareBuffer() throws IOException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return;
}
if (image.isAnimated || config != Config.ARGB_8888) {
return;
}
ByteBuffer buffer = image.getBuffer();
assertThat(buffer).isNotNull();
Info info = new Info();
assertThat(AvifDecoder.getInfo(buffer, buffer.remaining(), info)).isTrue();

HardwareBuffer hardwareBuffer =
AvifDecoder.decodeToHardwareBuffer(buffer, buffer.remaining(), image.threads);
assertThat(hardwareBuffer).isNotNull();
assertThat(hardwareBuffer.getWidth()).isEqualTo(info.width);
assertThat(hardwareBuffer.getHeight()).isEqualTo(info.height);
assertThat(hardwareBuffer.getFormat()).isEqualTo(HardwareBuffer.RGBA_8888);
hardwareBuffer.close();

for (float scaleFactor : SCALE_FACTORS) {
int targetWidth = (int) (info.width * scaleFactor);
int targetHeight = (int) (info.height * scaleFactor);
hardwareBuffer =
AvifDecoder.decodeToHardwareBuffer(
buffer, buffer.remaining(), targetWidth, targetHeight, image.threads);
assertThat(hardwareBuffer).isNotNull();
assertThat(hardwareBuffer.getWidth()).isEqualTo(targetWidth);
assertThat(hardwareBuffer.getHeight()).isEqualTo(targetHeight);
Bitmap hardwareBitmap =
Bitmap.wrapHardwareBuffer(hardwareBuffer, ColorSpace.get(ColorSpace.Named.SRGB));
assertThat(hardwareBitmap).isNotNull();
assertThat(hardwareBitmap.getWidth()).isEqualTo(targetWidth);
assertThat(hardwareBitmap.getHeight()).isEqualTo(targetHeight);
hardwareBitmap.recycle();
hardwareBuffer.close();
}
}

@Test
public void testDecodeToHardwareBufferRegularClass() throws IOException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return;
}
if (config != Config.ARGB_8888) {
return;
}
ByteBuffer buffer = image.getBuffer();
assertThat(buffer).isNotNull();
AvifDecoder decoder = AvifDecoder.create(buffer, image.threads);
assertThat(decoder).isNotNull();
for (int i = 0; i < image.frameCount; ++i) {
assertThat(decoder.nextFrameIndex()).isEqualTo(i);
HardwareBuffer hardwareBuffer = decoder.nextFrameHardwareBuffer();
assertThat(hardwareBuffer).isNotNull();
assertThat(hardwareBuffer.getWidth()).isEqualTo(image.width);
assertThat(hardwareBuffer.getHeight()).isEqualTo(image.height);
hardwareBuffer.close();
}
if (image.isAnimated) {
HardwareBuffer hardwareBuffer = decoder.nthFrameHardwareBuffer(0);
assertThat(hardwareBuffer).isNotNull();
hardwareBuffer.close();
}
decoder.release();
}

@Test
public void testUtilityFunctions() throws IOException {
// Test the avifResult value whose value and string representations are least likely to change.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package org.aomedia.avif.android;

import android.graphics.Bitmap;
import android.hardware.HardwareBuffer;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.nio.ByteBuffer;

/**
Expand All @@ -16,8 +18,9 @@
*
* <p>This class can be accessed statically without instantiating an object. This is useful to
* simply sniff and decode still AVIF images without having to maintain any decoder state. The
* following are the methods that can be accessed this way: {@link isAvifImage}, {@link getInfo} and
* {@link decode}. The {@link Info} inner class is used only in this case.
* following are the methods that can be accessed this way: {@link isAvifImage}, {@link getInfo},
* {@link decode} and {@link decodeToHardwareBuffer}. The {@link Info} inner class is used only in
* this case.
*
* <p>2) As an instantiated regular class.
*
Expand Down Expand Up @@ -118,6 +121,44 @@ public static boolean decode(ByteBuffer encoded, int length, Bitmap bitmap) {
*/
public static native boolean decode(ByteBuffer encoded, int length, Bitmap bitmap, int threads);

/**
* Decodes the AVIF image into an {@link HardwareBuffer} with RGBA_8888 pixels.
*
* <p>The returned buffer can be wrapped as a hardware {@link Bitmap} via {@link
* Bitmap#wrapHardwareBuffer(HardwareBuffer, android.graphics.ColorSpace)} on API 29+. Callers on
* older API levels must not use this method.
*
* <p>If {@code targetWidth} and {@code targetHeight} are both positive, the decoded image is
* scaled to those dimensions before RGB conversion. Otherwise the cropped image dimensions are
* used.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0.
* @param length Length of the encoded buffer.
* @param targetWidth Desired output width, or 0 to use the image width.
* @param targetHeight Desired output height, or 0 to use the image height.
* @param threads Number of threads to be used for the AVIF decode.
* @return a HardwareBuffer on success, or null on failure.
*/
@RequiresApi(29)
public static HardwareBuffer decodeToHardwareBuffer(
ByteBuffer encoded, int length, int targetWidth, int targetHeight, int threads) {
return decodeToHardwareBufferNative(encoded, length, targetWidth, targetHeight, threads);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

HardwareBuffer クラスは Android API 26 (Android 8.0) で導入されました。

AvifDecoder のメソッドシグネチャ(decodeToHardwareBuffer、nextFrameHardwareBuffer など)に HardwareBuffer を含めると、Android API < 26 のデバイスでクラスの検証(verification)に失敗し、VerifyError または NoClassDefFoundError が発生します。これにより、古いデバイスでは通常の Bitmap デコード機能を含め、AvifDecoder クラス自体のロードが完全に不可能になります。

この問題を回避するために、すべての HardwareBuffer 関連のメソッドを、API >= 29 でのみロードされる別のヘルパークラス(例: AvifHardwareDecoder または遅延ロードされるインナークラス)に移動することを推奨します。これにより、古いデバイスでも AvifDecoder を安全にロードして使用できるようになります。


/**
* Decodes the AVIF image into an {@link HardwareBuffer} at the cropped image dimensions.
*
* @see #decodeToHardwareBuffer(ByteBuffer, int, int, int, int)
*/
@RequiresApi(29)
public static HardwareBuffer decodeToHardwareBuffer(
ByteBuffer encoded, int length, int threads) {
return decodeToHardwareBuffer(encoded, length, 0, 0, threads);
}

private static native HardwareBuffer decodeToHardwareBufferNative(
ByteBuffer encoded, int length, int targetWidth, int targetHeight, int threads);

/** Get the width of the image. */
public int getWidth() {
return width;
Expand Down Expand Up @@ -208,6 +249,33 @@ public int nextFrame(Bitmap bitmap) {

private native int nextFrame(long decoder, Bitmap bitmap);

/**
* Decodes the next frame of the animated AVIF into an {@link HardwareBuffer}.
*
* @param targetWidth Desired output width, or 0 to use the image width.
* @param targetHeight Desired output height, or 0 to use the image height.
* @return a HardwareBuffer on success, or null on failure.
* @see #decodeToHardwareBuffer(ByteBuffer, int, int, int, int)
*/
@RequiresApi(29)
@Nullable
public HardwareBuffer nextFrameHardwareBuffer(int targetWidth, int targetHeight) {
return nextFrameHardwareBuffer(decoder, targetWidth, targetHeight);
}

/**
* Decodes the next frame of the animated AVIF into an {@link HardwareBuffer} at the cropped
* image dimensions.
*/
@RequiresApi(29)
@Nullable
public HardwareBuffer nextFrameHardwareBuffer() {
return nextFrameHardwareBuffer(decoder, 0, 0);
}

private native HardwareBuffer nextFrameHardwareBuffer(
long decoder, int targetWidth, int targetHeight);

/**
* Get the 0-based index of the frame that will be returned by the next call to {@link nextFrame}.
* If the returned value is same as {@link getFrameCount}, then the next call to {@link nextFrame}
Expand Down Expand Up @@ -238,6 +306,34 @@ public int nthFrame(int n, Bitmap bitmap) {

private native int nthFrame(long decoder, int n, Bitmap bitmap);

/**
* Decodes the nth frame of the animated AVIF into an {@link HardwareBuffer}.
*
* @param n The zero-based index of the frame to be decoded.
* @param targetWidth Desired output width, or 0 to use the image width.
* @param targetHeight Desired output height, or 0 to use the image height.
* @return a HardwareBuffer on success, or null on failure.
* @see #decodeToHardwareBuffer(ByteBuffer, int, int, int, int)
*/
@RequiresApi(29)
@Nullable
public HardwareBuffer nthFrameHardwareBuffer(int n, int targetWidth, int targetHeight) {
return nthFrameHardwareBuffer(decoder, n, targetWidth, targetHeight);
}

/**
* Decodes the nth frame of the animated AVIF into an {@link HardwareBuffer} at the cropped
* image dimensions.
*/
@RequiresApi(29)
@Nullable
public HardwareBuffer nthFrameHardwareBuffer(int n) {
return nthFrameHardwareBuffer(decoder, n, 0, 0);
}

private native HardwareBuffer nthFrameHardwareBuffer(
long decoder, int n, int targetWidth, int targetHeight);

/**
* Returns a String describing an avifResult enum value.
*
Expand Down
2 changes: 1 addition & 1 deletion android_jni/avifandroidjni/src/main/jni/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ include_directories(${CPU_FEATURES_DIR})
add_library(cpufeatures STATIC "${CPU_FEATURES_DIR}/cpu-features.c")

target_link_options(avif_android PRIVATE "-Wl,-z,max-page-size=16384")
target_link_libraries(avif_android jnigraphics avif log cpufeatures)
target_link_libraries(avif_android jnigraphics nativewindow avif log cpufeatures)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

target_link_libraries で nativewindow を直接リンクすると、libavif_android.so に libnativewindow.so へのハード依存関係が導入されます。

libnativewindow.so は Android API 26 (Android 8.0) で導入されたため、Android API < 26 のデバイスで libavif_android.so をロードしようとすると、ハードウェアバッファデコード機能を使用していない場合でも、即座に UnsatisfiedLinkError(例: library "libnativewindow.so" not found)が発生してアプリがクラッシュします。

古い Android バージョンとの後方互換性を維持するために、target_link_libraries から nativewindow を削除し、API レベル 29 以上で実行されている場合にのみ、dlopen を使用して libnativewindow.so を動的にロードし、dlsym を使用して必要な AHardwareBuffer_* シンボルを解決するようにしてください。

target_link_libraries(avif_android jnigraphics avif log cpufeatures)

Loading
Loading