Move bitmap decoding out of datasource util
PiperOrigin-RevId: 619139208
This commit is contained in:
parent
65e94480f4
commit
ef2314c404
@ -23,6 +23,7 @@ import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.test.utils.TestUtil;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -114,9 +115,7 @@ public class DataSourceBitmapLoaderTest {
|
||||
ListenableFuture<Bitmap> future = bitmapLoader.decodeBitmap(new byte[0]);
|
||||
|
||||
assertException(
|
||||
future::get,
|
||||
IllegalArgumentException.class,
|
||||
/* messagePart= */ "Could not decode image data");
|
||||
future::get, ParserException.class, /* messagePart= */ "Could not decode image data");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.datasource;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/** Utility methods for {@link Bitmap} instances. */
|
||||
@UnstableApi
|
||||
public final class BitmapUtil {
|
||||
|
||||
private BitmapUtil() {}
|
||||
|
||||
/**
|
||||
* Decodes a {@link Bitmap} from a byte array using {@link BitmapFactory} and the {@link
|
||||
* ExifInterface}.
|
||||
*
|
||||
* @param data Byte array of compressed image data.
|
||||
* @param length The number of bytes to parse.
|
||||
* @param options the {@link BitmapFactory.Options} to decode the {@code data} with.
|
||||
* @throws ParserException if the {@code data} could not be decoded.
|
||||
*/
|
||||
// BitmapFactory's options parameter is null-ok.
|
||||
@SuppressWarnings("nullness:argument.type.incompatible")
|
||||
public static Bitmap decode(byte[] data, int length, @Nullable BitmapFactory.Options options)
|
||||
throws IOException {
|
||||
@Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, length, options);
|
||||
if (bitmap == null) {
|
||||
throw ParserException.createForMalformedContainer(
|
||||
"Could not decode image data", new IllegalStateException());
|
||||
}
|
||||
// BitmapFactory doesn't read the exif header, so we use the ExifInterface to this do ensure the
|
||||
// bitmap is correctly orientated.
|
||||
ExifInterface exifInterface;
|
||||
try (InputStream inputStream = new ByteArrayInputStream(data)) {
|
||||
exifInterface = new ExifInterface(inputStream);
|
||||
}
|
||||
int rotationDegrees = exifInterface.getRotationDegrees();
|
||||
if (rotationDegrees != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postRotate(rotationDegrees);
|
||||
bitmap =
|
||||
Bitmap.createBitmap(
|
||||
bitmap,
|
||||
/* x= */ 0,
|
||||
/* y= */ 0,
|
||||
bitmap.getWidth(),
|
||||
bitmap.getHeight(),
|
||||
matrix,
|
||||
/* filter= */ false);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
}
|
@ -96,7 +96,7 @@ public final class DataSourceBitmapLoader implements BitmapLoader {
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Bitmap> decodeBitmap(byte[] data) {
|
||||
return listeningExecutorService.submit(() -> DataSourceUtil.decode(data, data.length, options));
|
||||
return listeningExecutorService.submit(() -> BitmapUtil.decode(data, data.length, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -111,7 +111,7 @@ public final class DataSourceBitmapLoader implements BitmapLoader {
|
||||
DataSpec dataSpec = new DataSpec(uri);
|
||||
dataSource.open(dataSpec);
|
||||
byte[] readData = DataSourceUtil.readToEnd(dataSource);
|
||||
return DataSourceUtil.decode(readData, readData.length, options);
|
||||
return BitmapUtil.decode(readData, readData.length, options);
|
||||
} finally {
|
||||
dataSource.close();
|
||||
}
|
||||
|
@ -15,24 +15,15 @@
|
||||
*/
|
||||
package androidx.media3.datasource;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Utility methods for {@link DataSource}. */
|
||||
@UnstableApi
|
||||
public final class DataSourceUtil {
|
||||
public static final String BITMAP_DECODING_EXCEPTION_MESSAGE = "Could not decode image data";
|
||||
|
||||
private DataSourceUtil() {}
|
||||
|
||||
@ -99,37 +90,4 @@ public final class DataSourceUtil {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a {@link Bitmap} from a byte array using {@link BitmapFactory} and the {@link
|
||||
* ExifInterface}.
|
||||
*/
|
||||
// BitmapFactory's options parameter is null-ok.
|
||||
@SuppressWarnings("nullness:argument.type.incompatible")
|
||||
public static Bitmap decode(byte[] data, int length, @Nullable BitmapFactory.Options options)
|
||||
throws IOException {
|
||||
@Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, length, options);
|
||||
checkArgument(bitmap != null, BITMAP_DECODING_EXCEPTION_MESSAGE);
|
||||
// BitmapFactory doesn't read the exif header, so we use the ExifInterface to this do ensure the
|
||||
// bitmap is correctly orientated.
|
||||
ExifInterface exifInterface;
|
||||
try (InputStream inputStream = new ByteArrayInputStream(data)) {
|
||||
exifInterface = new ExifInterface(inputStream);
|
||||
}
|
||||
int rotationDegrees = exifInterface.getRotationDegrees();
|
||||
if (rotationDegrees != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postRotate(rotationDegrees);
|
||||
bitmap =
|
||||
Bitmap.createBitmap(
|
||||
bitmap,
|
||||
/* x= */ 0,
|
||||
/* y= */ 0,
|
||||
bitmap.getWidth(),
|
||||
bitmap.getHeight(),
|
||||
matrix,
|
||||
/* filter= */ false);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.datasource.DataSourceUtil;
|
||||
import androidx.media3.datasource.BitmapUtil;
|
||||
import androidx.media3.decoder.DecoderInputBuffer;
|
||||
import androidx.media3.decoder.SimpleDecoder;
|
||||
import androidx.media3.exoplayer.RendererCapabilities;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An image decoder that uses {@link BitmapFactory} to decode images.
|
||||
@ -159,20 +159,17 @@ public final class BitmapFactoryImageDecoder
|
||||
*/
|
||||
private static Bitmap decode(byte[] data, int length) throws ImageDecoderException {
|
||||
try {
|
||||
return DataSourceUtil.decode(data, length, /* options= */ null);
|
||||
return BitmapUtil.decode(data, length, /* options= */ null);
|
||||
} catch (ParserException e) {
|
||||
throw new ImageDecoderException(
|
||||
"Could not decode image data with BitmapFactory. (data.length = "
|
||||
+ data.length
|
||||
+ ", input length = "
|
||||
+ length
|
||||
+ ")",
|
||||
e);
|
||||
} catch (IOException e) {
|
||||
throw new ImageDecoderException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (Objects.equals(e.getMessage(), DataSourceUtil.BITMAP_DECODING_EXCEPTION_MESSAGE)) {
|
||||
throw new ImageDecoderException(
|
||||
"Could not decode image data with BitmapFactory. (data.length = "
|
||||
+ data.length
|
||||
+ ", input length = "
|
||||
+ length
|
||||
+ ")");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.datasource.DataSourceBitmapLoader;
|
||||
import androidx.media3.datasource.HttpDataSource;
|
||||
import androidx.media3.test.utils.TestUtil;
|
||||
@ -124,7 +125,7 @@ public class CacheBitmapLoaderTest {
|
||||
assertThat(future1).isSameInstanceAs(future2);
|
||||
ExecutionException executionException =
|
||||
assertThrows(ExecutionException.class, () -> future1.get(10, SECONDS));
|
||||
assertThat(executionException).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(executionException).hasCauseThat().isInstanceOf(ParserException.class);
|
||||
assertThat(executionException).hasMessageThat().contains("Could not decode image data");
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user