diff options
author | 2016-12-14 09:43:50 -0800 | |
---|---|---|
committer | 2016-12-16 09:15:07 -0800 | |
commit | 9505a6552764461c22ce48f1ac13d025d23e1579 (patch) | |
tree | 8cd7b889674ada52f773a26fc6c5acaf20cb3649 | |
parent | de315b99dcaf7202898d6334570753ffee43c1f1 (diff) |
Add new RGBA_F16 bitmap config
This configuration uses 64 bits per pixel. Heach component is stored as a
half precision float value (16 bits). Half floats can be decoded/encoded
using android.util.Half.
RGBA_F16 bitmaps are used to decode wide-gamut images stored in 16 bit
formats (PNG 16 bit for instance). aapt is currently not aware of PNG
16 bits so such files must be placed in raw/ resource directories.
This first pass provides only partial drawing support with hardware
acceleration. RGBA_F16 bitmaps are stored in linear space and need
to be encoded to gamma space with the appropriate OETF to be rendered
properly on Android's current surfaces. They are however suitable for
linear blending. Full rendering support will be provided in a future
CL (BitmapShaders might be a bit tricky to handle properly during
shader generation).
Bug: 32984164
Test: bit CtsGraphicsTestCases:android.graphics.cts.BitmapRGBAF16Test
Change-Id: I328e6b567441a1b9d152a3e7be944a2cf63193bd
-rw-r--r-- | api/current.txt | 36 | ||||
-rw-r--r-- | api/system-current.txt | 36 | ||||
-rw-r--r-- | api/test-current.txt | 36 | ||||
-rwxr-xr-x | core/jni/android/graphics/Bitmap.cpp | 77 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 7 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 19 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 2 | ||||
-rw-r--r-- | core/jni/android_view_GraphicBuffer.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_view_Surface.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_view_SurfaceControl.cpp | 10 | ||||
-rw-r--r-- | core/jni/android_view_TextureView.cpp | 8 | ||||
-rw-r--r-- | docs/html/reference/images/graphics/colorspace_ucs.png | bin | 0 -> 291374 bytes | |||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 43 | ||||
-rw-r--r-- | graphics/java/android/graphics/ColorSpace.java | 214 | ||||
-rw-r--r-- | graphics/java/android/graphics/PixelFormat.java | 32 | ||||
-rw-r--r-- | libs/hwui/OpenGLReadback.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/Texture.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/hwui/Bitmap.cpp | 5 |
18 files changed, 412 insertions, 134 deletions
diff --git a/api/current.txt b/api/current.txt index cec2b9fc2457..89573ace6dac 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11769,6 +11769,8 @@ package android.graphics { method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean); method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config); + method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean); + method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean); method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config); @@ -11833,6 +11835,7 @@ package android.graphics { enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444; enum_constant public static final android.graphics.Bitmap.Config ARGB_8888; enum_constant public static final android.graphics.Bitmap.Config HARDWARE; + enum_constant public static final android.graphics.Bitmap.Config RGBA_F16; enum_constant public static final android.graphics.Bitmap.Config RGB_565; } @@ -12192,6 +12195,7 @@ package android.graphics { method public android.graphics.Bitmap render(); method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean); method public android.graphics.ColorSpace.Renderer size(int); + method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean); } public static class ColorSpace.Rgb extends android.graphics.ColorSpace { @@ -12727,7 +12731,9 @@ package android.graphics { field public static final deprecated int RGBA_4444 = 7; // 0x7 field public static final deprecated int RGBA_5551 = 6; // 0x6 field public static final int RGBA_8888 = 1; // 0x1 + field public static final int RGBA_F16 = 22; // 0x16 field public static final int RGBX_8888 = 2; // 0x2 + field public static final int RGBX_F16 = 23; // 0x17 field public static final deprecated int RGB_332 = 11; // 0xb field public static final int RGB_565 = 4; // 0x4 field public static final int RGB_888 = 3; // 0x3 @@ -61421,31 +61427,31 @@ package java.util.concurrent { ctor public CopyOnWriteArrayList(); ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>); ctor public CopyOnWriteArrayList(E[]); - method public synchronized boolean add(E); - method public synchronized void add(int, E); - method public synchronized boolean addAll(java.util.Collection<? extends E>); - method public synchronized boolean addAll(int, java.util.Collection<? extends E>); - method public synchronized int addAllAbsent(java.util.Collection<? extends E>); - method public synchronized boolean addIfAbsent(E); - method public synchronized void clear(); + method public boolean add(E); + method public void add(int, E); + method public boolean addAll(java.util.Collection<? extends E>); + method public boolean addAll(int, java.util.Collection<? extends E>); + method public int addAllAbsent(java.util.Collection<? extends E>); + method public boolean addIfAbsent(E); + method public void clear(); method public java.lang.Object clone(); method public boolean contains(java.lang.Object); method public boolean containsAll(java.util.Collection<?>); method public void forEach(java.util.function.Consumer<? super E>); method public E get(int); - method public int indexOf(E, int); method public int indexOf(java.lang.Object); + method public int indexOf(E, int); method public boolean isEmpty(); method public java.util.Iterator<E> iterator(); - method public int lastIndexOf(E, int); method public int lastIndexOf(java.lang.Object); - method public java.util.ListIterator<E> listIterator(int); + method public int lastIndexOf(E, int); method public java.util.ListIterator<E> listIterator(); - method public synchronized E remove(int); - method public synchronized boolean remove(java.lang.Object); - method public synchronized boolean removeAll(java.util.Collection<?>); - method public synchronized boolean retainAll(java.util.Collection<?>); - method public synchronized E set(int, E); + method public java.util.ListIterator<E> listIterator(int); + method public E remove(int); + method public boolean remove(java.lang.Object); + method public boolean removeAll(java.util.Collection<?>); + method public boolean retainAll(java.util.Collection<?>); + method public E set(int, E); method public int size(); method public java.util.List<E> subList(int, int); method public java.lang.Object[] toArray(); diff --git a/api/system-current.txt b/api/system-current.txt index b0236ea15fc2..7d8da95f0b37 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12262,6 +12262,8 @@ package android.graphics { method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean); method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config); + method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean); + method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean); method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config); @@ -12326,6 +12328,7 @@ package android.graphics { enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444; enum_constant public static final android.graphics.Bitmap.Config ARGB_8888; enum_constant public static final android.graphics.Bitmap.Config HARDWARE; + enum_constant public static final android.graphics.Bitmap.Config RGBA_F16; enum_constant public static final android.graphics.Bitmap.Config RGB_565; } @@ -12685,6 +12688,7 @@ package android.graphics { method public android.graphics.Bitmap render(); method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean); method public android.graphics.ColorSpace.Renderer size(int); + method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean); } public static class ColorSpace.Rgb extends android.graphics.ColorSpace { @@ -13220,7 +13224,9 @@ package android.graphics { field public static final deprecated int RGBA_4444 = 7; // 0x7 field public static final deprecated int RGBA_5551 = 6; // 0x6 field public static final int RGBA_8888 = 1; // 0x1 + field public static final int RGBA_F16 = 22; // 0x16 field public static final int RGBX_8888 = 2; // 0x2 + field public static final int RGBX_F16 = 23; // 0x17 field public static final deprecated int RGB_332 = 11; // 0xb field public static final int RGB_565 = 4; // 0x4 field public static final int RGB_888 = 3; // 0x3 @@ -64923,31 +64929,31 @@ package java.util.concurrent { ctor public CopyOnWriteArrayList(); ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>); ctor public CopyOnWriteArrayList(E[]); - method public synchronized boolean add(E); - method public synchronized void add(int, E); - method public synchronized boolean addAll(java.util.Collection<? extends E>); - method public synchronized boolean addAll(int, java.util.Collection<? extends E>); - method public synchronized int addAllAbsent(java.util.Collection<? extends E>); - method public synchronized boolean addIfAbsent(E); - method public synchronized void clear(); + method public boolean add(E); + method public void add(int, E); + method public boolean addAll(java.util.Collection<? extends E>); + method public boolean addAll(int, java.util.Collection<? extends E>); + method public int addAllAbsent(java.util.Collection<? extends E>); + method public boolean addIfAbsent(E); + method public void clear(); method public java.lang.Object clone(); method public boolean contains(java.lang.Object); method public boolean containsAll(java.util.Collection<?>); method public void forEach(java.util.function.Consumer<? super E>); method public E get(int); - method public int indexOf(E, int); method public int indexOf(java.lang.Object); + method public int indexOf(E, int); method public boolean isEmpty(); method public java.util.Iterator<E> iterator(); - method public int lastIndexOf(E, int); method public int lastIndexOf(java.lang.Object); - method public java.util.ListIterator<E> listIterator(int); + method public int lastIndexOf(E, int); method public java.util.ListIterator<E> listIterator(); - method public synchronized E remove(int); - method public synchronized boolean remove(java.lang.Object); - method public synchronized boolean removeAll(java.util.Collection<?>); - method public synchronized boolean retainAll(java.util.Collection<?>); - method public synchronized E set(int, E); + method public java.util.ListIterator<E> listIterator(int); + method public E remove(int); + method public boolean remove(java.lang.Object); + method public boolean removeAll(java.util.Collection<?>); + method public boolean retainAll(java.util.Collection<?>); + method public E set(int, E); method public int size(); method public java.util.List<E> subList(int, int); method public java.lang.Object[] toArray(); diff --git a/api/test-current.txt b/api/test-current.txt index c798b5e165cd..251a353d2832 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -11800,6 +11800,8 @@ package android.graphics { method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean); method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config); + method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean); + method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean); method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config); @@ -11864,6 +11866,7 @@ package android.graphics { enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444; enum_constant public static final android.graphics.Bitmap.Config ARGB_8888; enum_constant public static final android.graphics.Bitmap.Config HARDWARE; + enum_constant public static final android.graphics.Bitmap.Config RGBA_F16; enum_constant public static final android.graphics.Bitmap.Config RGB_565; } @@ -12223,6 +12226,7 @@ package android.graphics { method public android.graphics.Bitmap render(); method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean); method public android.graphics.ColorSpace.Renderer size(int); + method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean); } public static class ColorSpace.Rgb extends android.graphics.ColorSpace { @@ -12758,7 +12762,9 @@ package android.graphics { field public static final deprecated int RGBA_4444 = 7; // 0x7 field public static final deprecated int RGBA_5551 = 6; // 0x6 field public static final int RGBA_8888 = 1; // 0x1 + field public static final int RGBA_F16 = 22; // 0x16 field public static final int RGBX_8888 = 2; // 0x2 + field public static final int RGBX_F16 = 23; // 0x17 field public static final deprecated int RGB_332 = 11; // 0xb field public static final int RGB_565 = 4; // 0x4 field public static final int RGB_888 = 3; // 0x3 @@ -61710,31 +61716,31 @@ package java.util.concurrent { ctor public CopyOnWriteArrayList(); ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>); ctor public CopyOnWriteArrayList(E[]); - method public synchronized boolean add(E); - method public synchronized void add(int, E); - method public synchronized boolean addAll(java.util.Collection<? extends E>); - method public synchronized boolean addAll(int, java.util.Collection<? extends E>); - method public synchronized int addAllAbsent(java.util.Collection<? extends E>); - method public synchronized boolean addIfAbsent(E); - method public synchronized void clear(); + method public boolean add(E); + method public void add(int, E); + method public boolean addAll(java.util.Collection<? extends E>); + method public boolean addAll(int, java.util.Collection<? extends E>); + method public int addAllAbsent(java.util.Collection<? extends E>); + method public boolean addIfAbsent(E); + method public void clear(); method public java.lang.Object clone(); method public boolean contains(java.lang.Object); method public boolean containsAll(java.util.Collection<?>); method public void forEach(java.util.function.Consumer<? super E>); method public E get(int); - method public int indexOf(E, int); method public int indexOf(java.lang.Object); + method public int indexOf(E, int); method public boolean isEmpty(); method public java.util.Iterator<E> iterator(); - method public int lastIndexOf(E, int); method public int lastIndexOf(java.lang.Object); - method public java.util.ListIterator<E> listIterator(int); + method public int lastIndexOf(E, int); method public java.util.ListIterator<E> listIterator(); - method public synchronized E remove(int); - method public synchronized boolean remove(java.lang.Object); - method public synchronized boolean removeAll(java.util.Collection<?>); - method public synchronized boolean retainAll(java.util.Collection<?>); - method public synchronized E set(int, E); + method public java.util.ListIterator<E> listIterator(int); + method public E remove(int); + method public boolean remove(java.lang.Object); + method public boolean removeAll(java.util.Collection<?>); + method public boolean retainAll(java.util.Collection<?>); + method public E set(int, E); method public int size(); method public java.util.List<E> subList(int, int); method public java.lang.Object[] toArray(); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 673cf863391c..b656bb08cba9 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -5,7 +5,11 @@ #include "SkPixelRef.h" #include "SkImageEncoder.h" #include "SkImageInfo.h" +#include "SkColor.h" #include "SkColorPriv.h" +#include "SkHalf.h" +#include "SkPM4f.h" +#include "SkPM4fPriv.h" #include "GraphicsJNI.h" #include "SkDither.h" #include "SkUnPreMultiply.h" @@ -232,6 +236,28 @@ using namespace android::bitmap; typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, int x, int y); +static void FromColor_F16(void* dst, const SkColor src[], int width, + int, int) { + uint64_t* d = (uint64_t*)dst; + + for (int i = 0; i < width; i++) { + *d++ = SkColor4f::FromColor(*src++).premul().toF16(); + } +} + +static void FromColor_F16_Raw(void* dst, const SkColor src[], int width, + int, int) { + uint64_t* d = (uint64_t*)dst; + + for (int i = 0; i < width; i++) { + const float* color = SkColor4f::FromColor(*src++).vec(); + uint16_t* scratch = reinterpret_cast<uint16_t*>(d++); + for (int i = 0; i < 4; ++i) { + scratch[i] = SkFloatToHalf(color[i]); + } + } +} + static void FromColor_D32(void* dst, const SkColor src[], int width, int, int) { SkPMColor* d = (SkPMColor*)dst; @@ -321,6 +347,8 @@ static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) { return FromColor_D565; case kAlpha_8_SkColorType: return FromColor_DA8; + case kRGBA_F16_SkColorType: + return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_F16 : FromColor_F16_Raw; default: break; } @@ -351,8 +379,7 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int dstBitmap.notifyPixelsChanged(); - env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), - JNI_ABORT); + env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT); return true; } @@ -361,6 +388,24 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, SkColorTable*); +static void ToColor_F16_Alpha(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + uint64_t* s = (uint64_t*)src; + do { + *dst++ = SkPM4f::FromF16((const uint16_t*) s++).unpremul().toSkColor(); + } while (--width != 0); +} + +static void ToColor_F16_Raw(SkColor dst[], const void* src, int width, + SkColorTable*) { + SkASSERT(width > 0); + uint64_t* s = (uint64_t*)src; + do { + *dst++ = Sk4f_toS32(swizzle_rb(SkHalfToFloat_finite_ftz(*s++))); + } while (--width != 0); +} + static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, SkColorTable*) { SkASSERT(width > 0); @@ -520,6 +565,17 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) { } case kAlpha_8_SkColorType: return ToColor_SA8; + case kRGBA_F16_SkColorType: + switch (src.alphaType()) { + case kOpaque_SkAlphaType: + return ToColor_F16_Raw; + case kPremul_SkAlphaType: + return ToColor_F16_Alpha; + case kUnpremul_SkAlphaType: + return ToColor_F16_Raw; + default: + return NULL; + } default: break; } @@ -554,7 +610,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, - GraphicsJNI::defaultColorSpace())); + GraphicsJNI::colorSpaceForType(colorType))); sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL); if (!nativeBitmap) { @@ -562,8 +618,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, } if (jColors != NULL) { - GraphicsJNI::SetPixels(env, jColors, offset, stride, - 0, 0, width, height, bitmap); + GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); } return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable)); @@ -790,6 +845,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { const int density = p->readInt32(); if (kN32_SkColorType != colorType && + kRGBA_F16_SkColorType != colorType && kRGB_565_SkColorType != colorType && kARGB_4444_SkColorType != colorType && kIndex_8_SkColorType != colorType && @@ -800,8 +856,15 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { std::unique_ptr<SkBitmap> bitmap(new SkBitmap); - if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, - isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) { + sk_sp<SkColorSpace> colorSpace; + if (kRGBA_F16_SkColorType == colorType) { + colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named); + } else { + colorSpace = isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr; + } + + if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace), + rowBytes)) { return NULL; } diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 18ed0ed72190..69c705494932 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -283,8 +283,8 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // Create the codec. NinePatchPeeker peeker; - std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(), - &peeker)); + std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream( + streamDeleter.release(), &peeker)); if (!codec.get()) { return nullObjectReturn("SkAndroidCodec::NewFromStream returned null"); } @@ -399,7 +399,8 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // We always decode to sRGB, but only mark the bitmap with a color space if linear // blending is enabled. - SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(GraphicsJNI::defaultColorSpace()); + SkImageInfo bitmapInfo = decodeInfo.makeColorSpace( + GraphicsJNI::colorSpaceForType(decodeColorType)); if (decodeColorType == kGray_8_SkColorType) { // The legacy implementation of BitmapFactory used kAlpha8 for // grayscale images (before kGray8 existed). While the codec diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index d8984d336746..6f97c600a1cc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -297,13 +297,16 @@ enum LegacyBitmapConfig { kRGB_565_LegacyBitmapConfig = 3, kARGB_4444_LegacyBitmapConfig = 4, kARGB_8888_LegacyBitmapConfig = 5, - kHardware_LegacyBitmapConfig = 6, + kRGBA_16F_LegacyBitmapConfig = 6, + kHardware_LegacyBitmapConfig = 7, kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig }; jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) { switch (colorType) { + case kRGBA_F16_SkColorType: + return kRGBA_16F_LegacyBitmapConfig; case kN32_SkColorType: return kARGB_8888_LegacyBitmapConfig; case kARGB_4444_SkColorType: @@ -329,6 +332,7 @@ SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) { kRGB_565_SkColorType, kARGB_4444_SkColorType, kN32_SkColorType, + kRGBA_F16_SkColorType, kN32_SkColorType }; @@ -458,6 +462,19 @@ sk_sp<SkColorSpace> GraphicsJNI::defaultColorSpace() { #endif } +sk_sp<SkColorSpace> GraphicsJNI::linearColorSpace() { + return SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named); +} + +sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) { + switch (type) { + case kRGBA_F16_SkColorType: + return linearColorSpace(); + default: + return defaultColorSpace(); + } +} + /////////////////////////////////////////////////////////////////////////////// bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 03dc1fbe82fa..508c9ffd0f20 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -95,6 +95,8 @@ public: const SkBitmap& dstBitmap); static sk_sp<SkColorSpace> defaultColorSpace(); + static sk_sp<SkColorSpace> linearColorSpace(); + static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type); }; class HeapAllocator : public SkBRDAllocator { diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp index b6b524597896..bb69e27933b2 100644 --- a/core/jni/android_view_GraphicBuffer.cpp +++ b/core/jni/android_view_GraphicBuffer.cpp @@ -142,6 +142,10 @@ static inline SkColorType convertPixelFormat(int32_t format) { return kN32_SkColorType; case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType; + case PIXEL_FORMAT_RGBA_FP16: + return kRGBA_F16_SkColorType; + case PIXEL_FORMAT_RGBX_FP16: + return kRGBA_F16_SkColorType; case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType; default: diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 92693b710424..a5b7671fa1ac 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -168,6 +168,8 @@ PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( switch(format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_FP16: + case HAL_PIXEL_FORMAT_RGBX_FP16: case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_Y8: @@ -283,6 +285,8 @@ static inline SkColorType convertPixelFormat(PixelFormat format) { switch (format) { case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType; case PIXEL_FORMAT_RGBA_8888: return kN32_SkColorType; + case PIXEL_FORMAT_RGBX_FP16: return kRGBA_F16_SkColorType; + case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType; case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType; default: return kUnknown_SkColorType; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 5b8818171b6f..62c3d04f117b 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -164,6 +164,16 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, alphaType = kPremul_SkAlphaType; break; } + case PIXEL_FORMAT_RGBX_FP16: { + colorType = kRGBA_F16_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + } + case PIXEL_FORMAT_RGBA_FP16: { + colorType = kRGBA_F16_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + } case PIXEL_FORMAT_RGB_565: { colorType = kRGB_565_SkColorType; alphaType = kOpaque_SkAlphaType; diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index 3c8db7fd3c3d..d3c020215485 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -83,6 +83,14 @@ static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) colorType = kN32_SkColorType; alphaType = kOpaque_SkAlphaType; break; + case WINDOW_FORMAT_RGBA_FP16: + colorType = kRGBA_F16_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + case WINDOW_FORMAT_RGBX_FP16: + colorType = kRGBA_F16_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; case WINDOW_FORMAT_RGB_565: colorType = kRGB_565_SkColorType; alphaType = kOpaque_SkAlphaType; diff --git a/docs/html/reference/images/graphics/colorspace_ucs.png b/docs/html/reference/images/graphics/colorspace_ucs.png Binary files differnew file mode 100644 index 000000000000..3e0f0c6f0cc4 --- /dev/null +++ b/docs/html/reference/images/graphics/colorspace_ucs.png diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index cd75fe95121a..a041a28010e2 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -140,7 +140,7 @@ public final class Bitmap implements Parcelable { * Native bitmap has been reconfigured, so set premult and cached * width/height values */ - // called from JNI + @SuppressWarnings("unused") // called from JNI void reinit(int width, int height, boolean requestPremultiplied) { mWidth = width; mHeight = height; @@ -465,6 +465,15 @@ public final class Bitmap implements Parcelable { */ ARGB_8888 (5), + /** + * Each pixels is stored on 8 bytes. Each channel (RGB and alpha + * for translucency) is stored as a + * {@link android.util.Half half-precision floating point value}. + * + * This configuration is particularly suited for wide-gamut and + * HDR content. + */ + RGBA_F16 (6), /** * Special configuration, when bitmap is stored only in graphic memory. @@ -473,12 +482,12 @@ public final class Bitmap implements Parcelable { * It is optimal for cases, when the only operation with the bitmap is to draw it on a * screen. */ - HARDWARE (6); + HARDWARE (7); final int nativeInt; private static Config sConfigs[] = { - null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, HARDWARE + null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE }; Config(int ni) { @@ -760,6 +769,9 @@ public final class Bitmap implements Parcelable { case ALPHA_8: newConfig = Config.ALPHA_8; break; + case RGBA_F16: + newConfig = Config.RGBA_F16; + break; //noinspection deprecation case ARGB_4444: case ARGB_8888: @@ -781,8 +793,13 @@ public final class Bitmap implements Parcelable { neww = Math.round(deviceR.width()); newh = Math.round(deviceR.height()); - bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig, - transformed || source.hasAlpha()); + Config transformedConfig = newConfig; + if (transformed) { + if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) { + transformedConfig = Config.ARGB_8888; + } + } + bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha()); canvas.translate(-deviceR.left, -deviceR.top); canvas.concat(m); @@ -845,14 +862,14 @@ public final class Bitmap implements Parcelable { * @param width The width of the bitmap * @param height The height of the bitmap * @param config The bitmap config to create. - * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the - * bitmap as opaque. Doing so will clear the bitmap in black + * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to + * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. * * @throws IllegalArgumentException if the width or height are <= 0, or if * Config is Config.HARDWARE, because hardware bitmaps are always immutable */ - private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) { + public static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) { return createBitmap(null, width, height, config, hasAlpha); } @@ -865,14 +882,14 @@ public final class Bitmap implements Parcelable { * @param width The width of the bitmap * @param height The height of the bitmap * @param config The bitmap config to create. - * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the - * bitmap as opaque. Doing so will clear the bitmap in black + * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to + * mark the bitmap as opaque. Doing so will clear the bitmap in black * instead of transparent. * * @throws IllegalArgumentException if the width or height are <= 0, or if * Config is Config.HARDWARE, because hardware bitmaps are always immutable */ - private static Bitmap createBitmap(DisplayMetrics display, int width, int height, + public static Bitmap createBitmap(DisplayMetrics display, int width, int height, Config config, boolean hasAlpha) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); @@ -885,7 +902,7 @@ public final class Bitmap implements Parcelable { bm.mDensity = display.densityDpi; } bm.setHasAlpha(hasAlpha); - if (config == Config.ARGB_8888 && !hasAlpha) { + if ((config == Config.ARGB_8888 || config == Config.RGBA_F16) && !hasAlpha) { nativeErase(bm.mNativePtr, 0xff000000); } // No need to initialize the bitmap to zeroes with other configs; @@ -1526,7 +1543,7 @@ public final class Bitmap implements Parcelable { /** * <p>Replace pixels in the bitmap with the colors in the array. Each element - * in the array is a packed int prepresenting a non-premultiplied ARGB + * in the array is a packed int representing a non-premultiplied ARGB * {@link Color}.</p> * * @param pixels The colors to write to the bitmap diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 7dc5de3051e6..d968516f36bc 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1671,6 +1671,28 @@ public abstract class ColorSpace { } /** + * Converts values from CIE xyY to CIE L*u*v*. Y is assumed to be 1 so the + * input xyY array only contains the x and y components. After this method + * returns, the xyY array contains the converted u and v components. + * + * @param xyY The xyY value to convert to XYZ, cannot be null, + * length must be a multiple of 2 + */ + private static void xyYToUv(@NonNull @Size(multiple = 2) float[] xyY) { + for (int i = 0; i < xyY.length; i += 2) { + float x = xyY[i]; + float y = xyY[i + 1]; + + float d = -2.0f * x + 12.0f * y + 3; + float u = (4.0f * x) / d; + float v = (9.0f * y) / d; + + xyY[i] = u; + xyY[i + 1] = v; + } + } + + /** * <p>Computes the chromatic adaptation transform from the specified * source white point to the specified destination white point.</p> * @@ -3162,10 +3184,10 @@ public abstract class ColorSpace { /** * <p>A color space renderer can be used to visualize and compare the gamut and * white point of one or more color spaces. The output is an sRGB {@link Bitmap} - * showing a CIE 1931 xyY chromaticity diagram.</p> + * showing a CIE 1931 xyY or a CIE 1976 UCS chromaticity diagram.</p> * * <p>The following code snippet shows how to compare the {@link Named#SRGB} - * and {@link Named#DCI_P3} color spaces:</p> + * and {@link Named#DCI_P3} color spaces in a CIE 1931 diagram:</p> * * <pre class="prettyprint"> * Bitmap bitmap = ColorSpace.createRenderer() @@ -3188,12 +3210,18 @@ public abstract class ColorSpace { */ public static class Renderer { private static final int NATIVE_SIZE = 1440; + private static final float UCS_SCALE = 9.0f / 6.0f; + + // Number of subdivision of the inside of the spectral locus + private static final int CHROMATICITY_RESOLUTION = 32; + private static final double ONE_THIRD = 1.0 / 3.0; @IntRange(from = 128, to = Integer.MAX_VALUE) private int mSize = 1024; private boolean mShowWhitePoint = true; private boolean mClip = false; + private boolean mUcs = false; private final List<Pair<ColorSpace, Integer>> mColorSpaces = new ArrayList<>(2); private final List<Point> mPoints = new ArrayList<>(0); @@ -3241,6 +3269,35 @@ public abstract class ColorSpace { } /** + * <p>Defines whether the chromaticity diagram should use the uniform + * chromaticity scale (CIE 1976 UCS). When the uniform chromaticity scale + * is used, the distance between two points on the diagram is approximately + * proportional to the perceived color difference.</p> + * + * <p>The following code snippet shows how to enable the uniform chromaticity + * scale. The image below shows the result:</p> + * <pre class="prettyprint"> + * Bitmap bitmap = ColorSpace.createRenderer() + * .uniformChromaticityScale(true) + * .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff) + * .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845) + * .render(); + * </pre> + * <p> + * <img src="{@docRoot}reference/android/images/graphics/colorspace_ucs.png" /> + * <figcaption style="text-align: center;">CIE 1976 UCS diagram</figcaption> + * </p> + * + * @param ucs True to render the chromaticity diagram as the CIE 1976 UCS diagram + * @return This instance of {@link Renderer} + */ + @NonNull + public Renderer uniformChromaticityScale(boolean ucs) { + mUcs = ucs; + return this; + } + + /** * Sets the dimensions (width and height) in pixels of the output bitmap. * The size must be at least 128px and defaults to 1024px. * @@ -3302,7 +3359,7 @@ public abstract class ColorSpace { * </pre> * <p> * <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison2.png" /> - * <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption> + * <figcaption style="text-align: center;">sRGB, DCI-P3, ACES and scRGB</figcaption> * </p> * * @param colorSpace The color space whose gamut to render on the diagram @@ -3385,6 +3442,7 @@ public abstract class ColorSpace { setTransform(canvas, width, height, primaries); drawBox(canvas, width, height, paint, path); + setUcsTransform(canvas, height); drawLocus(canvas, width, height, paint, path, primaries); drawGamuts(canvas, width, height, paint, path, primaries, whitePoint); drawPoints(canvas, width, height, paint); @@ -3406,7 +3464,11 @@ public abstract class ColorSpace { paint.setStyle(Paint.Style.FILL); + float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f); + float[] v = new float[3]; + float[] xy = new float[2]; + for (final Point point : mPoints) { v[0] = point.mRgb[0]; v[1] = point.mRgb[1]; @@ -3415,10 +3477,13 @@ public abstract class ColorSpace { paint.setColor(point.mColor); - // XYZ to xyY, assuming Y=1.0 + // XYZ to xyY, assuming Y=1.0, then to L*u*v* if needed float sum = v[0] + v[1] + v[2]; - canvas.drawCircle(width * v[0] / sum, height - height * v[1] / sum, - 4.0f, paint); + xy[0] = v[0] / sum; + xy[1] = v[1] / sum; + if (mUcs) xyYToUv(xy); + + canvas.drawCircle(width * xy[0], height - height * xy[1], radius, paint); } } @@ -3440,6 +3505,8 @@ public abstract class ColorSpace { @NonNull Paint paint, @NonNull Path path, @NonNull @Size(6) float[] primaries, @NonNull @Size(2) float[] whitePoint) { + float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f); + for (final Pair<ColorSpace, Integer> item : mColorSpaces) { ColorSpace colorSpace = item.first; int color = item.second; @@ -3447,7 +3514,7 @@ public abstract class ColorSpace { if (colorSpace.getModel() != Model.RGB) continue; Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries); + getPrimaries(rgb, primaries, mUcs); path.rewind(); path.moveTo(width * primaries[0], height - height * primaries[1]); @@ -3462,11 +3529,12 @@ public abstract class ColorSpace { // Draw the white point if (mShowWhitePoint) { rgb.getWhitePoint(whitePoint); + if (mUcs) xyYToUv(whitePoint); paint.setStyle(Paint.Style.FILL); paint.setColor(color); - canvas.drawCircle(width * whitePoint[0], height - height * whitePoint[1], - 4.0f, paint); + canvas.drawCircle( + width * whitePoint[0], height - height * whitePoint[1], radius, paint); } } } @@ -3477,10 +3545,12 @@ public abstract class ColorSpace { * * @param rgb The color space whose primaries to extract * @param primaries A pre-allocated array of 6 floats that will hold the result + * @param asUcs True if the primaries should be returned in Luv, false for xyY */ @NonNull @Size(6) - private static float[] getPrimaries(@NonNull Rgb rgb, @NonNull @Size(6) float[] primaries) { + private static float[] getPrimaries(@NonNull Rgb rgb, + @NonNull @Size(6) float[] primaries, boolean asUcs) { // TODO: We should find a better way to handle these cases if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) || rgb.equals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB))) { @@ -3490,9 +3560,11 @@ public abstract class ColorSpace { primaries[3] = 1.24f; primaries[4] = -0.23f; primaries[5] = -0.57f; - return primaries; + } else { + rgb.getPrimaries(primaries); } - return rgb.getPrimaries(primaries); + if (asUcs) xyYToUv(primaries); + return primaries; } /** @@ -3513,7 +3585,13 @@ public abstract class ColorSpace { int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6; float[] vertices = new float[vertexCount * 2]; int[] colors = new int[vertices.length]; - computeChromaticityMesh(NATIVE_SIZE, NATIVE_SIZE, vertices, colors); + computeChromaticityMesh(vertices, colors); + + if (mUcs) xyYToUv(vertices); + for (int i = 0; i < vertices.length; i += 2) { + vertices[i] *= width; + vertices[i + 1] = height - vertices[i + 1] * height; + } // Draw the spectral locus if (mClip && mColorSpaces.size() > 0) { @@ -3522,7 +3600,8 @@ public abstract class ColorSpace { if (colorSpace.getModel() != Model.RGB) continue; Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries); + getPrimaries(rgb, primaries, mUcs); + break; } @@ -3559,6 +3638,7 @@ public abstract class ColorSpace { } path.close(); + paint.setStrokeWidth(4.0f / (mUcs ? UCS_SCALE : 1.0f)); paint.setStyle(Paint.Style.STROKE); paint.setColor(0xff000000); canvas.drawPath(path, paint); @@ -3576,25 +3656,38 @@ public abstract class ColorSpace { */ private void drawBox(@NonNull Canvas canvas, int width, int height, @NonNull Paint paint, @NonNull Path path) { + + int lineCount = 10; + float scale = 1.0f; + if (mUcs) { + lineCount = 7; + scale = UCS_SCALE; + } + // Draw the unit grid paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(2.0f); paint.setColor(0xffc0c0c0); - for (int i = 1; i <= 9; i++) { - canvas.drawLine(0.0f, height - (height * i / 10.0f), - 0.9f * width, height - (height * i / 10.0f), paint); - canvas.drawLine(width * i / 10.0f, height, - width * i / 10.0f, 0.1f * height, paint); + + for (int i = 1; i < lineCount - 1; i++) { + float v = i / 10.0f; + float x = (width * v) * scale; + float y = height - (height * v) * scale; + + canvas.drawLine(0.0f, y, 0.9f * width, y, paint); + canvas.drawLine(x, height, x, 0.1f * height, paint); } // Draw tick marks paint.setStrokeWidth(4.0f); paint.setColor(0xff000000); - for (int i = 1; i <= 9; i++) { - canvas.drawLine(0.0f, height - (height * i / 10.0f), - width / 100.0f, height - (height * i / 10.0f), paint); - canvas.drawLine(width * i / 10.0f, height, - width * i / 10.0f, height - (height / 100.0f), paint); + for (int i = 1; i < lineCount - 1; i++) { + float v = i / 10.0f; + float x = (width * v) * scale; + float y = height - (height * v) * scale; + + canvas.drawLine(0.0f, y, width / 100.0f, y, paint); + canvas.drawLine(x, height, x, height - (height / 100.0f), paint); } // Draw the axis labels @@ -3603,14 +3696,15 @@ public abstract class ColorSpace { paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL)); Rect bounds = new Rect(); - for (int i = 1; i < 9; i++) { + for (int i = 1; i < lineCount - 1; i++) { String text = "0." + i; paint.getTextBounds(text, 0, text.length(), bounds); - float y = height - (height * i / 10.0f); - canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint); + float v = i / 10.0f; + float x = (width * v) * scale; + float y = height - (height * v) * scale; - float x = width * i / 10.0f; + canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint); canvas.drawText(text, x - bounds.width() / 2.0f, height + bounds.height() + 16, paint); } @@ -3643,7 +3737,7 @@ public abstract class ColorSpace { if (colorSpace.getModel() != Model.RGB) continue; Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries); + getPrimaries(rgb, primaries, mUcs); primariesBounds.left = Math.min(primariesBounds.left, primaries[4]); primariesBounds.top = Math.min(primariesBounds.top, primaries[5]); @@ -3651,26 +3745,42 @@ public abstract class ColorSpace { primariesBounds.bottom = Math.max(primariesBounds.bottom, primaries[3]); } + float max = mUcs ? 0.6f : 0.9f; + primariesBounds.left = Math.min(0.0f, primariesBounds.left); primariesBounds.top = Math.min(0.0f, primariesBounds.top); - primariesBounds.right = Math.max(0.9f, primariesBounds.right); - primariesBounds.bottom = Math.max(0.9f, primariesBounds.bottom); + primariesBounds.right = Math.max(max, primariesBounds.right); + primariesBounds.bottom = Math.max(max, primariesBounds.bottom); - float scaleX = 0.9f / primariesBounds.width(); - float scaleY = 0.9f / primariesBounds.height(); + float scaleX = max / primariesBounds.width(); + float scaleY = max / primariesBounds.height(); float scale = Math.min(scaleX, scaleY); canvas.scale(mSize / (float) NATIVE_SIZE, mSize / (float) NATIVE_SIZE); canvas.scale(scale, scale); canvas.translate( - (primariesBounds.width() - 0.9f) * width / 2.0f, - (primariesBounds.height() - 0.9f) * height / 2.0f); + (primariesBounds.width() - max) * width / 2.0f, + (primariesBounds.height() - max) * height / 2.0f); // The spectrum extends ~0.85 vertically and ~0.65 horizontally // We shift the canvas a little bit to get nicer margins canvas.translate(0.05f * width, -0.05f * height); } + /** + * Computes and applies the Canvas transforms required to render the CIE + * 197 UCS chromaticity diagram. + * + * @param canvas The canvas to transform + * @param height Height in pixel of the final image + */ + private void setUcsTransform(@NonNull Canvas canvas, int height) { + if (mUcs) { + canvas.translate(0.0f, (height - height * UCS_SCALE)); + canvas.scale(UCS_SCALE, UCS_SCALE); + } + } + // X coordinates of the spectral locus in CIE 1931 private static final float[] SPECTRUM_LOCUS_X = { 0.175596f, 0.172787f, 0.170806f, 0.170085f, 0.160343f, @@ -3716,21 +3826,15 @@ public abstract class ColorSpace { 0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f }; - // Number of subdivision of the inside of the spectral locus - private static final int CHROMATICITY_RESOLUTION = 32; - private static final double ONE_THIRD = 1.0 / 3.0; - /** * Computes a 2D mesh representation of the CIE 1931 chromaticity * diagram. * - * @param width Width in pixels of the mesh - * @param height Height in pixels of the mesh * @param vertices Array of floats that will hold the mesh vertices * @param colors Array of floats that will hold the mesh colors */ - private static void computeChromaticityMesh(int width, int height, - @NonNull float[] vertices, @NonNull int[] colors) { + private static void computeChromaticityMesh(@NonNull float[] vertices, + @NonNull int[] colors) { ColorSpace colorSpace = get(Named.SRGB); @@ -3796,18 +3900,18 @@ public abstract class ColorSpace { colorIndex += 6; // Flip the mesh upside down to match Canvas' coordinates system - vertices[vertexIndex++] = v1x * width; - vertices[vertexIndex++] = height - v1y * height; - vertices[vertexIndex++] = v2x * width; - vertices[vertexIndex++] = height - v2y * height; - vertices[vertexIndex++] = v3x * width; - vertices[vertexIndex++] = height - v3y * height; - vertices[vertexIndex++] = v1x * width; - vertices[vertexIndex++] = height - v1y * height; - vertices[vertexIndex++] = v3x * width; - vertices[vertexIndex++] = height - v3y * height; - vertices[vertexIndex++] = v4x * width; - vertices[vertexIndex++] = height - v4y * height; + vertices[vertexIndex++] = v1x; + vertices[vertexIndex++] = v1y; + vertices[vertexIndex++] = v2x; + vertices[vertexIndex++] = v2y; + vertices[vertexIndex++] = v3x; + vertices[vertexIndex++] = v3y; + vertices[vertexIndex++] = v1x; + vertices[vertexIndex++] = v1y; + vertices[vertexIndex++] = v3x; + vertices[vertexIndex++] = v3y; + vertices[vertexIndex++] = v4x; + vertices[vertexIndex++] = v4y; } } } diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java index 98082cae8379..0fa52f895dd9 100644 --- a/graphics/java/android/graphics/PixelFormat.java +++ b/graphics/java/android/graphics/PixelFormat.java @@ -22,13 +22,17 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class PixelFormat { - /** @hide */ @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE}) @Retention(RetentionPolicy.SOURCE) public @interface Opacity {} - /* these constants need to match those in hardware/hardware.h */ + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565}) + public @interface Format { }; + + // NOTE: these constants must match the values from graphics/common/x.x/types.hal public static final int UNKNOWN = 0; @@ -62,7 +66,6 @@ public class PixelFormat { @Deprecated public static final int RGB_332 = 0xB; - /** * @deprecated use {@link android.graphics.ImageFormat#NV16 * ImageFormat.NV16} instead. @@ -84,6 +87,9 @@ public class PixelFormat { @Deprecated public static final int YCbCr_422_I = 0x14; + public static final int RGBA_F16 = 0x16; + public static final int RGBX_F16 = 0x17; + /** * @deprecated use {@link android.graphics.ImageFormat#JPEG * ImageFormat.JPEG} instead. @@ -91,7 +97,10 @@ public class PixelFormat { @Deprecated public static final int JPEG = 0x100; - public static void getPixelFormatInfo(int format, PixelFormat info) { + public int bytesPerPixel; + public int bitsPerPixel; + + public static void getPixelFormatInfo(@Format int format, PixelFormat info) { switch (format) { case RGBA_8888: case RGBX_8888: @@ -124,18 +133,24 @@ public class PixelFormat { info.bitsPerPixel = 12; info.bytesPerPixel = 1; break; + case RGBA_F16: + case RGBX_F16: + info.bitsPerPixel = 64; + info.bytesPerPixel = 8; + break; default: throw new IllegalArgumentException("unknown pixel format " + format); } } - public static boolean formatHasAlpha(int format) { + public static boolean formatHasAlpha(@Format int format) { switch (format) { case PixelFormat.A_8: case PixelFormat.LA_88: case PixelFormat.RGBA_4444: case PixelFormat.RGBA_5551: case PixelFormat.RGBA_8888: + case PixelFormat.RGBA_F16: case PixelFormat.TRANSLUCENT: case PixelFormat.TRANSPARENT: return true; @@ -143,9 +158,6 @@ public class PixelFormat { return false; } - public int bytesPerPixel; - public int bitsPerPixel; - /** * Determine whether or not this is a public-visible and non-deprecated {@code format}. * @@ -159,12 +171,14 @@ public class PixelFormat { * * @hide */ - public static boolean isPublicFormat(int format) { + public static boolean isPublicFormat(@Format int format) { switch (format) { case RGBA_8888: case RGBX_8888: case RGB_888: case RGB_565: + case RGBA_F16: + case RGBX_F16: return true; } diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp index 4c8abc5da832..938b6efa7901 100644 --- a/libs/hwui/OpenGLReadback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -131,6 +131,13 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, destWidth, destHeight, caches.maxTextureSize); return CopyResult::DestinationInvalid; } + + // TODO: Add support for RGBA_F16 destinations + if (bitmap->colorType() == kRGBA_F16_SkColorType) { + ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); + return CopyResult::DestinationInvalid; + } + GLuint fbo = renderState.createFramebuffer(); if (!fbo) { ALOGW("Could not obtain an FBO"); diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 5b5b74e1c3f3..705395e1e2b7 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -225,6 +225,12 @@ void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType color *outInternalFormat = GL_LUMINANCE; *outType = GL_UNSIGNED_BYTE; break; + case kRGBA_F16_SkColorType: + // This format is always linear + *outFormat = GL_RGBA; + *outInternalFormat = GL_RGBA16F; + *outType = GL_HALF_FLOAT; + break; default: LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); break; diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 2177af1773e5..a9058b1fb6f6 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -108,6 +108,8 @@ static PixelFormat internalFormatToPixelFormat(GLint internalFormat) { return PIXEL_FORMAT_RGBA_8888; case GL_RGB: return PIXEL_FORMAT_RGB_565; + case GL_RGBA16F: + return PIXEL_FORMAT_RGBA_FP16; default: LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat); return PIXEL_FORMAT_UNKNOWN; @@ -306,7 +308,8 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) { PixelFormat format = graphicBuffer->getPixelFormat(); - if (!graphicBuffer.get() || format != PIXEL_FORMAT_RGBA_8888) { + if (!graphicBuffer.get() || + (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) { return nullptr; } SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), |