Loading core/api/current.txt +23 −0 Original line number Diff line number Diff line Loading @@ -14933,6 +14933,7 @@ package android.graphics { method @Nullable public android.graphics.ColorSpace getColorSpace(); method @NonNull public android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method @Nullable public android.graphics.Gainmap getGainmap(); method public int getGenerationId(); method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer(); method public int getHeight(); Loading @@ -14948,6 +14949,7 @@ package android.graphics { method public int getScaledWidth(int); method public int getWidth(); method public boolean hasAlpha(); method public boolean hasGainmap(); method public boolean hasMipMap(); method public boolean isMutable(); method public boolean isPremultiplied(); Loading @@ -14959,6 +14961,7 @@ package android.graphics { method public void setColorSpace(@NonNull android.graphics.ColorSpace); method public void setConfig(@NonNull android.graphics.Bitmap.Config); method public void setDensity(int); method public void setGainmap(@Nullable android.graphics.Gainmap); method public void setHasAlpha(boolean); method public void setHasMipMap(boolean); method public void setHeight(int); Loading Loading @@ -15494,6 +15497,26 @@ package android.graphics { ctor @Deprecated public EmbossMaskFilter(float[], float, float, float); } public final class Gainmap { ctor public Gainmap(@NonNull android.graphics.Bitmap); method @NonNull public float getDisplayRatioForFullHdr(); method @NonNull public float[] getEpsilonHdr(); method @NonNull public float[] getEpsilonSdr(); method @NonNull public android.graphics.Bitmap getGainmapContents(); method @NonNull public float[] getGamma(); method @NonNull public float getMinDisplayRatioForHdrTransition(); method @NonNull public float[] getRatioMax(); method @NonNull public float[] getRatioMin(); method @NonNull public void setDisplayRatioForFullHdr(float); method @NonNull public void setEpsilonHdr(float, float, float); method @NonNull public void setEpsilonSdr(float, float, float); method public void setGainmapContents(@NonNull android.graphics.Bitmap); method @NonNull public void setGamma(float, float, float); method @NonNull public void setMinDisplayRatioForHdrTransition(@FloatRange(from=1.0f) float); method @NonNull public void setRatioMax(float, float, float); method @NonNull public void setRatioMin(float, float, float); } public class HardwareBufferRenderer implements java.lang.AutoCloseable { ctor public HardwareBufferRenderer(@NonNull android.hardware.HardwareBuffer); method public void close(); graphics/java/android/graphics/Bitmap.java +9 −2 Original line number Diff line number Diff line Loading @@ -1899,7 +1899,6 @@ public final class Bitmap implements Parcelable { /** * Returns whether or not this Bitmap contains a Gainmap. * @hide */ public boolean hasGainmap() { checkRecycled("Bitmap is recycled"); Loading @@ -1908,7 +1907,6 @@ public final class Bitmap implements Parcelable { /** * Returns the gainmap or null if the bitmap doesn't contain a gainmap * @hide */ public @Nullable Gainmap getGainmap() { checkRecycled("Bitmap is recycled"); Loading @@ -1918,6 +1916,14 @@ public final class Bitmap implements Parcelable { return mGainmap; } /** * Sets a gainmap on this bitmap, or removes the gainmap if null */ public void setGainmap(@Nullable Gainmap gainmap) { checkRecycled("Bitmap is recycled"); nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr); } /** * Fills the bitmap's pixels with the specified {@link Color}. * Loading Loading @@ -2403,6 +2409,7 @@ public final class Bitmap implements Parcelable { private static native void nativeSetImmutable(long nativePtr); private static native Gainmap nativeExtractGainmap(long nativePtr); private static native void nativeSetGainmap(long bitmapPtr, long gainmapPtr); // ---------------- @CriticalNative ------------------- Loading graphics/java/android/graphics/Gainmap.java +231 −46 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.graphics; import android.annotation.FloatRange; import android.annotation.NonNull; import libcore.util.NativeAllocationRegistry; Loading @@ -24,104 +25,288 @@ 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 * It is a combination of a set of metadata describing how to apply the gainmap, as well as either * a 1 (such as {@link android.graphics.Bitmap.Config#ALPHA_8} or 3 * (such as {@link android.graphics.Bitmap.Config#ARGB_8888} with the alpha channel ignored) * channel Bitmap that represents the gainmap data itself. * * @hide * When rendering to an {@link android.content.pm.ActivityInfo#COLOR_MODE_HDR} activity, the * hardware accelerated {@link Canvas} will automatically apply the gainmap when sufficient * HDR headroom is available. * * <h3>Gainmap Structure</h3> * * The logical whole of a gainmap'd image consists of a base Bitmap that represents the original * image as would be displayed without gainmap support in addition to a gainmap with a second * enhancement image. In the case of a JPEG, the base image would be the typical 8-bit SDR image * that the format is commonly associated with. The gainmap image is embedded alongside the base * image, often at a lower resolution (such as 1/4th), along with some metadata to describe * how to apply the gainmap. The gainmap image itself is then a greyscale image representing * the transformation to apply onto the base image to reconstruct an HDR rendition of it. * * As such these "gainmap images" consist of 3 parts - a base {@link Bitmap} with a * {@link Bitmap#getGainmap()} that returns an instance of this class which in turn contains * the enhancement layer represented as another Bitmap, accessible via {@link #getGainmapContents()} * * <h3>Applying a gainmap manually</h3> * * When doing custom rendering such as to an OpenGL ES or Vulkan context, the gainmap is not * automatically applied. In such situations, the following steps are appropriate to render the * gainmap in combination with the base image. * * Suppose our display has HDR to SDR ratio of H, and we wish to display an image with gainmap on * this display. Let B be the pixel value from the base image in a color space that has the * primaries of the base image and a linear transfer function. Let G be the pixel value from the * gainmap. Let D be the output pixel in the same color space as B. The value of D is computed * as follows: * * First, let W be a weight parameter determining how much the gainmap will be applied. * W = clamp((log(H) - log(displayRatioHdr)) / * (log(displayRatioHdr) - log(displayRatioSdr), 0, 1) * * Next, let L be the gainmap value in log space. We compute this from the value G that was * sampled from the texture as follows: * L = mix(log(gainmapRatioMin), log(gainmapRatioMax), pow(G, gainmapGamma)) * * Finally, apply the gainmap to compute D, the displayed pixel. If the base image is SDR then * compute: * D = (B + epsilonSdr) * exp(L * W) - epsilonHdr * If the base image is HDR then compute: * D = (B + epsilonHdr) * exp(L * (W - 1)) - epsilonSdr * * In the above math, log() is a natural logarithm and exp() is natural exponentiation. */ public class Gainmap { private final long mNativePtr; private final Bitmap mGainmapImage; public final class Gainmap { // called from JNI and Bitmap_Delegate. private Gainmap(Bitmap gainmapImage, long nativeGainmap, int allocationByteCount, boolean fromMalloc) { // Use a Holder to allow static initialization of Gainmap in the boot image. private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( Gainmap.class.getClassLoader(), nGetFinalizer()); } final long mNativePtr; private Bitmap mGainmapContents; // called from JNI private Gainmap(Bitmap gainmapContents, long nativeGainmap) { if (nativeGainmap == 0) { throw new RuntimeException("internal error: native gainmap is 0"); } mGainmapImage = gainmapImage; mGainmapContents = gainmapContents; mNativePtr = nativeGainmap; final NativeAllocationRegistry registry; if (fromMalloc) { registry = NativeAllocationRegistry.createMalloced( Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); } else { registry = NativeAllocationRegistry.createNonmalloced( Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, nativeGainmap); } /** * Creates a gainmap from a given Bitmap. The caller is responsible for setting the various * fields to the desired values. The defaults are as follows: * <ul> * <li>Ratio min is 1f, 1f, 1f</li> * <li>Ratio max is 2f, 2f, 2f</li> * <li>Gamma is 1f, 1f, 1f</li> * <li>Epsilon SDR is 0f, 0f, 0f</li> * <li>Epsilon HDR is 0f, 0f, 0f</li> * <li>Display ratio SDR is 1f</li> * <li>Display ratio HDR is 2f</li> * </ul> * It is strongly recommended that at least the ratio max and display ratio HDR are adjusted * to better suit the given gainmap contents. */ public Gainmap(@NonNull Bitmap gainmapContents) { this(gainmapContents, nCreateEmpty()); } /** * @return Returns the image data of the gainmap represented as a Bitmap. This is represented * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not * relevant to the gainmap's enhancement layer. */ @NonNull public Bitmap getGainmapContents() { return mGainmapContents; } /** * Sets the image data of the gainmap. This is the 1 or 3 channel enhancement layer to apply * to the base image. This is represented as a Bitmap for broad API compatibility, however * certain aspects of the Bitmap are ignored such as {@link Bitmap#getColorSpace()} or * {@link Bitmap#getGainmap()} as they are not relevant to the gainmap's enhancement layer. * * @param bitmap The non-null bitmap to set as the gainmap's contents */ public void setGainmapContents(@NonNull Bitmap bitmap) { // TODO: Validate here or leave native-side? if (bitmap.isRecycled()) throw new IllegalArgumentException("Bitmap is recycled"); nSetBitmap(mNativePtr, bitmap); mGainmapContents = bitmap; } /** * Sets the gainmap ratio min. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setRatioMin(float r, float g, float b) { nSetRatioMin(mNativePtr, r, g, b); } /** * Gets the gainmap ratio max. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getRatioMin() { float[] ret = new float[3]; nGetRatioMin(mNativePtr, ret); return ret; } /** * Sets the gainmap ratio max. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setRatioMax(float r, float g, float b) { nSetRatioMax(mNativePtr, r, g, b); } /** * Gets the gainmap ratio max. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getRatioMax() { float[] ret = new float[3]; nGetRatioMax(mNativePtr, ret); return ret; } /** * Sets the gainmap gamma. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setGamma(float r, float g, float b) { nSetGamma(mNativePtr, r, g, b); } /** * Gets the gainmap gamma. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getGamma() { float[] ret = new float[3]; nGetGamma(mNativePtr, ret); return ret; } registry.registerNativeAllocation(this, nativeGainmap); /** * Sets the sdr epsilon which is used to avoid numerical instability. * For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setEpsilonSdr(float r, float g, float b) { nSetEpsilonSdr(mNativePtr, r, g, b); } /** * Returns the image data of the gainmap represented as a Bitmap * @return * Gets the sdr epsilon. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public Bitmap getGainmapImage() { return mGainmapImage; public float[] getEpsilonSdr() { float[] ret = new float[3]; nGetEpsilonSdr(mNativePtr, ret); return ret; } /** * Sets the gainmap max metadata. For single-plane gainmaps, r, g, and b should be the same. * Sets the hdr epsilon which is used to avoid numerical instability. * 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); public void setEpsilonHdr(float r, float g, float b) { nSetEpsilonHdr(mNativePtr, r, g, b); } /** * Gets the gainmap max metadata. For single-plane gainmaps, all 3 components should be the * Gets the hdr epsilon. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getGainmapMax() { public float[] getEpsilonHdr() { float[] ret = new float[3]; nGetGainmapMax(mNativePtr, ret); nGetEpsilonHdr(mNativePtr, ret); return ret; } /** * Sets the maximum HDR ratio for the gainmap * Sets the hdr/sdr ratio at which point the gainmap is fully applied. * @param max The hdr/sdr ratio at which the gainmap is fully applied. Must be >= 1.0f */ @NonNull public void setHdrRatioMax(float max) { nSetHdrRatioMax(mNativePtr, max); public void setDisplayRatioForFullHdr(float max) { if (!Float.isFinite(max) || max < 1f) { throw new IllegalArgumentException( "setDisplayRatioForFullHdr must be >= 1.0f, got = " + max); } nSetDisplayRatioHdr(mNativePtr, max); } /** * Gets the maximum HDR ratio for the gainmap * Gets the hdr/sdr ratio at which point the gainmap is fully applied. */ @NonNull public float getHdrRatioMax() { return nGetHdrRatioMax(mNativePtr); public float getDisplayRatioForFullHdr() { return nGetDisplayRatioHdr(mNativePtr); } /** * Sets the maximum HDR ratio for the gainmap * Sets the hdr/sdr ratio below which only the SDR image is displayed. * @param min The minimum hdr/sdr ratio at which to begin applying the gainmap. Must be >= 1.0f */ @NonNull public void setHdrRatioMin(float min) { nSetHdrRatioMin(mNativePtr, min); public void setMinDisplayRatioForHdrTransition(@FloatRange(from = 1.0f) float min) { if (!Float.isFinite(min) || min < 1f) { throw new IllegalArgumentException( "setMinDisplayRatioForHdrTransition must be >= 1.0f, got = " + min); } nSetDisplayRatioSdr(mNativePtr, min); } /** * Gets the maximum HDR ratio for the gainmap * Gets the hdr/sdr ratio below which only the SDR image is displayed. */ @NonNull public float getHdrRatioMin() { return nGetHdrRatioMin(mNativePtr); public float getMinDisplayRatioForHdrTransition() { return nGetDisplayRatioSdr(mNativePtr); } private static native long nGetFinalizer(); private static native long nCreateEmpty(); private static native void nSetBitmap(long ptr, Bitmap bitmap); private static native void nSetRatioMin(long ptr, float r, float g, float b); private static native void nGetRatioMin(long ptr, float[] components); private static native void nSetRatioMax(long ptr, float r, float g, float b); private static native void nGetRatioMax(long ptr, float[] components); private static native void nSetGamma(long ptr, float r, float g, float b); private static native void nGetGamma(long ptr, float[] components); private static native void nSetEpsilonSdr(long ptr, float r, float g, float b); private static native void nGetEpsilonSdr(long ptr, float[] components); 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 nSetEpsilonHdr(long ptr, float r, float g, float b); private static native void nGetEpsilonHdr(long ptr, float[] components); private static native void nSetHdrRatioMax(long ptr, float max); private static native float nGetHdrRatioMax(long ptr); private static native void nSetDisplayRatioHdr(long ptr, float max); private static native float nGetDisplayRatioHdr(long ptr); private static native void nSetHdrRatioMin(long ptr, float min); private static native float nGetHdrRatioMin(long ptr); private static native void nSetDisplayRatioSdr(long ptr, float min); private static native float nGetDisplayRatioSdr(long ptr); } libs/hwui/hwui/ImageDecoder.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -506,6 +506,9 @@ SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) { decoder.mOverrideOrigin.emplace(getOrigin()); // Update mDecodeSize / mTargetSize for the overridden origin decoder.setTargetSize(decoder.width(), decoder.height()); if (decoder.gray()) { decoder.setOutColorType(kGray_8_SkColorType); } const bool isScaled = width() != mTargetSize.width() || height() != mTargetSize.height(); Loading @@ -528,6 +531,9 @@ SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) { } SkImageInfo bitmapInfo = decoder.getOutputInfo(); if (bitmapInfo.colorType() == kGray_8_SkColorType) { bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } SkBitmap bm; if (!bm.setInfo(bitmapInfo)) { Loading libs/hwui/jni/Bitmap.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -1300,6 +1300,13 @@ static jobject Bitmap_extractGainmap(JNIEnv* env, jobject, jlong bitmapHandle) { return Gainmap_extractFromBitmap(env, bitmapHolder->bitmap()); } static void Bitmap_setGainmap(JNIEnv*, jobject, jlong bitmapHandle, jlong gainmapPtr) { LocalScopedBitmap bitmapHolder(bitmapHandle); if (!bitmapHolder.valid()) return; uirenderer::Gainmap* gainmap = reinterpret_cast<uirenderer::Gainmap*>(gainmapPtr); bitmapHolder->bitmap().setGainmap(sp<uirenderer::Gainmap>::fromExisting(gainmap)); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { Loading Loading @@ -1351,6 +1358,7 @@ static const JNINativeMethod gBitmapMethods[] = { {"nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, {"nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, {"nativeExtractGainmap", "(J)Landroid/graphics/Gainmap;", (void*)Bitmap_extractGainmap}, {"nativeSetGainmap", "(JJ)V", (void*)Bitmap_setGainmap}, // ------------ @CriticalNative ---------------- {"nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable}, Loading Loading
core/api/current.txt +23 −0 Original line number Diff line number Diff line Loading @@ -14933,6 +14933,7 @@ package android.graphics { method @Nullable public android.graphics.ColorSpace getColorSpace(); method @NonNull public android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method @Nullable public android.graphics.Gainmap getGainmap(); method public int getGenerationId(); method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer(); method public int getHeight(); Loading @@ -14948,6 +14949,7 @@ package android.graphics { method public int getScaledWidth(int); method public int getWidth(); method public boolean hasAlpha(); method public boolean hasGainmap(); method public boolean hasMipMap(); method public boolean isMutable(); method public boolean isPremultiplied(); Loading @@ -14959,6 +14961,7 @@ package android.graphics { method public void setColorSpace(@NonNull android.graphics.ColorSpace); method public void setConfig(@NonNull android.graphics.Bitmap.Config); method public void setDensity(int); method public void setGainmap(@Nullable android.graphics.Gainmap); method public void setHasAlpha(boolean); method public void setHasMipMap(boolean); method public void setHeight(int); Loading Loading @@ -15494,6 +15497,26 @@ package android.graphics { ctor @Deprecated public EmbossMaskFilter(float[], float, float, float); } public final class Gainmap { ctor public Gainmap(@NonNull android.graphics.Bitmap); method @NonNull public float getDisplayRatioForFullHdr(); method @NonNull public float[] getEpsilonHdr(); method @NonNull public float[] getEpsilonSdr(); method @NonNull public android.graphics.Bitmap getGainmapContents(); method @NonNull public float[] getGamma(); method @NonNull public float getMinDisplayRatioForHdrTransition(); method @NonNull public float[] getRatioMax(); method @NonNull public float[] getRatioMin(); method @NonNull public void setDisplayRatioForFullHdr(float); method @NonNull public void setEpsilonHdr(float, float, float); method @NonNull public void setEpsilonSdr(float, float, float); method public void setGainmapContents(@NonNull android.graphics.Bitmap); method @NonNull public void setGamma(float, float, float); method @NonNull public void setMinDisplayRatioForHdrTransition(@FloatRange(from=1.0f) float); method @NonNull public void setRatioMax(float, float, float); method @NonNull public void setRatioMin(float, float, float); } public class HardwareBufferRenderer implements java.lang.AutoCloseable { ctor public HardwareBufferRenderer(@NonNull android.hardware.HardwareBuffer); method public void close();
graphics/java/android/graphics/Bitmap.java +9 −2 Original line number Diff line number Diff line Loading @@ -1899,7 +1899,6 @@ public final class Bitmap implements Parcelable { /** * Returns whether or not this Bitmap contains a Gainmap. * @hide */ public boolean hasGainmap() { checkRecycled("Bitmap is recycled"); Loading @@ -1908,7 +1907,6 @@ public final class Bitmap implements Parcelable { /** * Returns the gainmap or null if the bitmap doesn't contain a gainmap * @hide */ public @Nullable Gainmap getGainmap() { checkRecycled("Bitmap is recycled"); Loading @@ -1918,6 +1916,14 @@ public final class Bitmap implements Parcelable { return mGainmap; } /** * Sets a gainmap on this bitmap, or removes the gainmap if null */ public void setGainmap(@Nullable Gainmap gainmap) { checkRecycled("Bitmap is recycled"); nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr); } /** * Fills the bitmap's pixels with the specified {@link Color}. * Loading Loading @@ -2403,6 +2409,7 @@ public final class Bitmap implements Parcelable { private static native void nativeSetImmutable(long nativePtr); private static native Gainmap nativeExtractGainmap(long nativePtr); private static native void nativeSetGainmap(long bitmapPtr, long gainmapPtr); // ---------------- @CriticalNative ------------------- Loading
graphics/java/android/graphics/Gainmap.java +231 −46 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.graphics; import android.annotation.FloatRange; import android.annotation.NonNull; import libcore.util.NativeAllocationRegistry; Loading @@ -24,104 +25,288 @@ 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 * It is a combination of a set of metadata describing how to apply the gainmap, as well as either * a 1 (such as {@link android.graphics.Bitmap.Config#ALPHA_8} or 3 * (such as {@link android.graphics.Bitmap.Config#ARGB_8888} with the alpha channel ignored) * channel Bitmap that represents the gainmap data itself. * * @hide * When rendering to an {@link android.content.pm.ActivityInfo#COLOR_MODE_HDR} activity, the * hardware accelerated {@link Canvas} will automatically apply the gainmap when sufficient * HDR headroom is available. * * <h3>Gainmap Structure</h3> * * The logical whole of a gainmap'd image consists of a base Bitmap that represents the original * image as would be displayed without gainmap support in addition to a gainmap with a second * enhancement image. In the case of a JPEG, the base image would be the typical 8-bit SDR image * that the format is commonly associated with. The gainmap image is embedded alongside the base * image, often at a lower resolution (such as 1/4th), along with some metadata to describe * how to apply the gainmap. The gainmap image itself is then a greyscale image representing * the transformation to apply onto the base image to reconstruct an HDR rendition of it. * * As such these "gainmap images" consist of 3 parts - a base {@link Bitmap} with a * {@link Bitmap#getGainmap()} that returns an instance of this class which in turn contains * the enhancement layer represented as another Bitmap, accessible via {@link #getGainmapContents()} * * <h3>Applying a gainmap manually</h3> * * When doing custom rendering such as to an OpenGL ES or Vulkan context, the gainmap is not * automatically applied. In such situations, the following steps are appropriate to render the * gainmap in combination with the base image. * * Suppose our display has HDR to SDR ratio of H, and we wish to display an image with gainmap on * this display. Let B be the pixel value from the base image in a color space that has the * primaries of the base image and a linear transfer function. Let G be the pixel value from the * gainmap. Let D be the output pixel in the same color space as B. The value of D is computed * as follows: * * First, let W be a weight parameter determining how much the gainmap will be applied. * W = clamp((log(H) - log(displayRatioHdr)) / * (log(displayRatioHdr) - log(displayRatioSdr), 0, 1) * * Next, let L be the gainmap value in log space. We compute this from the value G that was * sampled from the texture as follows: * L = mix(log(gainmapRatioMin), log(gainmapRatioMax), pow(G, gainmapGamma)) * * Finally, apply the gainmap to compute D, the displayed pixel. If the base image is SDR then * compute: * D = (B + epsilonSdr) * exp(L * W) - epsilonHdr * If the base image is HDR then compute: * D = (B + epsilonHdr) * exp(L * (W - 1)) - epsilonSdr * * In the above math, log() is a natural logarithm and exp() is natural exponentiation. */ public class Gainmap { private final long mNativePtr; private final Bitmap mGainmapImage; public final class Gainmap { // called from JNI and Bitmap_Delegate. private Gainmap(Bitmap gainmapImage, long nativeGainmap, int allocationByteCount, boolean fromMalloc) { // Use a Holder to allow static initialization of Gainmap in the boot image. private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( Gainmap.class.getClassLoader(), nGetFinalizer()); } final long mNativePtr; private Bitmap mGainmapContents; // called from JNI private Gainmap(Bitmap gainmapContents, long nativeGainmap) { if (nativeGainmap == 0) { throw new RuntimeException("internal error: native gainmap is 0"); } mGainmapImage = gainmapImage; mGainmapContents = gainmapContents; mNativePtr = nativeGainmap; final NativeAllocationRegistry registry; if (fromMalloc) { registry = NativeAllocationRegistry.createMalloced( Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); } else { registry = NativeAllocationRegistry.createNonmalloced( Bitmap.class.getClassLoader(), nGetFinalizer(), allocationByteCount); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, nativeGainmap); } /** * Creates a gainmap from a given Bitmap. The caller is responsible for setting the various * fields to the desired values. The defaults are as follows: * <ul> * <li>Ratio min is 1f, 1f, 1f</li> * <li>Ratio max is 2f, 2f, 2f</li> * <li>Gamma is 1f, 1f, 1f</li> * <li>Epsilon SDR is 0f, 0f, 0f</li> * <li>Epsilon HDR is 0f, 0f, 0f</li> * <li>Display ratio SDR is 1f</li> * <li>Display ratio HDR is 2f</li> * </ul> * It is strongly recommended that at least the ratio max and display ratio HDR are adjusted * to better suit the given gainmap contents. */ public Gainmap(@NonNull Bitmap gainmapContents) { this(gainmapContents, nCreateEmpty()); } /** * @return Returns the image data of the gainmap represented as a Bitmap. This is represented * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not * relevant to the gainmap's enhancement layer. */ @NonNull public Bitmap getGainmapContents() { return mGainmapContents; } /** * Sets the image data of the gainmap. This is the 1 or 3 channel enhancement layer to apply * to the base image. This is represented as a Bitmap for broad API compatibility, however * certain aspects of the Bitmap are ignored such as {@link Bitmap#getColorSpace()} or * {@link Bitmap#getGainmap()} as they are not relevant to the gainmap's enhancement layer. * * @param bitmap The non-null bitmap to set as the gainmap's contents */ public void setGainmapContents(@NonNull Bitmap bitmap) { // TODO: Validate here or leave native-side? if (bitmap.isRecycled()) throw new IllegalArgumentException("Bitmap is recycled"); nSetBitmap(mNativePtr, bitmap); mGainmapContents = bitmap; } /** * Sets the gainmap ratio min. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setRatioMin(float r, float g, float b) { nSetRatioMin(mNativePtr, r, g, b); } /** * Gets the gainmap ratio max. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getRatioMin() { float[] ret = new float[3]; nGetRatioMin(mNativePtr, ret); return ret; } /** * Sets the gainmap ratio max. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setRatioMax(float r, float g, float b) { nSetRatioMax(mNativePtr, r, g, b); } /** * Gets the gainmap ratio max. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getRatioMax() { float[] ret = new float[3]; nGetRatioMax(mNativePtr, ret); return ret; } /** * Sets the gainmap gamma. For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setGamma(float r, float g, float b) { nSetGamma(mNativePtr, r, g, b); } /** * Gets the gainmap gamma. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getGamma() { float[] ret = new float[3]; nGetGamma(mNativePtr, ret); return ret; } registry.registerNativeAllocation(this, nativeGainmap); /** * Sets the sdr epsilon which is used to avoid numerical instability. * For single-plane gainmaps, r, g, and b should be the same. */ @NonNull public void setEpsilonSdr(float r, float g, float b) { nSetEpsilonSdr(mNativePtr, r, g, b); } /** * Returns the image data of the gainmap represented as a Bitmap * @return * Gets the sdr epsilon. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public Bitmap getGainmapImage() { return mGainmapImage; public float[] getEpsilonSdr() { float[] ret = new float[3]; nGetEpsilonSdr(mNativePtr, ret); return ret; } /** * Sets the gainmap max metadata. For single-plane gainmaps, r, g, and b should be the same. * Sets the hdr epsilon which is used to avoid numerical instability. * 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); public void setEpsilonHdr(float r, float g, float b) { nSetEpsilonHdr(mNativePtr, r, g, b); } /** * Gets the gainmap max metadata. For single-plane gainmaps, all 3 components should be the * Gets the hdr epsilon. For single-plane gainmaps, all 3 components should be the * same. The components are in r, g, b order. */ @NonNull public float[] getGainmapMax() { public float[] getEpsilonHdr() { float[] ret = new float[3]; nGetGainmapMax(mNativePtr, ret); nGetEpsilonHdr(mNativePtr, ret); return ret; } /** * Sets the maximum HDR ratio for the gainmap * Sets the hdr/sdr ratio at which point the gainmap is fully applied. * @param max The hdr/sdr ratio at which the gainmap is fully applied. Must be >= 1.0f */ @NonNull public void setHdrRatioMax(float max) { nSetHdrRatioMax(mNativePtr, max); public void setDisplayRatioForFullHdr(float max) { if (!Float.isFinite(max) || max < 1f) { throw new IllegalArgumentException( "setDisplayRatioForFullHdr must be >= 1.0f, got = " + max); } nSetDisplayRatioHdr(mNativePtr, max); } /** * Gets the maximum HDR ratio for the gainmap * Gets the hdr/sdr ratio at which point the gainmap is fully applied. */ @NonNull public float getHdrRatioMax() { return nGetHdrRatioMax(mNativePtr); public float getDisplayRatioForFullHdr() { return nGetDisplayRatioHdr(mNativePtr); } /** * Sets the maximum HDR ratio for the gainmap * Sets the hdr/sdr ratio below which only the SDR image is displayed. * @param min The minimum hdr/sdr ratio at which to begin applying the gainmap. Must be >= 1.0f */ @NonNull public void setHdrRatioMin(float min) { nSetHdrRatioMin(mNativePtr, min); public void setMinDisplayRatioForHdrTransition(@FloatRange(from = 1.0f) float min) { if (!Float.isFinite(min) || min < 1f) { throw new IllegalArgumentException( "setMinDisplayRatioForHdrTransition must be >= 1.0f, got = " + min); } nSetDisplayRatioSdr(mNativePtr, min); } /** * Gets the maximum HDR ratio for the gainmap * Gets the hdr/sdr ratio below which only the SDR image is displayed. */ @NonNull public float getHdrRatioMin() { return nGetHdrRatioMin(mNativePtr); public float getMinDisplayRatioForHdrTransition() { return nGetDisplayRatioSdr(mNativePtr); } private static native long nGetFinalizer(); private static native long nCreateEmpty(); private static native void nSetBitmap(long ptr, Bitmap bitmap); private static native void nSetRatioMin(long ptr, float r, float g, float b); private static native void nGetRatioMin(long ptr, float[] components); private static native void nSetRatioMax(long ptr, float r, float g, float b); private static native void nGetRatioMax(long ptr, float[] components); private static native void nSetGamma(long ptr, float r, float g, float b); private static native void nGetGamma(long ptr, float[] components); private static native void nSetEpsilonSdr(long ptr, float r, float g, float b); private static native void nGetEpsilonSdr(long ptr, float[] components); 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 nSetEpsilonHdr(long ptr, float r, float g, float b); private static native void nGetEpsilonHdr(long ptr, float[] components); private static native void nSetHdrRatioMax(long ptr, float max); private static native float nGetHdrRatioMax(long ptr); private static native void nSetDisplayRatioHdr(long ptr, float max); private static native float nGetDisplayRatioHdr(long ptr); private static native void nSetHdrRatioMin(long ptr, float min); private static native float nGetHdrRatioMin(long ptr); private static native void nSetDisplayRatioSdr(long ptr, float min); private static native float nGetDisplayRatioSdr(long ptr); }
libs/hwui/hwui/ImageDecoder.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -506,6 +506,9 @@ SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) { decoder.mOverrideOrigin.emplace(getOrigin()); // Update mDecodeSize / mTargetSize for the overridden origin decoder.setTargetSize(decoder.width(), decoder.height()); if (decoder.gray()) { decoder.setOutColorType(kGray_8_SkColorType); } const bool isScaled = width() != mTargetSize.width() || height() != mTargetSize.height(); Loading @@ -528,6 +531,9 @@ SkCodec::Result ImageDecoder::extractGainmap(Bitmap* destination) { } SkImageInfo bitmapInfo = decoder.getOutputInfo(); if (bitmapInfo.colorType() == kGray_8_SkColorType) { bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } SkBitmap bm; if (!bm.setInfo(bitmapInfo)) { Loading
libs/hwui/jni/Bitmap.cpp +8 −0 Original line number Diff line number Diff line Loading @@ -1300,6 +1300,13 @@ static jobject Bitmap_extractGainmap(JNIEnv* env, jobject, jlong bitmapHandle) { return Gainmap_extractFromBitmap(env, bitmapHolder->bitmap()); } static void Bitmap_setGainmap(JNIEnv*, jobject, jlong bitmapHandle, jlong gainmapPtr) { LocalScopedBitmap bitmapHolder(bitmapHandle); if (!bitmapHolder.valid()) return; uirenderer::Gainmap* gainmap = reinterpret_cast<uirenderer::Gainmap*>(gainmapPtr); bitmapHolder->bitmap().setGainmap(sp<uirenderer::Gainmap>::fromExisting(gainmap)); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { Loading Loading @@ -1351,6 +1358,7 @@ static const JNINativeMethod gBitmapMethods[] = { {"nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, {"nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, {"nativeExtractGainmap", "(J)Landroid/graphics/Gainmap;", (void*)Bitmap_extractGainmap}, {"nativeSetGainmap", "(JJ)V", (void*)Bitmap_setGainmap}, // ------------ @CriticalNative ---------------- {"nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable}, Loading