| // Copyright 2022 Google LLC |
| // SPDX-License-Identifier: BSD-2-Clause |
| |
| package org.aomedia.avif.android; |
| |
| import android.graphics.Bitmap; |
| import androidx.annotation.Nullable; |
| import java.nio.ByteBuffer; |
| |
| /** |
| * An AVIF Decoder. AVIF Specification: https://aomediacodec.github.io/av1-avif/. |
| * |
| * <p>There are two ways to use this class. |
| * |
| * <p>1) As a static utility class. |
| * |
| * <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. |
| * |
| * <p>2) As an instantiated regular class. |
| * |
| * <p>When used this way, the {@link create} method must be used to create an instance of this class |
| * with a valid AVIF image. This will create a long running underlying decoder object which will be |
| * used to decode the image(s). Using the returned object, other public methods of the class can be |
| * called to get information about the image and to get the individual decoded frames. When the |
| * decoder object is no longer needed, {@link release} must be called to release the underlying |
| * decoder. |
| * |
| * <p>This is useful for decoding animated AVIF images and obtaining each decoded frame one after |
| * the other. |
| * |
| * <p>NOTE: The API for using this as an instantiated regular class is still under development and |
| * might change. |
| */ |
| @SuppressWarnings("CatchAndPrintStackTrace") |
| public class AvifDecoder { |
| static { |
| try { |
| System.loadLibrary("avif_android"); |
| } catch (UnsatisfiedLinkError exception) { |
| exception.printStackTrace(); |
| } |
| } |
| |
| private long decoder; |
| private int width; |
| private int height; |
| private int depth; |
| private boolean alphaPresent; |
| private int frameCount; |
| private int repetitionCount; |
| private double[] frameDurations; |
| |
| private AvifDecoder(ByteBuffer encoded, int threads) { |
| decoder = createDecoder(encoded, encoded.remaining(), threads); |
| } |
| |
| /** Contains information about the AVIF Image. This class is only used for getInfo(). */ |
| public static class Info { |
| public int width; |
| public int height; |
| public int depth; |
| public boolean alphaPresent; |
| } |
| |
| /** |
| * Returns true if the bytes in the buffer seem like an AVIF image. |
| * |
| * @param buffer The encoded image. buffer.position() must be 0. |
| * @return true if the bytes seem like an AVIF image, false otherwise. |
| */ |
| public static boolean isAvifImage(ByteBuffer buffer) { |
| return AvifDecoder.isAvifImage(buffer, buffer.remaining()); |
| } |
| |
| private static native boolean isAvifImage(ByteBuffer encoded, int length); |
| |
| /** |
| * Parses the AVIF header and populates the Info. |
| * |
| * @param encoded The encoded AVIF image. encoded.position() must be 0. |
| * @param length Length of the encoded buffer. |
| * @param info Output parameter whose fields will be populated. |
| * @return true on success and false on failure. |
| */ |
| public static native boolean getInfo(ByteBuffer encoded, int length, Info info); |
| |
| /** |
| * Decodes the AVIF image into the bitmap. |
| * |
| * @param encoded The encoded AVIF image. encoded.position() must be 0. |
| * @param length Length of the encoded buffer. |
| * @param bitmap The decoded pixels will be copied into the bitmap. |
| * @return true on success and false on failure. A few possible reasons for failure are: 1) Input |
| * was not valid AVIF. 2) Bitmap was not large enough to store the decoded image. |
| */ |
| public static boolean decode(ByteBuffer encoded, int length, Bitmap bitmap) { |
| return decode(encoded, length, bitmap, 0); |
| } |
| |
| /** |
| * Decodes the AVIF image into the bitmap. |
| * |
| * @param encoded The encoded AVIF image. encoded.position() must be 0. |
| * @param length Length of the encoded buffer. |
| * @param bitmap The decoded pixels will be copied into the bitmap. |
| * @param threads Number of threads to be used for the AVIF decode. Zero means use number of CPU |
| * cores as the thread count. Negative values are invalid. When this value is > 0, it is |
| * simply mapped to the maxThreads parameter in libavif. For more details, see the |
| * documentation for maxThreads variable in avif.h. |
| * @return true on success and false on failure. A few possible reasons for failure are: 1) Input |
| * was not valid AVIF. 2) Bitmap was not large enough to store the decoded image. 3) Negative |
| * value was passed for the threads parameter. |
| */ |
| public static native boolean decode(ByteBuffer encoded, int length, Bitmap bitmap, int threads); |
| |
| /** Get the width of the image. */ |
| public int getWidth() { |
| return width; |
| } |
| |
| /** Get the height of the image. */ |
| public int getHeight() { |
| return height; |
| } |
| |
| /** Get the depth (bit depth) of the image. */ |
| public int getDepth() { |
| return depth; |
| } |
| |
| /** Returns true if the image contains a transparency/alpha channel, false otherwise. */ |
| public boolean getAlphaPresent() { |
| return alphaPresent; |
| } |
| |
| /** Get the number of frames in the image. */ |
| public int getFrameCount() { |
| return frameCount; |
| } |
| |
| /** |
| * Get the number of repetitions for an animated image (see repetitionCount in avif.h for |
| * details). |
| */ |
| public int getRepetitionCount() { |
| return repetitionCount; |
| } |
| |
| /** Get the duration for each frame in the image. */ |
| public double[] getFrameDurations() { |
| return frameDurations; |
| } |
| |
| /** Releases the underlying decoder object. */ |
| public void release() { |
| if (decoder != 0) { |
| destroyDecoder(decoder); |
| } |
| decoder = 0; |
| } |
| |
| /** |
| * Create and return an AvifDecoder. |
| * |
| * @param encoded The encoded AVIF image. encoded.position() must be 0. The memory of this |
| * ByteBuffer must be kept alive until release() is called. |
| * @return null on failure. AvifDecoder object on success. |
| */ |
| @Nullable |
| public static AvifDecoder create(ByteBuffer encoded) { |
| return create(encoded, 1); |
| } |
| |
| /** |
| * Create and return an AvifDecoder with the specified number of threads. |
| * |
| * @param encoded The encoded AVIF image. encoded.position() must be 0. The memory of this |
| * ByteBuffer must be kept alive until release() is called. |
| * @param threads Number of threads to be used by the decoder. Zero means use number of CPU cores |
| * as the thread count. Negative values are invalid. When this value is > 0, it is simply |
| * mapped to the maxThreads parameter in libavif. For more details, see the documentation for |
| * maxThreads variable in avif.h. |
| * @return null on failure. AvifDecoder object on success. |
| */ |
| @Nullable |
| public static AvifDecoder create(ByteBuffer encoded, int threads) { |
| AvifDecoder decoder = new AvifDecoder(encoded, threads); |
| return (decoder.decoder == 0) ? null : decoder; |
| } |
| |
| /** |
| * Decodes the next frame of the animated AVIF into the bitmap. |
| * |
| * @param bitmap The decoded pixels will be copied into the bitmap. |
| * @return 0 (AVIF_RESULT_OK) on success and some other avifStatus on failure. For a list of all |
| * possible status codes, see the avifResult enum on avif.h in libavif's C source code. A |
| * String describing the return value can be obtained by calling {@link resultToString} with |
| * the return value of this function. |
| */ |
| public int nextFrame(Bitmap bitmap) { |
| return nextFrame(decoder, bitmap); |
| } |
| |
| private native int nextFrame(long decoder, Bitmap bitmap); |
| |
| /** |
| * 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} |
| * will fail. |
| */ |
| public int nextFrameIndex() { |
| return nextFrameIndex(decoder); |
| } |
| |
| private native int nextFrameIndex(long decoder); |
| |
| /** |
| * Decodes the nth frame of the animated AVIF into the bitmap. |
| * |
| * <p>Note that calling this method will change the behavior of subsequent calls to {@link |
| * nextFrame}. {@link nextFrame} will start outputting the frame after this one. |
| * |
| * @param bitmap The decoded pixels will be copied into the bitmap. |
| * @param n The zero-based index of the frame to be decoded. |
| * @return 0 (AVIF_RESULT_OK) on success and some other avifStatus on failure. For a list of all |
| * possible status codes, see the avifResult enum on avif.h in libavif's C source code. A |
| * String describing the return value can be obtained by calling {@link resultToString} with |
| * the return value of this function. |
| */ |
| public int nthFrame(int n, Bitmap bitmap) { |
| return nthFrame(decoder, n, bitmap); |
| } |
| |
| private native int nthFrame(long decoder, int n, Bitmap bitmap); |
| |
| /** |
| * Returns a String describing an avifResult enum value. |
| * |
| * @param result The avifResult value. Typically this is the return value of {@link nextFrame} or |
| * {@link nthFrame}. |
| * @return A String containing the description of the avifResult. |
| */ |
| public static native String resultToString(int result); |
| |
| /** |
| * Returns a String that contains information about the libavif version, underlying codecs and |
| * libyuv version (if available). |
| */ |
| public static native String versionString(); |
| |
| private native long createDecoder(ByteBuffer encoded, int length, int threads); |
| |
| private native void destroyDecoder(long decoder); |
| } |