Add BitmapDecoder interface

PiperOrigin-RevId: 557827954
This commit is contained in:
tofunmi 2023-08-17 16:14:23 +01:00 committed by Julia Bibik
parent b814404c56
commit a8944ef2f0
3 changed files with 67 additions and 25 deletions

View File

@ -15,15 +15,18 @@
*/
package androidx.media3.exoplayer.image;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.test.utils.TestUtil;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.nio.ByteBuffer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -36,16 +39,20 @@ public class DefaultImageDecoderTest {
private static final String PNG_TEST_IMAGE_PATH = "media/png/non-motion-photo-shortened.png";
private static final String JPEG_TEST_IMAGE_PATH = "media/jpeg/non-motion-photo-shortened.jpg";
private DefaultImageDecoder imageDecoder;
private DefaultImageDecoder decoder;
private DecoderInputBuffer inputBuffer;
private ImageOutputBuffer outputBuffer;
@Before
public void setUp() {
imageDecoder = new DefaultImageDecoder();
decoder = new DefaultImageDecoder.Factory().createImageDecoder();
inputBuffer = decoder.createInputBuffer();
outputBuffer = decoder.createOutputBuffer();
}
@After
public void tearDown() {
imageDecoder.release();
decoder.release();
}
@Test
@ -53,7 +60,7 @@ public class DefaultImageDecoderTest {
byte[] imageData =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), PNG_TEST_IMAGE_PATH);
Bitmap bitmap = imageDecoder.decode(imageData, imageData.length);
Bitmap bitmap = decode(imageData);
assertThat(
bitmap.sameAs(
@ -79,14 +86,22 @@ public class DefaultImageDecoderTest {
rotationMatrix,
/* filter= */ false);
Bitmap actualBitmap = imageDecoder.decode(imageData, imageData.length);
Bitmap actualBitmap = decode(imageData);
assertThat(actualBitmap.sameAs(expectedBitmap)).isTrue();
}
@Test
public void decodeBitmap_withInvalidData_throws() throws ImageDecoderException {
assertThrows(
ImageDecoderException.class, () -> imageDecoder.decode(new byte[1], /* length= */ 1));
assertThrows(ImageDecoderException.class, () -> decode(new byte[1]));
}
private Bitmap decode(byte[] data) throws Exception {
inputBuffer.data = ByteBuffer.wrap(data);
Exception e = decoder.decode(inputBuffer, outputBuffer, /* reset= */ false);
if (e != null) {
throw e;
}
return checkNotNull(outputBuffer.bitmap);
}
}

View File

@ -45,13 +45,27 @@ import java.nio.ByteBuffer;
* alongside one timestamp)).
*/
@UnstableApi
public class DefaultImageDecoder
public final class DefaultImageDecoder
extends SimpleDecoder<DecoderInputBuffer, ImageOutputBuffer, ImageDecoderException>
implements ImageDecoder {
/** A functional interface for turning byte arrays into bitmaps. */
public interface BitmapDecoder {
/**
* Decodes data into a {@link Bitmap}.
*
* @param data An array holding the data to be decoded, starting at position 0.
* @param length The length of the input to be decoded.
* @return The decoded {@link Bitmap}.
* @throws ImageDecoderException If a decoding error occurs.
*/
Bitmap decode(byte[] data, int length) throws ImageDecoderException;
}
/** A factory for {@link DefaultImageDecoder} instances. */
public static final class Factory implements ImageDecoder.Factory {
private final BitmapDecoder bitmapDecoder;
private static final ImmutableSet<String> SUPPORTED_IMAGE_TYPES =
ImmutableSet.of(
MimeTypes.IMAGE_PNG,
@ -60,6 +74,22 @@ public class DefaultImageDecoder
MimeTypes.IMAGE_HEIF,
MimeTypes.IMAGE_WEBP);
/**
* Creates an instance using a {@link BitmapFactory} implementation of {@link BitmapDecoder}.
*/
public Factory() {
this.bitmapDecoder = DefaultImageDecoder::decode;
}
/**
* Creates an instance.
*
* @param bitmapDecoder The {@link BitmapDecoder} used to turn a byte arrays into a bitmap.
*/
public Factory(BitmapDecoder bitmapDecoder) {
this.bitmapDecoder = bitmapDecoder;
}
@Override
public @RendererCapabilities.Capabilities int supportsFormat(Format format) {
if (!MimeTypes.isImage(format.containerMimeType)) {
@ -75,13 +105,15 @@ public class DefaultImageDecoder
@Override
public DefaultImageDecoder createImageDecoder() {
return new DefaultImageDecoder();
return new DefaultImageDecoder(bitmapDecoder);
}
}
/** Creates an instance. */
public DefaultImageDecoder() {
private final BitmapDecoder bitmapDecoder;
private DefaultImageDecoder(BitmapDecoder bitmapDecoder) {
super(new DecoderInputBuffer[1], new ImageOutputBuffer[1]);
this.bitmapDecoder = bitmapDecoder;
}
@Override
@ -117,7 +149,7 @@ public class DefaultImageDecoder
ByteBuffer inputData = checkNotNull(inputBuffer.data);
checkState(inputData.hasArray());
checkArgument(inputData.arrayOffset() == 0);
outputBuffer.bitmap = decode(inputData.array(), inputData.remaining());
outputBuffer.bitmap = bitmapDecoder.decode(inputData.array(), inputData.remaining());
outputBuffer.timeUs = inputBuffer.timeUs;
return null;
} catch (ImageDecoderException e) {
@ -133,7 +165,7 @@ public class DefaultImageDecoder
* @return The decoded {@link Bitmap}.
* @throws ImageDecoderException If a decoding error occurs.
*/
/* package */ Bitmap decode(byte[] data, int length) throws ImageDecoderException {
private static Bitmap decode(byte[] data, int length) throws ImageDecoderException {
@Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, length);
if (bitmap == null) {
throw new ImageDecoderException(

View File

@ -42,22 +42,17 @@ public class DefaultImageDecoderBufferQueueTest {
private Bitmap decodedBitmap1;
private Bitmap decodedBitmap2;
public int decodeCallCount;
@Before
public void setUp() throws Exception {
decodeCallCount = 0;
decodedBitmap1 = Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888);
decodedBitmap2 = Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888);
fakeImageDecoder =
new DefaultImageDecoder() {
public int decodeCallCount;
/** Overrides the decode method to fake it. */
@Override
protected Bitmap decode(byte[] data, int length) {
decodeCallCount++;
return decodeCallCount == 1 ? decodedBitmap1 : decodedBitmap2;
}
};
new DefaultImageDecoder.Factory(
(data, length) -> ++decodeCallCount == 1 ? decodedBitmap1 : decodedBitmap2)
.createImageDecoder();
}
@After