mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add BitmapDecoder interface
PiperOrigin-RevId: 557827954
This commit is contained in:
parent
b814404c56
commit
a8944ef2f0
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user