diff options
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/jni/android/graphics/ImageDecoder.cpp | 15 | ||||
-rw-r--r-- | graphics/java/android/graphics/ImageDecoder.java | 95 |
3 files changed, 94 insertions, 20 deletions
diff --git a/api/current.txt b/api/current.txt index a01ef0d4a09c..2837091a400b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13643,7 +13643,8 @@ package android.graphics { method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor); method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean); - method public android.graphics.ImageDecoder setSampleSize(int); + method public android.graphics.ImageDecoder setTargetColorSpace(android.graphics.ColorSpace); + method public android.graphics.ImageDecoder setTargetSampleSize(int); method public android.graphics.ImageDecoder setTargetSize(int, int); field public static final int ALLOCATOR_DEFAULT = 0; // 0x0 field public static final int ALLOCATOR_HARDWARE = 3; // 0x3 @@ -13660,6 +13661,7 @@ package android.graphics { } public static class ImageDecoder.ImageInfo { + method public android.graphics.ColorSpace getColorSpace(); method public java.lang.String getMimeType(); method public android.util.Size getSize(); method public boolean isAnimated(); diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 726c450a4af2..825b7a0a9884 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -210,7 +210,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong jint desiredWidth, jint desiredHeight, jobject jsubset, jboolean requireMutable, jint allocator, jboolean requireUnpremul, jboolean preferRamOverQuality, - jboolean asAlphaMask) { + jboolean asAlphaMask, jobject jcolorSpace) { auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); SkAndroidCodec* codec = decoder->mCodec.get(); const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight); @@ -264,7 +264,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong // This is currently the only way to know that we should decode to F16. colorType = codec->computeOutputColorType(colorType); } - sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType); + sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace); + colorSpace = codec->computeOutputColorSpace(colorType, colorSpace); decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace); SkBitmap bm; @@ -507,18 +508,26 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n return encodedFormatToString(env, decoder->mCodec->getEncodedFormat()); } +static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get(); + auto colorType = codec->computeOutputColorType(codec->getInfo().colorType()); + sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType); + return GraphicsJNI::getColorSpace(env, colorSpace, colorType); +} + static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, - { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", + { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, { "nClose", "(J)V", (void*) ImageDecoder_nClose}, { "nGetMimeType", "(J)Ljava/lang/String;", (void*) ImageDecoder_nGetMimeType }, + { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*) ImageDecoder_nGetColorSpace }, }; int register_android_graphics_ImageDecoder(JNIEnv* env) { diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 6939907bb419..6051f88733bf 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -432,6 +432,18 @@ public final class ImageDecoder implements AutoCloseable { public boolean isAnimated() { return mDecoder.mAnimated; } + + /** + * If known, the color space the decoded bitmap will have. Note that the + * output color space is not guaranteed to be the color space the bitmap + * is encoded with. If not known (when the config is + * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error, + * it is set to null. + */ + @Nullable + public ColorSpace getColorSpace() { + return mDecoder.getColorSpace(); + } }; /** @removed @@ -582,16 +594,17 @@ public final class ImageDecoder implements AutoCloseable { private final int mHeight; private final boolean mAnimated; - private int mDesiredWidth; - private int mDesiredHeight; - private int mAllocator = ALLOCATOR_DEFAULT; - private boolean mRequireUnpremultiplied = false; - private boolean mMutable = false; - private boolean mConserveMemory = false; - private boolean mDecodeAsAlphaMask = false; - private Rect mCropRect; - private Rect mOutPaddingRect; - private Source mSource; + private int mDesiredWidth; + private int mDesiredHeight; + private int mAllocator = ALLOCATOR_DEFAULT; + private boolean mRequireUnpremultiplied = false; + private boolean mMutable = false; + private boolean mConserveMemory = false; + private boolean mDecodeAsAlphaMask = false; + private ColorSpace mDesiredColorSpace = null; + private Rect mCropRect; + private Rect mOutPaddingRect; + private Source mSource; private PostProcessor mPostProcessor; private OnPartialImageListener mOnPartialImageListener; @@ -806,7 +819,8 @@ public final class ImageDecoder implements AutoCloseable { * image, which can be retrieved from the {@link ImageInfo} in * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * <p>Only the last call to this or {@link #setSampleSize} is respected.</p> + * <p>Only the last call to this or {@link #setTargetSampleSize} is + * respected.</p> * * @param width must be greater than 0. * @param height must be greater than 0. @@ -824,11 +838,11 @@ public final class ImageDecoder implements AutoCloseable { } /** @removed - * @deprecated Renamed to {@link #setSampleSize}. + * @deprecated Renamed to {@link #setTargetSampleSize}. */ @java.lang.Deprecated public ImageDecoder setResize(int sampleSize) { - return this.setSampleSize(sampleSize); + return this.setTargetSampleSize(sampleSize); } private int getTargetDimension(int original, int sampleSize, int computed) { @@ -877,7 +891,7 @@ public final class ImageDecoder implements AutoCloseable { * @param sampleSize Sampling rate of the encoded image. * @return this object for chaining. */ - public ImageDecoder setSampleSize(int sampleSize) { + public ImageDecoder setTargetSampleSize(int sampleSize) { Size size = this.getSampledSize(sampleSize); int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth()); int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight()); @@ -1179,6 +1193,37 @@ public final class ImageDecoder implements AutoCloseable { return this.getDecodeAsAlphaMask(); } + /** + * Specify the desired {@link ColorSpace} for the output. + * + * <p>If non-null, the decoder will try to decode into this + * color space. If it is null, which is the default, or the request cannot + * be met, the decoder will pick either the color space embedded in the + * image or the color space best suited for the requested image + * configuration (for instance {@link ColorSpace.Named#SRGB sRGB} for + * the {@link Bitmap.Config#ARGB_8888} configuration).</p> + * + * <p>{@link Bitmap.Config#RGBA_F16} always uses the + * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space). + * Bitmaps in other configurations without an embedded color space are + * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p> + * + * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are + * currently supported. An <code>IllegalArgumentException</code> will + * be thrown by the decode methods when setting a non-RGB color space + * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p> + * + * <p class="note">The specified color space's transfer function must be + * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An + * <code>IllegalArgumentException</code> will be thrown by the decode methods + * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the + * specified color space returns null.</p> + */ + public ImageDecoder setTargetColorSpace(ColorSpace colorSpace) { + mDesiredColorSpace = colorSpace; + return this; + } + @Override public void close() { mCloseGuard.close(); @@ -1217,6 +1262,17 @@ public final class ImageDecoder implements AutoCloseable { if (mPostProcessor != null && mRequireUnpremultiplied) { throw new IllegalStateException("Cannot draw to unpremultiplied pixels!"); } + + if (mDesiredColorSpace != null) { + if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) { + throw new IllegalArgumentException("The target color space must use the " + + "RGB color model - provided: " + mDesiredColorSpace); + } + if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) { + throw new IllegalArgumentException("The target color space must use an " + + "ICC parametric transfer function - provided: " + mDesiredColorSpace); + } + } } private static void checkSubset(int width, int height, Rect r) { @@ -1235,7 +1291,7 @@ public final class ImageDecoder implements AutoCloseable { return nDecodeBitmap(mNativePtr, this, mPostProcessor != null, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, - mConserveMemory, mDecodeAsAlphaMask); + mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace); } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -1425,6 +1481,11 @@ public final class ImageDecoder implements AutoCloseable { return nGetMimeType(mNativePtr); } + @Nullable + private ColorSpace getColorSpace() { + return nGetColorSpace(mNativePtr); + } + /** * See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. */ @@ -1474,11 +1535,13 @@ public final class ImageDecoder implements AutoCloseable { int width, int height, @Nullable Rect cropRect, boolean mutable, int allocator, boolean requireUnpremul, - boolean conserveMemory, boolean decodeAsAlphaMask) + boolean conserveMemory, boolean decodeAsAlphaMask, + @Nullable ColorSpace desiredColorSpace) throws IOException; private static native Size nGetSampledSize(long nativePtr, int sampleSize); private static native void nGetPadding(long nativePtr, @NonNull Rect outRect); private static native void nClose(long nativePtr); private static native String nGetMimeType(long nativePtr); + private static native ColorSpace nGetColorSpace(long nativePtr); } |