From 65327c05c4040a580edba906d8ac517a664d63fd Mon Sep 17 00:00:00 2001 From: Jernej Virag Date: Wed, 3 May 2023 14:30:45 +0200 Subject: [PATCH] Add ability to decode only image information with ImageDecoder This creates a @hide API which only retrieves ImageInfo object without continuing the decode of full image. It mirrors BitmapOptions.inJustDecodeBounds behaviour and allows checking dimensions and color space configuration of images without proceeding to full decode (if able). Bug: 280572656 Test: atest android.graphics.ImageDecoderTest android.graphics.cts.ImageDecoderTest Change-Id: I704ec6c41d76f655222da7ee322e3b0e4954533d --- .../res/drawable-nodpi/animated_webp.webp | Bin 0 -> 24754 bytes .../android/graphics/ImageDecoderTest.java | 80 ++++++++++++++++++ .../java/android/graphics/ImageDecoder.java | 61 ++++++++++--- 3 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 core/tests/coretests/res/drawable-nodpi/animated_webp.webp create mode 100644 core/tests/coretests/src/android/graphics/ImageDecoderTest.java diff --git a/core/tests/coretests/res/drawable-nodpi/animated_webp.webp b/core/tests/coretests/res/drawable-nodpi/animated_webp.webp new file mode 100644 index 0000000000000000000000000000000000000000..2d28dbfd380b85bb66a94b964ab94f51d6256ef2 GIT binary patch literal 24754 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 literal 0 HcmV?d00001 diff --git a/core/tests/coretests/src/android/graphics/ImageDecoderTest.java b/core/tests/coretests/src/android/graphics/ImageDecoderTest.java new file mode 100644 index 0000000000000..8b3e6ba283b2e --- /dev/null +++ b/core/tests/coretests/src/android/graphics/ImageDecoderTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 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 android.graphics; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.frameworks.coretests.R; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ImageDecoderTest { + + private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); + + @Test + public void onDecodeHeader_png_returnsPopulatedData() throws IOException { + ImageDecoder.Source src = + ImageDecoder.createSource(mContext.getResources(), R.drawable.gettysburg); + ImageDecoder.ImageInfo info = ImageDecoder.decodeHeader(src); + assertThat(info.getSize().getWidth()).isEqualTo(432); + assertThat(info.getSize().getHeight()).isEqualTo(291); + assertThat(info.getMimeType()).isEqualTo("image/png"); + assertThat(info.getColorSpace()).isNotNull(); + assertThat(info.getColorSpace().getModel()).isEqualTo(ColorSpace.Model.RGB); + assertThat(info.getColorSpace().getId()).isEqualTo(0); + assertThat(info.isAnimated()).isFalse(); + } + + @Test + public void onDecodeHeader_animatedWebP_returnsPopulatedData() throws IOException { + ImageDecoder.Source src = + ImageDecoder.createSource(mContext.getResources(), R.drawable.animated_webp); + ImageDecoder.ImageInfo info = ImageDecoder.decodeHeader(src); + assertThat(info.getSize().getWidth()).isEqualTo(278); + assertThat(info.getSize().getHeight()).isEqualTo(183); + assertThat(info.getMimeType()).isEqualTo("image/webp"); + assertThat(info.getColorSpace()).isNotNull(); + assertThat(info.getColorSpace().getModel()).isEqualTo(ColorSpace.Model.RGB); + assertThat(info.getColorSpace().getId()).isEqualTo(0); + assertThat(info.isAnimated()).isTrue(); + } + + @Test(expected = IOException.class) + public void onDecodeHeader_invalidSource_throwsException() throws IOException { + ImageDecoder.Source src = ImageDecoder.createSource(new File("/this/file/does/not/exist")); + ImageDecoder.decodeHeader(src); + } + + @Test(expected = IOException.class) + public void onDecodeHeader_invalidResource_throwsException() throws IOException { + ImageDecoder.Source src = + ImageDecoder.createSource(mContext.getResources(), R.drawable.box); + ImageDecoder.decodeHeader(src); + } +} diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index dd4b58eb83dc0..b2da233fc21ec 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -627,11 +627,19 @@ public final class ImageDecoder implements AutoCloseable { */ public static class ImageInfo { private final Size mSize; - private ImageDecoder mDecoder; + private final boolean mIsAnimated; + private final String mMimeType; + private final ColorSpace mColorSpace; - private ImageInfo(@NonNull ImageDecoder decoder) { - mSize = new Size(decoder.mWidth, decoder.mHeight); - mDecoder = decoder; + private ImageInfo( + @NonNull Size size, + boolean isAnimated, + @NonNull String mimeType, + @Nullable ColorSpace colorSpace) { + mSize = size; + mIsAnimated = isAnimated; + mMimeType = mimeType; + mColorSpace = colorSpace; } /** @@ -647,7 +655,7 @@ public final class ImageDecoder implements AutoCloseable { */ @NonNull public String getMimeType() { - return mDecoder.getMimeType(); + return mMimeType; } /** @@ -657,7 +665,7 @@ public final class ImageDecoder implements AutoCloseable { * return an {@link AnimatedImageDrawable}.

*/ public boolean isAnimated() { - return mDecoder.mAnimated; + return mIsAnimated; } /** @@ -669,7 +677,7 @@ public final class ImageDecoder implements AutoCloseable { */ @Nullable public ColorSpace getColorSpace() { - return mDecoder.getColorSpace(); + return mColorSpace; } }; @@ -1798,12 +1806,39 @@ public final class ImageDecoder implements AutoCloseable { private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @NonNull Source src) { if (listener != null) { - ImageInfo info = new ImageInfo(this); - try { - listener.onHeaderDecoded(this, info, src); - } finally { - info.mDecoder = null; - } + ImageInfo info = + new ImageInfo( + new Size(mWidth, mHeight), mAnimated, getMimeType(), getColorSpace()); + listener.onHeaderDecoded(this, info, src); + } + } + + /** + * Return {@link ImageInfo} from a {@code Source}. + * + *

Returns the same {@link ImageInfo} object that a usual decoding process would return as + * part of {@link OnHeaderDecodedListener}. + * + * @param src representing the encoded image. + * @return ImageInfo describing the image. + * @throws IOException if {@code src} is not found, is an unsupported format, or cannot be + * decoded for any reason. + * @hide + */ + @WorkerThread + @NonNull + public static ImageInfo decodeHeader(@NonNull Source src) throws IOException { + Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeHeader"); + try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) { + // We don't want to leak decoder so resolve all properties immediately. + return new ImageInfo( + new Size(decoder.mWidth, decoder.mHeight), + decoder.mAnimated, + decoder.getMimeType(), + decoder.getColorSpace()); + } finally { + // Close the ImageDecoder#decodeHeader trace. + Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } }