Add Gainmap bitmap & imagedecoder
Bug: 266628247
Test: builds & boots
Change-Id: I0da44e0c48cf8a6b6f95e3b62f6d5f74bd6c1eab
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index e60506f..046373d 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -93,6 +93,7 @@
private boolean mRecycled;
private ColorSpace mColorSpace;
+ private Gainmap mGainmap;
/*package*/ int mDensity = getDefaultDensity();
@@ -1897,6 +1898,27 @@
}
/**
+ * 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 @@
private static native void nativeSetImmutable(long nativePtr);
+ private static native Gainmap nativeExtractGainmap(long nativePtr);
+
// ---------------- @CriticalNative -------------------
@CriticalNative
@@ -2387,4 +2411,7 @@
@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 0000000..a25a605
--- /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 8d4bda2..dd6c2bc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -347,6 +347,7 @@
"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 0000000..765f98a
--- /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 f57d80c..c509ed4 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -55,6 +55,7 @@
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 @@
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 feafc23..0a755f0 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 @@
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 133f1fe..912d311 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 @@
};
namespace uirenderer {
+class Gainmap;
namespace renderthread {
class RenderThread;
}
@@ -119,6 +124,11 @@
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 @@
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 dd68f82..b1abe85 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 @@
}
bool ImageDecoder::swapWidthHeight() const {
- return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin());
+ return SkEncodedOriginSwapsWidthHeight(getOrigin());
}
int ImageDecoder::width() const {
@@ -316,7 +321,7 @@
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 @@
// 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 @@
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 @@
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 b6d73b3..97573e1 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -79,6 +79,8 @@
// 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 @@
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 @@
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
old mode 100755
new mode 100644
index c68a6b9..10c287d
--- 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 @@
namespace android {
+jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap);
+
class BitmapWrapper {
public:
explicit BitmapWrapper(Bitmap* bitmap)
@@ -1251,68 +1254,77 @@
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},
+ {"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}
+ // ------------ @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 0000000..f2efbc7
--- /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 bad710d..add62b1 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 @@
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 @@
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 @@
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);
}