Merge "Add Gainmap bitmap & imagedecoder"

This commit is contained in:
John Reck
2023-01-25 18:05:08 +00:00
committed by Android (Google) Code Review
12 changed files with 528 additions and 78 deletions

View File

@@ -93,6 +93,7 @@ public final class Bitmap implements Parcelable {
private boolean mRecycled;
private ColorSpace mColorSpace;
private Gainmap mGainmap;
/*package*/ int mDensity = getDefaultDensity();
@@ -1896,6 +1897,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}.
*
@@ -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);
}

View File

@@ -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);
}

View File

@@ -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",

32
libs/hwui/Gainmap.h Normal file
View File

@@ -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

View File

@@ -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),

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

128
libs/hwui/jni/Bitmap.cpp Executable file → Normal file
View File

@@ -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},
{"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},
};

125
libs/hwui/jni/Gainmap.cpp Normal file
View File

@@ -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

View File

@@ -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);
}