diff options
author | 2023-01-24 20:13:45 -0500 | |
---|---|---|
committer | 2023-01-24 20:46:45 -0500 | |
commit | 5bd537ea140e393ba64421cd8da168736c3b269e (patch) | |
tree | 6a2c4414f81ac6806fb8e5178fa6a1ad40cabc9d | |
parent | 9ad8f025c8108b7c23f93687bd3315744f38f162 (diff) |
Add Gainmap bitmap & imagedecoder
Bug: 266628247
Test: builds & boots
Change-Id: I0da44e0c48cf8a6b6f95e3b62f6d5f74bd6c1eab
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 27 | ||||
-rw-r--r-- | graphics/java/android/graphics/Gainmap.java | 127 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/Gainmap.h | 32 | ||||
-rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Bitmap.cpp | 12 | ||||
-rw-r--r-- | libs/hwui/hwui/Bitmap.h | 12 | ||||
-rw-r--r-- | libs/hwui/hwui/ImageDecoder.cpp | 88 | ||||
-rw-r--r-- | libs/hwui/hwui/ImageDecoder.h | 7 | ||||
-rw-r--r--[-rwxr-xr-x] | libs/hwui/jni/Bitmap.cpp | 130 | ||||
-rw-r--r-- | libs/hwui/jni/Gainmap.cpp | 125 | ||||
-rw-r--r-- | libs/hwui/jni/ImageDecoder.cpp | 45 |
12 files changed, 529 insertions, 79 deletions
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index e60506ff88ef..046373d671af 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -93,6 +93,7 @@ public final class Bitmap implements Parcelable { private boolean mRecycled; private ColorSpace mColorSpace; + private Gainmap mGainmap; /*package*/ int mDensity = getDefaultDensity(); @@ -1897,6 +1898,27 @@ public final class Bitmap implements Parcelable { } /** + * Returns whether or not this Bitmap contains a Gainmap. + * @hide + */ + public boolean hasGainmap() { + checkRecycled("Bitmap is recycled"); + return nativeHasGainmap(mNativePtr); + } + + /** + * Returns the gainmap or null if the bitmap doesn't contain a gainmap + * @hide + */ + public @Nullable Gainmap getGainmap() { + checkRecycled("Bitmap is recycled"); + if (mGainmap == null) { + mGainmap = nativeExtractGainmap(mNativePtr); + } + return mGainmap; + } + + /** * Fills the bitmap's pixels with the specified {@link Color}. * * @throws IllegalStateException if the bitmap is not mutable. @@ -2380,6 +2402,8 @@ public final class Bitmap implements Parcelable { private static native void nativeSetImmutable(long nativePtr); + private static native Gainmap nativeExtractGainmap(long nativePtr); + // ---------------- @CriticalNative ------------------- @CriticalNative @@ -2387,4 +2411,7 @@ public final class Bitmap implements Parcelable { @CriticalNative private static native boolean nativeIsBackedByAshmem(long nativePtr); + + @CriticalNative + private static native boolean nativeHasGainmap(long nativePtr); } diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java new file mode 100644 index 000000000000..a25a60568510 --- /dev/null +++ b/graphics/java/android/graphics/Gainmap.java @@ -0,0 +1,127 @@ +/* + * 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 android.annotation.NonNull; + +import libcore.util.NativeAllocationRegistry; + +/** + * Gainmap represents a mechanism for augmenting an SDR image to produce an HDR one with variable + * display adjustment capability. + * + * It is a combination of a set of metadata describing the gainmap, as well as either a 1 or 3 + * channel Bitmap that represents the gainmap data itself. + * + * @hide + */ +public class Gainmap { + private final long mNativePtr; + private final Bitmap mGainmapImage; + + // called from JNI and Bitmap_Delegate. + private Gainmap(Bitmap gainmapImage, long nativeGainmap, int allocationByteCount, + boolean fromMalloc) { + if (nativeGainmap == 0) { + throw new RuntimeException("internal error: native gainmap is 0"); + } + + mGainmapImage = gainmapImage; + mNativePtr = nativeGainmap; + + final NativeAllocationRegistry registry; + if (fromMalloc) { + registry = NativeAllocationRegistry.createMalloced( + Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); + } else { + registry = NativeAllocationRegistry.createNonmalloced( + Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); + } + registry.registerNativeAllocation(this, nativeGainmap); + } + + /** + * Returns the image data of the gainmap represented as a Bitmap + * @return + */ + @NonNull + public Bitmap getGainmapImage() { + return mGainmapImage; + } + + /** + * Sets the gainmap max metadata. For single-plane gainmaps, r, g, and b should be the same. + */ + @NonNull + public void setGainmapMax(float r, float g, float b) { + nSetGainmapMax(mNativePtr, r, g, b); + } + + /** + * Gets the gainmap max metadata. For single-plane gainmaps, all 3 components should be the + * same. The components are in r, g, b order. + */ + @NonNull + public float[] getGainmapMax() { + float[] ret = new float[3]; + nGetGainmapMax(mNativePtr, ret); + return ret; + } + + /** + * Sets the maximum HDR ratio for the gainmap + */ + @NonNull + public void setHdrRatioMax(float max) { + nSetHdrRatioMax(mNativePtr, max); + } + + /** + * Gets the maximum HDR ratio for the gainmap + */ + @NonNull + public float getHdrRatioMax() { + return nGetHdrRatioMax(mNativePtr); + } + + /** + * Sets the maximum HDR ratio for the gainmap + */ + @NonNull + public void setHdrRatioMin(float min) { + nSetHdrRatioMin(mNativePtr, min); + } + + /** + * Gets the maximum HDR ratio for the gainmap + */ + @NonNull + public float getHdrRatioMin() { + return nGetHdrRatioMin(mNativePtr); + } + + private static native long nGetFinalizer(); + + private static native void nSetGainmapMax(long ptr, float r, float g, float b); + private static native void nGetGainmapMax(long ptr, float[] components); + + private static native void nSetHdrRatioMax(long ptr, float max); + private static native float nGetHdrRatioMax(long ptr); + + private static native void nSetHdrRatioMin(long ptr, float min); + private static native float nGetHdrRatioMin(long ptr); +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 8d4bda26c841..dd6c2bc326fd 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -347,6 +347,7 @@ cc_defaults { "jni/CreateJavaOutputStreamAdaptor.cpp", "jni/FontFamily.cpp", "jni/FontUtils.cpp", + "jni/Gainmap.cpp", "jni/Graphics.cpp", "jni/ImageDecoder.cpp", "jni/Interpolator.cpp", diff --git a/libs/hwui/Gainmap.h b/libs/hwui/Gainmap.h new file mode 100644 index 000000000000..765f98a257b8 --- /dev/null +++ b/libs/hwui/Gainmap.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#pragma once + +#include <SkGainmapInfo.h> +#include <SkImage.h> +#include <hwui/Bitmap.h> +#include <utils/LightRefBase.h> + +namespace android::uirenderer { + +class Gainmap : public LightRefBase<Gainmap> { +public: + SkGainmapInfo info; + sk_sp<Bitmap> bitmap; +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index f57d80c496ad..c509ed4dfd21 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -55,6 +55,7 @@ extern int register_android_graphics_ColorFilter(JNIEnv* env); extern int register_android_graphics_ColorSpace(JNIEnv* env); extern int register_android_graphics_DrawFilter(JNIEnv* env); extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_Gainmap(JNIEnv* env); extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env); extern int register_android_graphics_Matrix(JNIEnv* env); extern int register_android_graphics_Paint(JNIEnv* env); @@ -114,6 +115,7 @@ extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env); REG_JNI(register_android_graphics_ColorFilter), REG_JNI(register_android_graphics_DrawFilter), REG_JNI(register_android_graphics_FontFamily), + REG_JNI(register_android_graphics_Gainmap), REG_JNI(register_android_graphics_HardwareRendererObserver), REG_JNI(register_android_graphics_ImageDecoder), REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index feafc2372442..0a755f0d67fd 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -34,6 +34,7 @@ #include <binder/IServiceManager.h> #endif +#include <Gainmap.h> #include <SkCanvas.h> #include <SkColor.h> #include <SkEncodedImageFormat.h> @@ -44,6 +45,7 @@ #include <SkRect.h> #include <SkStream.h> #include <SkWebpEncoder.h> + #include <limits> namespace android { @@ -494,4 +496,14 @@ bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, return SkEncodeImage(stream, bitmap, fm, quality); } + +sp<uirenderer::Gainmap> Bitmap::gainmap() const { + LOG_ALWAYS_FATAL_IF(!hasGainmap(), "Bitmap doesn't have a gainmap"); + return mGainmap; +} + +void Bitmap::setGainmap(sp<uirenderer::Gainmap>&& gainmap) { + mGainmap = std::move(gainmap); +} + } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 133f1fe0a1e7..912d311c3678 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -23,6 +23,10 @@ #include <SkPixelRef.h> #include <SkRefCnt.h> #include <cutils/compiler.h> +#include <utils/StrongPointer.h> + +#include <optional> + #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include <android/hardware_buffer.h> #endif @@ -47,6 +51,7 @@ enum class BitmapPalette { }; namespace uirenderer { +class Gainmap; namespace renderthread { class RenderThread; } @@ -119,6 +124,11 @@ public: void getBounds(SkRect* bounds) const; bool isHardware() const { return mPixelStorageType == PixelStorageType::Hardware; } + bool hasGainmap() const { return mGainmap.get() != nullptr; } + + sp<uirenderer::Gainmap> gainmap() const; + + void setGainmap(sp<uirenderer::Gainmap>&& gainmap); PixelStorageType pixelStorageType() const { return mPixelStorageType; } @@ -193,6 +203,8 @@ private: bool mHasHardwareMipMap = false; + sp<uirenderer::Gainmap> mGainmap; + union { struct { SkPixelRef* pixelRef; diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index dd68f825b61d..b1abe8529afe 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -16,15 +16,20 @@ #include "ImageDecoder.h" -#include <hwui/Bitmap.h> -#include <log/log.h> - +#include <Gainmap.h> #include <SkAndroidCodec.h> #include <SkBitmap.h> #include <SkBlendMode.h> #include <SkCanvas.h> #include <SkEncodedOrigin.h> +#include <SkGainmapInfo.h> #include <SkPaint.h> +#include <SkStream.h> +#include <hwui/Bitmap.h> +#include <log/log.h> +#include <utils/Trace.h> + +#include <memory> #undef LOG_TAG #define LOG_TAG "ImageDecoder" @@ -195,7 +200,7 @@ SkImageInfo ImageDecoder::getOutputInfo() const { } bool ImageDecoder::swapWidthHeight() const { - return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin()); + return SkEncodedOriginSwapsWidthHeight(getOrigin()); } int ImageDecoder::width() const { @@ -316,7 +321,7 @@ SkCodec::FrameInfo ImageDecoder::getCurrentFrameInfo() { info.fFrameRect = SkIRect::MakeSize(dims); } - if (auto origin = mCodec->codec()->getOrigin(); origin != kDefault_SkEncodedOrigin) { + if (auto origin = getOrigin(); origin != kDefault_SkEncodedOrigin) { if (SkEncodedOriginSwapsWidthHeight(origin)) { dims = swapped(dims); } @@ -400,7 +405,7 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; const bool scale = mDecodeSize != mTargetSize; - const auto origin = mCodec->codec()->getOrigin(); + const auto origin = getOrigin(); const bool handleOrigin = origin != kDefault_SkEncodedOrigin; SkMatrix outputMatrix; if (scale || handleOrigin || mCropRect) { @@ -455,12 +460,15 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { mOptions.fZeroInitialized = SkCodec::kYes_ZeroInitialized; } + ATRACE_BEGIN("getAndroidPixels"); auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &mOptions); + ATRACE_END(); // The next call to decode() may not provide zero initialized memory. mOptions.fZeroInitialized = SkCodec::kNo_ZeroInitialized; if (scale || handleOrigin || mCropRect) { + ATRACE_NAME("Handling scale/origin/crop"); SkBitmap scaledBm; if (!scaledBm.installPixels(outputInfo, pixels, rowBytes)) { return SkCodec::kInternalError; @@ -478,3 +486,71 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { return result; } +SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) { + ATRACE_CALL(); + SkGainmapInfo gainmapInfo; + std::unique_ptr<SkStream> gainmapStream; + { + ATRACE_NAME("getAndroidGainmap"); + if (!mCodec->getAndroidGainmap(&gainmapInfo, &gainmapStream)) { + return SkCodec::kSuccess; + } + } + auto gainmapCodec = SkAndroidCodec::MakeFromStream(std::move(gainmapStream)); + if (!gainmapCodec) { + ALOGW("Failed to create codec for gainmap stream"); + return SkCodec::kInvalidInput; + } + ImageDecoder decoder{std::move(gainmapCodec)}; + // Gainmap inherits the origin of the containing image + decoder.mOverrideOrigin.emplace(getOrigin()); + + const bool isScaled = width() != mTargetSize.width() || height() != mTargetSize.height(); + + if (isScaled) { + float scaleX = (float)mTargetSize.width() / width(); + float scaleY = (float)mTargetSize.height() / height(); + decoder.setTargetSize(decoder.width() * scaleX, decoder.height() * scaleY); + } + + if (mCropRect) { + float sX = decoder.mTargetSize.width() / (float)mTargetSize.width(); + float sY = decoder.mTargetSize.height() / (float)mTargetSize.height(); + SkIRect crop = *mCropRect; + // TODO: Tweak rounding? + crop.fLeft *= sX; + crop.fRight *= sX; + crop.fTop *= sY; + crop.fBottom *= sY; + decoder.setCropRect(&crop); + } + + SkImageInfo bitmapInfo = decoder.getOutputInfo(); + + SkBitmap bm; + if (!bm.setInfo(bitmapInfo)) { + ALOGE("Failed to setInfo properly"); + return SkCodec::kInternalError; + } + + // TODO: We don't currently parcel the gainmap, but if we should then also support + // the shared allocator + sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bm); + if (!nativeBitmap) { + ALOGE("OOM allocating Bitmap with dimensions %i x %i", bitmapInfo.width(), + bitmapInfo.height()); + return SkCodec::kInternalError; + } + + SkCodec::Result result = decoder.decode(bm.getPixels(), bm.rowBytes()); + bm.setImmutable(); + + if (result == SkCodec::kSuccess) { + auto gainmap = sp<uirenderer::Gainmap>::make(); + gainmap->info = gainmapInfo; + gainmap->bitmap = std::move(nativeBitmap); + destination->setGainmap(std::move(gainmap)); + } + + return result; +} diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index b6d73b39d8d0..97573e1e8207 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -79,6 +79,8 @@ public: // Set whether the ImageDecoder should handle RestorePrevious frames. void setHandleRestorePrevious(bool handle); + SkCodec::Result extractGainmap(Bitmap* destination); + private: // State machine for keeping track of how to handle RestorePrevious (RP) // frames in decode(). @@ -115,6 +117,7 @@ private: RestoreState mRestoreState; sk_sp<Bitmap> mRestoreFrame; std::optional<SkIRect> mCropRect; + std::optional<SkEncodedOrigin> mOverrideOrigin; ImageDecoder(const ImageDecoder&) = delete; ImageDecoder& operator=(const ImageDecoder&) = delete; @@ -124,6 +127,10 @@ private: bool swapWidthHeight() const; // Store/restore a frame if necessary. Returns false on error. bool handleRestorePrevious(const SkImageInfo&, void* pixels, size_t rowBytes); + + SkEncodedOrigin getOrigin() const { + return mOverrideOrigin.has_value() ? *mOverrideOrigin : mCodec->codec()->getOrigin(); + } }; } // namespace android diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index c68a6b9962c2..10c287d1e07d 100755..100644 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -6,6 +6,7 @@ #include <hwui/Paint.h> #include "CreateJavaOutputStreamAdaptor.h" +#include "Gainmap.h" #include "GraphicsJNI.h" #include "HardwareBufferHelpers.h" #include "SkBitmap.h" @@ -47,6 +48,8 @@ static jmethodID gBitmap_reinitMethodID; namespace android { +jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap); + class BitmapWrapper { public: explicit BitmapWrapper(Bitmap* bitmap) @@ -1251,68 +1254,77 @@ static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) { return bitmapHolder->bitmap().setImmutable(); } +static jboolean Bitmap_hasGainmap(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return false; + + return bitmapHolder->bitmap().hasGainmap(); +} + +static jobject Bitmap_extractGainmap(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return nullptr; + if (!bitmapHolder->bitmap().hasGainmap()) return nullptr; + + return Gainmap_extractFromBitmap(env, bitmapHolder->bitmap()); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { - { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", - (void*)Bitmap_creator }, - { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", - (void*)Bitmap_copy }, - { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", - (void*)Bitmap_copyAshmem }, - { "nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", - (void*)Bitmap_copyAshmemConfig }, - { "nativeGetAshmemFD", "(J)I", (void*)Bitmap_getAshmemFd }, - { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer }, - { "nativeRecycle", "(J)V", (void*)Bitmap_recycle }, - { "nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure }, - { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", - (void*)Bitmap_compress }, - { "nativeErase", "(JI)V", (void*)Bitmap_erase }, - { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong }, - { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, - { "nativeConfig", "(J)I", (void*)Bitmap_config }, - { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, - { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, - { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, - { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, - { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, - { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, - { "nativeCreateFromParcel", - "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", - (void*)Bitmap_createFromParcel }, - { "nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", - (void*)Bitmap_writeToParcel }, - { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", - (void*)Bitmap_extractAlpha }, - { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, - { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel }, - { "nativeGetColor", "(JII)J", (void*)Bitmap_getColor }, - { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels }, - { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel }, - { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels }, - { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", - (void*)Bitmap_copyPixelsToBuffer }, - { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", - (void*)Bitmap_copyPixelsFromBuffer }, - { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, - { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, - { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount }, - { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;", - (void*)Bitmap_copyPreserveInternalConfig }, - { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", - (void*) Bitmap_wrapHardwareBufferBitmap }, - { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", - (void*) Bitmap_getHardwareBuffer }, - { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, - { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, - { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, - { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, - { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, - - // ------------ @CriticalNative ---------------- - { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable}, - { "nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem} + {"nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", (void*)Bitmap_creator}, + {"nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", (void*)Bitmap_copy}, + {"nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmem}, + {"nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmemConfig}, + {"nativeGetAshmemFD", "(J)I", (void*)Bitmap_getAshmemFd}, + {"nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer}, + {"nativeRecycle", "(J)V", (void*)Bitmap_recycle}, + {"nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure}, + {"nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress}, + {"nativeErase", "(JI)V", (void*)Bitmap_erase}, + {"nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong}, + {"nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes}, + {"nativeConfig", "(J)I", (void*)Bitmap_config}, + {"nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha}, + {"nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, + {"nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, + {"nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, + {"nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap}, + {"nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap}, + {"nativeCreateFromParcel", "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", + (void*)Bitmap_createFromParcel}, + {"nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", (void*)Bitmap_writeToParcel}, + {"nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", (void*)Bitmap_extractAlpha}, + {"nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId}, + {"nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel}, + {"nativeGetColor", "(JII)J", (void*)Bitmap_getColor}, + {"nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels}, + {"nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel}, + {"nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels}, + {"nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsToBuffer}, + {"nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer}, + {"nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs}, + {"nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw}, + {"nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount}, + {"nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyPreserveInternalConfig}, + {"nativeWrapHardwareBufferBitmap", + "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", + (void*)Bitmap_wrapHardwareBufferBitmap}, + {"nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", + (void*)Bitmap_getHardwareBuffer}, + {"nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", + (void*)Bitmap_computeColorSpace}, + {"nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace}, + {"nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB}, + {"nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, + {"nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, + {"nativeExtractGainmap", "(J)Landroid/graphics/Gainmap;", (void*)Bitmap_extractGainmap}, + + // ------------ @CriticalNative ---------------- + {"nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable}, + {"nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem}, + {"nativeHasGainmap", "(J)Z", (void*)Bitmap_hasGainmap}, }; diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp new file mode 100644 index 000000000000..f2efbc7b833e --- /dev/null +++ b/libs/hwui/jni/Gainmap.cpp @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#include <Gainmap.h> + +#include "Bitmap.h" +#include "GraphicsJNI.h" +#include "graphics_jni_helpers.h" + +namespace android { + +static jclass gGainmap_class; +static jmethodID gGainmap_constructorMethodID; + +using namespace uirenderer; + +static Gainmap* fromJava(jlong gainmap) { + return reinterpret_cast<Gainmap*>(gainmap); +} + +static int getCreateFlags(const sk_sp<Bitmap>& bitmap) { + int flags = 0; + if (bitmap->info().alphaType() == kPremul_SkAlphaType) { + flags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + } + if (!bitmap->isImmutable()) { + flags |= android::bitmap::kBitmapCreateFlag_Mutable; + } + return flags; +} + +jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap) { + auto gainmap = bitmap.gainmap(); + jobject jGainmapImage; + size_t allocationSize; + + { + // Scope to guard the release of nativeBitmap + auto nativeBitmap = gainmap->bitmap; + const int createFlags = getCreateFlags(nativeBitmap); + allocationSize = nativeBitmap->getAllocationByteCount(); + jGainmapImage = bitmap::createBitmap(env, nativeBitmap.release(), createFlags); + } + + // Grab a ref for the jobject + gainmap->incStrong(0); + jobject obj = env->NewObject(gGainmap_class, gGainmap_constructorMethodID, jGainmapImage, + gainmap.get(), allocationSize + sizeof(Gainmap), true); + + if (env->ExceptionCheck() != 0) { + // sadtrombone + gainmap->decStrong(0); + ALOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + } + return obj; +} + +static void Gainmap_destructor(Gainmap* gainmap) { + gainmap->decStrong(0); +} + +static jlong Gainmap_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Gainmap_destructor)); +} + +static void Gainmap_setGainmapMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g, + jfloat b) { + fromJava(gainmapPtr)->info.fLogRatioMax = {r, g, b, 1.f}; +} + +static void Gainmap_getGainmapMax(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) { + const auto ratioMax = fromJava(gainmapPtr)->info.fLogRatioMax; + jfloat buf[3]{ratioMax.fR, ratioMax.fG, ratioMax.fB}; + env->SetFloatArrayRegion(components, 0, 3, buf); +} + +static void Gainmap_setHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat max) { + fromJava(gainmapPtr)->info.fHdrRatioMax = max; +} + +static jfloat Gainmap_getHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr) { + return fromJava(gainmapPtr)->info.fHdrRatioMax; +} + +static void Gainmap_setHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr, jfloat min) { + fromJava(gainmapPtr)->info.fHdrRatioMin = min; +} + +static jfloat Gainmap_getHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr) { + return fromJava(gainmapPtr)->info.fHdrRatioMin; +} + +static const JNINativeMethod gGainmapMethods[] = { + {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer}, + {"nSetGainmapMax", "(JFFF)V", (void*)Gainmap_setGainmapMax}, + {"nGetGainmapMax", "(J[F)V", (void*)Gainmap_getGainmapMax}, + {"nSetHdrRatioMax", "(JF)V", (void*)Gainmap_setHdrRatioMax}, + {"nGetHdrRatioMax", "(J)F", (void*)Gainmap_getHdrRatioMax}, + {"nSetHdrRatioMin", "(JF)V", (void*)Gainmap_setHdrRatioMin}, + {"nGetHdrRatioMin", "(J)F", (void*)Gainmap_getHdrRatioMin}, +}; + +int register_android_graphics_Gainmap(JNIEnv* env) { + gGainmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Gainmap")); + gGainmap_constructorMethodID = + GetMethodIDOrDie(env, gGainmap_class, "<init>", "(Landroid/graphics/Bitmap;JIZ)V"); + return android::RegisterMethodsOrDie(env, "android/graphics/Gainmap", gGainmapMethods, + NELEM(gGainmapMethods)); +} + +} // namespace android diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index bad710dec274..add62b1f0d6d 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -14,20 +14,10 @@ * limitations under the License. */ -#include "Bitmap.h" -#include "BitmapFactory.h" -#include "ByteBufferStreamAdaptor.h" -#include "CreateJavaOutputStreamAdaptor.h" -#include "GraphicsJNI.h" #include "ImageDecoder.h" -#include "NinePatchPeeker.h" -#include "Utils.h" - -#include <hwui/Bitmap.h> -#include <hwui/ImageDecoder.h> -#include <HardwareBitmapUploader.h> #include <FrontBufferedStream.h> +#include <HardwareBitmapUploader.h> #include <SkAndroidCodec.h> #include <SkBitmap.h> #include <SkColorSpace.h> @@ -35,11 +25,22 @@ #include <SkRect.h> #include <SkStream.h> #include <SkString.h> - #include <androidfw/Asset.h> #include <fcntl.h> +#include <gui/TraceUtils.h> +#include <hwui/Bitmap.h> +#include <hwui/ImageDecoder.h> #include <sys/stat.h> +#include "Bitmap.h" +#include "BitmapFactory.h" +#include "ByteBufferStreamAdaptor.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "Gainmap.h" +#include "GraphicsJNI.h" +#include "NinePatchPeeker.h" +#include "Utils.h" + using namespace android; static jclass gImageDecoder_class; @@ -246,6 +247,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong jboolean requireUnpremul, jboolean preferRamOverQuality, jboolean asAlphaMask, jlong colorSpaceHandle, jboolean extended) { + ATRACE_CALL(); auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); if (!decoder->setTargetSize(targetWidth, targetHeight)) { doThrowISE(env, "Could not scale to target size!"); @@ -336,10 +338,21 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong return nullptr; } + ATRACE_FORMAT("Decoding %dx%d bitmap", bitmapInfo.width(), bitmapInfo.height()); SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes()); jthrowable jexception = get_and_clear_exception(env); - int onPartialImageError = jexception ? kSourceException - : 0; // No error. + int onPartialImageError = jexception ? kSourceException : 0; // No error. + + // Only attempt to extract the gainmap if we're not post-processing, as we can't automatically + // mimic that to the gainmap and expect it to be meaningful. And also don't extract the gainmap + // if we're prioritizing RAM over quality, since the gainmap improves quality at the + // cost of RAM + if (result == SkCodec::kSuccess && !jpostProcess && !preferRamOverQuality) { + // The gainmap costs RAM to improve quality, so skip this if we're prioritizing RAM instead + result = decoder->extractGainmap(nativeBitmap.get()); + jexception = get_and_clear_exception(env); + } + switch (result) { case SkCodec::kSuccess: // Ignore the exception, since the decode was successful anyway. @@ -450,6 +463,10 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm); if (hwBitmap) { hwBitmap->setImmutable(); + if (nativeBitmap->hasGainmap()) { + // TODO: Also convert to a HW gainmap image + hwBitmap->setGainmap(nativeBitmap->gainmap()); + } return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets); } |