diff options
author | 2017-02-27 11:00:04 -0800 | |
---|---|---|
committer | 2017-03-06 19:33:25 -0800 | |
commit | efb4b06493fe7b1604c762a448b13c7af2845a8d (patch) | |
tree | 310d40716decb7686065339226534dac0bb35faa | |
parent | 4f357c081260c5bb36498169eb698adf7089899c (diff) |
Add ColorSpace information on Bitmap
This is the first step toward interpreting color spaces at render time.
Bug: 32984164
Test: BitmapColorSpaceTest in CtsGraphicsTestCases
Change-Id: I0164a18f1ed74a745874fe5229168042afe27a04
-rw-r--r-- | api/current.txt | 19 | ||||
-rw-r--r-- | api/system-current.txt | 19 | ||||
-rw-r--r-- | api/test-current.txt | 19 | ||||
-rwxr-xr-x | core/jni/android/graphics/Bitmap.cpp | 60 | ||||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 47 | ||||
-rw-r--r-- | graphics/java/android/graphics/ColorSpace.java | 691 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 2 | ||||
-rw-r--r-- | libs/hwui/Extensions.cpp | 17 | ||||
-rw-r--r-- | libs/hwui/Extensions.h | 7 | ||||
-rw-r--r-- | libs/hwui/GradientCache.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/GradientCache.h | 2 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.h | 2 | ||||
-rw-r--r-- | libs/hwui/Texture.cpp | 16 | ||||
-rw-r--r-- | libs/hwui/Texture.h | 6 | ||||
-rw-r--r-- | libs/hwui/TextureCache.h | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Bitmap.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/renderstate/RenderState.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/utils/Color.h | 2 |
19 files changed, 790 insertions, 143 deletions
diff --git a/api/current.txt b/api/current.txt index cf6c925a6efe..7dcd0eda6fc2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12201,6 +12201,7 @@ package android.graphics { method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]); method public final int getAllocationByteCount(); method public final int getByteCount(); + method public final android.graphics.ColorSpace getColorSpace(); method public final android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method public int getGenerationId(); @@ -12583,6 +12584,7 @@ package android.graphics { method public java.lang.String getName(); method public boolean isSrgb(); method public abstract boolean isWideGamut(); + method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters); method public float[] toXyz(float, float, float); method public abstract float[] toXyz(float[]); field public static final float[] ILLUMINANT_A; @@ -12667,6 +12669,10 @@ package android.graphics { public static class ColorSpace.Rgb extends android.graphics.ColorSpace { ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator); ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float); + ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], double); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double); method public float[] fromLinear(float, float, float); method public float[] fromLinear(float[]); method public float[] fromXyz(float[]); @@ -12678,6 +12684,7 @@ package android.graphics { method public java.util.function.DoubleUnaryOperator getOetf(); method public float[] getPrimaries(float[]); method public float[] getPrimaries(); + method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters(); method public float[] getTransform(float[]); method public float[] getTransform(); method public float[] getWhitePoint(float[]); @@ -12688,6 +12695,18 @@ package android.graphics { method public float[] toXyz(float[]); } + public static class ColorSpace.Rgb.TransferParameters { + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double); + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double); + field public final double a; + field public final double b; + field public final double c; + field public final double d; + field public final double e; + field public final double f; + field public final double g; + } + public class ComposePathEffect extends android.graphics.PathEffect { ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect); } diff --git a/api/system-current.txt b/api/system-current.txt index 7ef4db8d894b..961f53c6eea4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12836,6 +12836,7 @@ package android.graphics { method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]); method public final int getAllocationByteCount(); method public final int getByteCount(); + method public final android.graphics.ColorSpace getColorSpace(); method public final android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method public int getGenerationId(); @@ -13218,6 +13219,7 @@ package android.graphics { method public java.lang.String getName(); method public boolean isSrgb(); method public abstract boolean isWideGamut(); + method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters); method public float[] toXyz(float, float, float); method public abstract float[] toXyz(float[]); field public static final float[] ILLUMINANT_A; @@ -13302,6 +13304,10 @@ package android.graphics { public static class ColorSpace.Rgb extends android.graphics.ColorSpace { ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator); ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float); + ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], double); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double); method public float[] fromLinear(float, float, float); method public float[] fromLinear(float[]); method public float[] fromXyz(float[]); @@ -13313,6 +13319,7 @@ package android.graphics { method public java.util.function.DoubleUnaryOperator getOetf(); method public float[] getPrimaries(float[]); method public float[] getPrimaries(); + method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters(); method public float[] getTransform(float[]); method public float[] getTransform(); method public float[] getWhitePoint(float[]); @@ -13323,6 +13330,18 @@ package android.graphics { method public float[] toXyz(float[]); } + public static class ColorSpace.Rgb.TransferParameters { + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double); + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double); + field public final double a; + field public final double b; + field public final double c; + field public final double d; + field public final double e; + field public final double f; + field public final double g; + } + public class ComposePathEffect extends android.graphics.PathEffect { ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect); } diff --git a/api/test-current.txt b/api/test-current.txt index 5e4911c39e39..6491e525eb97 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12238,6 +12238,7 @@ package android.graphics { method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]); method public final int getAllocationByteCount(); method public final int getByteCount(); + method public final android.graphics.ColorSpace getColorSpace(); method public final android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method public int getGenerationId(); @@ -12620,6 +12621,7 @@ package android.graphics { method public java.lang.String getName(); method public boolean isSrgb(); method public abstract boolean isWideGamut(); + method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters); method public float[] toXyz(float, float, float); method public abstract float[] toXyz(float[]); field public static final float[] ILLUMINANT_A; @@ -12704,6 +12706,10 @@ package android.graphics { public static class ColorSpace.Rgb extends android.graphics.ColorSpace { ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator); ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float); + ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters); + ctor public ColorSpace.Rgb(java.lang.String, float[], double); + ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double); method public float[] fromLinear(float, float, float); method public float[] fromLinear(float[]); method public float[] fromXyz(float[]); @@ -12715,6 +12721,7 @@ package android.graphics { method public java.util.function.DoubleUnaryOperator getOetf(); method public float[] getPrimaries(float[]); method public float[] getPrimaries(); + method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters(); method public float[] getTransform(float[]); method public float[] getTransform(); method public float[] getWhitePoint(float[]); @@ -12725,6 +12732,18 @@ package android.graphics { method public float[] toXyz(float[]); } + public static class ColorSpace.Rgb.TransferParameters { + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double); + ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double); + field public final double a; + field public final double b; + field public final double c; + field public final double d; + field public final double e; + field public final double f; + field public final double g; + } + public class ComposePathEffect extends android.graphics.PathEffect { ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect); } diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index a196540078f5..2766689c23c4 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -9,6 +9,7 @@ #include "SkColor.h" #include "SkColorPriv.h" #include "SkHalf.h" +#include "SkMatrix44.h" #include "SkPM4f.h" #include "SkPM4fPriv.h" #include "GraphicsJNI.h" @@ -1168,6 +1169,61 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, /////////////////////////////////////////////////////////////////////////////// +static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_TRUE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + return colorSpace == nullptr || + colorSpace == SkColorSpace::MakeSRGB().get() || + colorSpace == SkColorSpace::MakeRGB( + SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kSRGB_Gamut, + SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get(); +} + +static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, + jfloatArray xyzArray, jfloatArray paramsArray) { + + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + if (colorSpace == nullptr) return JNI_FALSE; + + SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE; + + jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL); + xyz[0] = xyzMatrix.getFloat(0, 0); + xyz[1] = xyzMatrix.getFloat(1, 0); + xyz[2] = xyzMatrix.getFloat(2, 0); + xyz[3] = xyzMatrix.getFloat(0, 1); + xyz[4] = xyzMatrix.getFloat(1, 1); + xyz[5] = xyzMatrix.getFloat(2, 1); + xyz[6] = xyzMatrix.getFloat(0, 2); + xyz[7] = xyzMatrix.getFloat(1, 2); + xyz[8] = xyzMatrix.getFloat(2, 2); + env->ReleaseFloatArrayElements(xyzArray, xyz, 0); + + SkColorSpaceTransferFn transferParams; + if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE; + + jfloat* params = env->GetFloatArrayElements(paramsArray, NULL); + params[0] = transferParams.fA; + params[1] = transferParams.fB; + params[2] = transferParams.fC; + params[3] = transferParams.fD; + params[4] = transferParams.fE; + params[5] = transferParams.fF; + params[6] = transferParams.fG; + env->ReleaseFloatArrayElements(paramsArray, params, 0); + + return JNI_TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// + static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, jint x, jint y) { SkBitmap bitmap; @@ -1459,7 +1515,9 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;", (void*) Bitmap_createHardwareBitmap }, { "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;", - (void*) Bitmap_createGraphicBufferHandle } + (void*) Bitmap_createGraphicBufferHandle }, + { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace }, + { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, }; int register_android_graphics_Bitmap(JNIEnv* env) diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index ba7f05d86fc8..9cd9d3d75749 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -19,12 +19,12 @@ package android.graphics; import android.annotation.CheckResult; import android.annotation.ColorInt; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.os.Trace; import android.util.DisplayMetrics; import android.util.Log; - import libcore.util.NativeAllocationRegistry; import java.io.OutputStream; @@ -73,6 +73,8 @@ public final class Bitmap implements Parcelable { private int mHeight; private boolean mRecycled; + private ColorSpace mColorSpace; + /** @hide */ public int mDensity = getDefaultDensity(); @@ -1435,6 +1437,47 @@ public final class Bitmap implements Parcelable { } /** + * Returns the color space associated with this bitmap. If the color + * space is unknown, this method returns null. + */ + @Nullable + public final ColorSpace getColorSpace() { + // A reconfigure can change the configuration and rgba16f is + // always linear scRGB at this time + if (getConfig() == Config.RGBA_F16) { + // Reset the color space for potential future reconfigurations + mColorSpace = null; + return ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB); + } + + // Cache the color space retrieval since it can be fairly expensive + if (mColorSpace == null) { + if (nativeIsSRGB(mNativePtr)) { + mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + } else { + float[] xyz = new float[9]; + float[] params = new float[7]; + + boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params); + if (hasColorSpace) { + ColorSpace.Rgb.TransferParameters parameters = + new ColorSpace.Rgb.TransferParameters( + params[0], params[1], params[2], + params[3], params[4], params[5], params[6]); + ColorSpace cs = ColorSpace.match(xyz, parameters); + if (cs != null) { + mColorSpace = cs; + } else { + mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters); + } + } + } + } + + return mColorSpace; + } + + /** * Fills the bitmap's pixels with the specified {@link Color}. * * @throws IllegalStateException if the bitmap is not mutable. @@ -1816,4 +1859,6 @@ public final class Bitmap implements Parcelable { private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap); private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer); private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap); + private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params); + private static native boolean nativeIsSRGB(long nativePtr); } diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index b1a433cbaaeb..908ec5045b27 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -20,8 +20,8 @@ import android.annotation.AnyThread; import android.annotation.ColorInt; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.Size; import android.annotation.Nullable; +import android.annotation.Size; import android.util.Pair; import java.util.ArrayList; @@ -263,18 +263,18 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} - * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\ - * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases} + * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\ + * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases} * \end{equation}\) * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} - * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\ - * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases} + * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\ + * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases} * \end{equation}\) * </td> * </tr> @@ -298,11 +298,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1 (Linear)</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{sRGB} = C_{linear}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{sRGB}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr> @@ -325,22 +325,22 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">scRGB-nl IEC 61966-2-2:2003</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{scRGB} = \begin{cases} sign(C_{linear}) 12.92 \times \left| C_{linear} \right| & - * \left| C_{linear} \right| \le 0.0031308 \\ + * \left| C_{linear} \right| \lt 0.0031308 \\ * sign(C_{linear}) 1.055 \times \left| C_{linear} \right| ^{\frac{1}{2.4}} - 0.055 & - * \left| C_{linear} \right| \gt 0.0031308 \end{cases} + * \left| C_{linear} \right| \ge 0.0031308 \end{cases} * \end{equation}\) * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}sign(C_{scRGB}) \frac{\left| C_{scRGB} \right|}{12.92} & - * \left| C_{scRGB} \right| \le 0.04045 \\ + * \left| C_{scRGB} \right| \lt 0.04045 \\ * sign(C_{scRGB}) \left( \frac{\left| C_{scRGB} \right| + 0.055}{1.055} \right) ^{2.4} & - * \left| C_{scRGB} \right| \gt 0.04045 \end{cases} + * \left| C_{scRGB} \right| \ge 0.04045 \end{cases} * \end{equation}\) * </td> * </tr> @@ -364,11 +364,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">scRGB IEC 61966-2-2:2003</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{scRGB} = C_{linear}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{scRGB}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([-0.5..7.499[\)</td></tr> @@ -391,7 +391,7 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.709-5</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases} @@ -399,7 +399,7 @@ public abstract class ColorSpace { * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases} @@ -426,7 +426,7 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.2020-1</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{BT2020} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.0181 \\ * 1.0993 \times C_{linear}^{\frac{1}{2.2}} - 0.0993 & C_{linear} \ge 0.0181 \end{cases} @@ -434,7 +434,7 @@ public abstract class ColorSpace { * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}\frac{C_{BT2020}}{4.5} & C_{BT2020} \lt 0.08145 \\ * \left( \frac{C_{BT2020} + 0.0993}{1.0993} \right) ^{2.2} & C_{BT2020} \ge 0.08145 \end{cases} @@ -461,11 +461,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">SMPTE RP 431-2-2007 DCI (P3)</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">N/A</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{P3} = C_{linear}^{\frac{1}{2.6}}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{P3}^{2.6}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr> @@ -488,18 +488,18 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">Display P3</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} - * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\ - * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases} + * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\ + * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases} * \end{equation}\) * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} - * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\ - * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases} + * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\ + * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases} * \end{equation}\) * </td> * </tr> @@ -523,7 +523,7 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">NTSC (1953)</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">C</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases} @@ -531,7 +531,7 @@ public abstract class ColorSpace { * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases} @@ -558,7 +558,7 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">SMPTE-C RGB</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\ * 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases} @@ -566,7 +566,7 @@ public abstract class ColorSpace { * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\ * \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases} @@ -593,11 +593,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">Adobe RGB (1998)</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{RGB} = C_{linear}^{\frac{1}{2.2}}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{RGB}^{2.2}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr> @@ -620,7 +620,7 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">ROMM RGB ISO 22028-2:2013</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(\begin{equation} * C_{ROMM} = \begin{cases} 16 \times C_{linear} & C_{linear} \lt 0.001953 \\ * C_{linear}^{\frac{1}{1.8}} & C_{linear} \ge 0.001953 \end{cases} @@ -628,7 +628,7 @@ public abstract class ColorSpace { * </td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(\begin{equation} * C_{linear} = \begin{cases}\frac{C_{ROMM}}{16} & C_{ROMM} \lt 0.031248 \\ * C_{ROMM}^{1.8} & C_{ROMM} \ge 0.031248 \end{cases} @@ -655,11 +655,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">SMPTE ST 2065-1:2012 ACES</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{ACES} = C_{linear}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{ACES}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr> @@ -682,11 +682,11 @@ public abstract class ColorSpace { * <tr><td>Name</td><td colspan="4">Academy S-2014-004 ACEScg</td></tr> * <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr> * <tr> - * <td>Opto-electronic transfer function</td> + * <td>Opto-electronic transfer function (OETF)</td> * <td colspan="4">\(C_{ACEScg} = C_{linear}\)</td> * </tr> * <tr> - * <td>Electro-optical transfer function</td> + * <td>Electro-optical transfer function (EOTF)</td> * <td colspan="4">\(C_{linear} = C_{ACEScg}\)</td> * </tr> * <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr> @@ -1379,6 +1379,37 @@ public abstract class ColorSpace { } /** + * <p>Returns a {@link Named} instance of {@link ColorSpace} that matches + * the specified RGB to CIE XYZ transform and transfer functions. If no + * instance can be found, this method returns null.</p> + * + * <p>The color transform matrix is assumed to target the CIE XYZ space + * a {@link #ILLUMINANT_D50 D50} standard illuminant.</p> + * + * @param toXYZD50 3x3 column-major transform matrix from RGB to the profile + * connection space CIE XYZ as an array of 9 floats, cannot be null + * @param function Parameters for the transfer functions + * @return A non-null {@link ColorSpace} if a match is found, null otherwise + */ + @Nullable + public static ColorSpace match( + @NonNull @Size(9) float[] toXYZD50, + @NonNull Rgb.TransferParameters function) { + + for (ColorSpace colorSpace : sNamedColorSpaces) { + if (colorSpace.getModel() == Model.RGB) { + ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ); + if (compare(toXYZD50, rgb.mTransform) && + compare(function, rgb.mTransferParameters)) { + return colorSpace; + } + } + } + + return null; + } + + /** * <p>Creates a new {@link Renderer} that can be used to visualize and * debug color spaces. See the documentation of {@link Renderer} for * more information.</p> @@ -1397,17 +1428,14 @@ public abstract class ColorSpace { "sRGB IEC61966-2.1", SRGB_PRIMARIES, ILLUMINANT_D65, - x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), - x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), Named.SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb( "sRGB IEC61966-2.1 (Linear)", SRGB_PRIMARIES, ILLUMINANT_D65, - DoubleUnaryOperator.identity(), - DoubleUnaryOperator.identity(), + 1.0, 0.0f, 1.0f, Named.LINEAR_SRGB.ordinal() ); @@ -1415,17 +1443,16 @@ public abstract class ColorSpace { "scRGB-nl IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, - x -> absRcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), - x -> absResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), + x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), + x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), -0.799f, 2.399f, Named.EXTENDED_SRGB.ordinal() ); sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( - "scRGB- IEC 61966-2-2:2003", + "scRGB IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, - DoubleUnaryOperator.identity(), - DoubleUnaryOperator.identity(), + 1.0, -0.5f, 7.499f, Named.LINEAR_EXTENDED_SRGB.ordinal() ); @@ -1433,26 +1460,21 @@ public abstract class ColorSpace { "Rec. ITU-R BT.709-5", new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f }, ILLUMINANT_D65, - x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45), Named.BT709.ordinal() ); sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb( "Rec. ITU-R BT.2020-1", new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f }, ILLUMINANT_D65, - x -> rcpResponse(x, 1 / 0.45, 1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145), - x -> response(x, 1 / 0.45, 1 / 1.0993, 0.099 / 1.0993, 1 / 4.5, 0.08145), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45), Named.BT2020.ordinal() ); sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb( "SMPTE RP 431-2-2007 DCI (P3)", new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f }, new float[] { 0.314f, 0.351f }, - x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.6), - x -> Math.pow(x < 0.0f ? 0.0f : x, 2.6), + 2.6, 0.0f, 1.0f, Named.DCI_P3.ordinal() ); @@ -1460,35 +1482,28 @@ public abstract class ColorSpace { "Display P3", new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f }, ILLUMINANT_D65, - x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), - x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4), Named.DISPLAY_P3.ordinal() ); sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb( "NTSC (1953)", NTSC_1953_PRIMARIES, ILLUMINANT_C, - x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45), Named.NTSC_1953.ordinal() ); sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb( "SMPTE-C RGB", new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f }, ILLUMINANT_D65, - x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081), - 0.0f, 1.0f, + new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45), Named.SMPTE_C.ordinal() ); sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb( "Adobe RGB (1998)", new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f }, ILLUMINANT_D65, - x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.2), - x -> Math.pow(x < 0.0f ? 0.0f : x, 2.2), + 2.2, 0.0f, 1.0f, Named.ADOBE_RGB.ordinal() ); @@ -1496,17 +1511,14 @@ public abstract class ColorSpace { "ROMM RGB ISO 22028-2:2013", new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f }, ILLUMINANT_D50, - x -> rcpResponse(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248), - x -> response(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248), - 0.0f, 1.0f, + new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8), Named.PRO_PHOTO_RGB.ordinal() ); sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb( "SMPTE ST 2065-1:2012 ACES", new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f }, ILLUMINANT_D60, - DoubleUnaryOperator.identity(), - DoubleUnaryOperator.identity(), + 1.0, -65504.0f, 65504.0f, Named.ACES.ordinal() ); @@ -1514,8 +1526,7 @@ public abstract class ColorSpace { "Academy S-2014-004 ACEScg", new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f }, ILLUMINANT_D60, - DoubleUnaryOperator.identity(), - DoubleUnaryOperator.identity(), + 1.0, -65504.0f, 65504.0f, Named.ACESCG.ordinal() ); @@ -1530,27 +1541,61 @@ public abstract class ColorSpace { } // Reciprocal piecewise gamma response - private static double rcpResponse(double x, double g,double a, double b, double c, double d) { + private static double rcpResponse(double x, double a, double b, double c, double d, double g) { return x >= d * c ? (Math.pow(x, 1.0 / g) - b) / a : x / c; } // Piecewise gamma response - private static double response(double x, double g, double a, double b, double c, double d) { + private static double response(double x, double a, double b, double c, double d, double g) { return x >= d ? Math.pow(a * x + b, g) : c * x; } + // Reciprocal piecewise gamma response + private static double rcpResponse(double x, double a, double b, double c, double d, + double e, double f, double g) { + return x >= d * c ? (Math.pow(x - e, 1.0 / g) - b) / a : (x - f) / c; + } + + // Piecewise gamma response + private static double response(double x, double a, double b, double c, double d, + double e, double f, double g) { + return x >= d ? Math.pow(a * x + b, g) + e : c * x + f; + } + // Reciprocal piecewise gamma response, encoded as sign(x).f(abs(x)) for color // spaces that allow negative values @SuppressWarnings("SameParameterValue") - private static double absRcpResponse(double x, double g, double a, double b, double c, double d) { - return Math.copySign(rcpResponse(x < 0.0 ? -x : x, g, a, b, c, d), x); + private static double absRcpResponse(double x, double a, double b, double c, double d, double g) { + return Math.copySign(rcpResponse(x < 0.0 ? -x : x, a, b, c, d, g), x); } // Piecewise gamma response, encoded as sign(x).f(abs(x)) for color spaces that // allow negative values @SuppressWarnings("SameParameterValue") - private static double absResponse(double x, double g, double a, double b, double c, double d) { - return Math.copySign(response(x < 0.0 ? -x : x, g, a, b, c, d), x); + private static double absResponse(double x, double a, double b, double c, double d, double g) { + return Math.copySign(response(x < 0.0 ? -x : x, a, b, c, d, g), x); + } + + /** + * Compares two sets of parametric transfer functions parameters with a precision of 1e-3. + * + * @param a The first set of parameters to compare + * @param b The second set of parameters to compare + * @return True if the two sets are equal, false otherwise + */ + private static boolean compare( + @Nullable Rgb.TransferParameters a, + @Nullable Rgb.TransferParameters b) { + //noinspection SimplifiableIfStatement + if (a == null && b == null) return true; + return a != null && b != null && + Math.abs(a.a - b.a) < 1e-3 && + Math.abs(a.b - b.b) < 1e-3 && + Math.abs(a.c - b.c) < 1e-3 && + Math.abs(a.d - b.d) < 1e-3 && + Math.abs(a.e - b.e) < 1e-3 && + Math.abs(a.f - b.f) < 1e-3 && + Math.abs(a.g - b.g) < 1e-3; } /** @@ -1710,7 +1755,7 @@ public abstract class ColorSpace { * <p>Computes the chromatic adaptation transform from the specified * source white point to the specified destination white point.</p> * - * <p>The transform is computed using the von Kris method, described + * <p>The transform is computed using the von Kries method, described * in more details in the documentation of {@link Adaptation}. The * {@link Adaptation} enum provides different matrices that can be * used to perform the adaptation.</p> @@ -1925,6 +1970,11 @@ public abstract class ColorSpace { * * $$RGB_{out} = OETF(f(EOTF(RGB_{in})))$$ * + * <p>If the transfer functions of the color space can be expressed as an + * ICC parametric curve as defined in ICC.1:2004-10, the numeric parameters + * can be retrieved by calling {@link #getTransferParameters()}. This can + * be useful to match color spaces for instance.</p> + * * <p class="note">Some RGB color spaces, such as {@link Named#ACES} and * {@link Named#LINEAR_EXTENDED_SRGB scRGB}, are said to be linear because * their transfer functions are the identity function: \(f(x) = x\). @@ -1967,14 +2017,175 @@ public abstract class ColorSpace { */ @AnyThread public static class Rgb extends ColorSpace { + /** + * {@usesMathJax} + * + * <p>Defines the parameters for the ICC parametric curve type 4, as + * defined in ICC.1:2004-10, section 10.15.</p> + * + * <p>The EOTF is of the form:</p> + * + * \(\begin{equation} + * Y = \begin{cases}c X + f & X \lt d \\ + * \left( a X + b \right) ^{g} + e & X \ge d \end{cases} + * \end{equation}\) + * + * <p>The corresponding OETF is simply the inverse function.</p> + * + * <p>The parameters defined by this class form a valid transfer + * function only if all the following conditions are met:</p> + * <ul> + * <li>No parameter is a {@link Double#isNaN(double) Not-a-Number}</li> + * <li>\(d\) is in the range \([0..1]\)</li> + * <li>The function is not constant</li> + * <li>The function is positive and increasing</li> + * </ul> + */ + public static class TransferParameters { + /** Variable \(a\) in the equation of the EOTF described above. */ + public final double a; + /** Variable \(b\) in the equation of the EOTF described above. */ + public final double b; + /** Variable \(c\) in the equation of the EOTF described above. */ + public final double c; + /** Variable \(d\) in the equation of the EOTF described above. */ + public final double d; + /** Variable \(e\) in the equation of the EOTF described above. */ + public final double e; + /** Variable \(f\) in the equation of the EOTF described above. */ + public final double f; + /** Variable \(g\) in the equation of the EOTF described above. */ + public final double g; + + /** + * <p>Defines the parameters for the ICC parametric curve type 3, as + * defined in ICC.1:2004-10, section 10.15.</p> + * + * <p>The EOTF is of the form:</p> + * + * \(\begin{equation} + * Y = \begin{cases}c X & X \lt d \\ + * \left( a X + b \right) ^{g} & X \ge d \end{cases} + * \end{equation}\) + * + * <p>This constructor is equivalent to setting \(e\) and \(f\) to 0.</p> + * + * @param a The value of \(a\) in the equation of the EOTF described above + * @param b The value of \(b\) in the equation of the EOTF described above + * @param c The value of \(c\) in the equation of the EOTF described above + * @param d The value of \(d\) in the equation of the EOTF described above + * @param g The value of \(g\) in the equation of the EOTF described above + * + * @throws IllegalArgumentException If the parameters form an invalid transfer function + */ + public TransferParameters(double a, double b, double c, double d, double g) { + this(a, b, c, d, 0.0, 0.0, g); + } + + /** + * <p>Defines the parameters for the ICC parametric curve type 4, as + * defined in ICC.1:2004-10, section 10.15.</p> + * + * @param a The value of \(a\) in the equation of the EOTF described above + * @param b The value of \(b\) in the equation of the EOTF described above + * @param c The value of \(c\) in the equation of the EOTF described above + * @param d The value of \(d\) in the equation of the EOTF described above + * @param e The value of \(e\) in the equation of the EOTF described above + * @param f The value of \(f\) in the equation of the EOTF described above + * @param g The value of \(g\) in the equation of the EOTF described above + * + * @throws IllegalArgumentException If the parameters form an invalid transfer function + */ + public TransferParameters(double a, double b, double c, double d, double e, + double f, double g) { + + if (Double.isNaN(a) || Double.isNaN(b) || Double.isNaN(c) || + Double.isNaN(d) || Double.isNaN(e) || Double.isNaN(f) || + Double.isNaN(g)) { + throw new IllegalArgumentException("Parameters cannot be NaN"); + } + + if (!(d >= 0.0 && d <= 1.0 + Math.ulp(1.0))) { + throw new IllegalArgumentException("Parameter d must be in the range [0..1]"); + } + + if (d == 0.0 && (a == 0.0 || g == 0.0)) { + throw new IllegalArgumentException( + "Parameter a or g is zero, the transfer function is constant"); + } + + if (d >= 1.0 && c == 0.0) { + throw new IllegalArgumentException( + "Parameter c is zero, the transfer function is constant"); + } + + if ((a == 0.0 || g == 0.0) && c == 0.0) { + throw new IllegalArgumentException("Parameter a or g is zero," + + " and c is zero, the transfer function is constant"); + } + + if (c < 0.0) { + throw new IllegalArgumentException("The transfer function must be increasing"); + } + + if (a < 0.0 || g < 0.0) { + throw new IllegalArgumentException("The transfer function must be " + + "positive or increasing"); + } + + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.g = g; + } + + @SuppressWarnings("SimplifiableIfStatement") + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TransferParameters that = (TransferParameters) o; + + if (Double.compare(that.a, a) != 0) return false; + if (Double.compare(that.b, b) != 0) return false; + if (Double.compare(that.c, c) != 0) return false; + if (Double.compare(that.d, d) != 0) return false; + if (Double.compare(that.e, e) != 0) return false; + if (Double.compare(that.f, f) != 0) return false; + return Double.compare(that.g, g) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(a); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(b); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(c); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(d); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(e); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(f); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(g); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + } + @NonNull private final float[] mWhitePoint; @NonNull private final float[] mPrimaries; @NonNull private final float[] mTransform; @NonNull private final float[] mInverseTransform; - @NonNull private final boolean mIsWideGamut; - @NonNull private final boolean mIsSrgb; - @NonNull private final DoubleUnaryOperator mOetf; @NonNull private final DoubleUnaryOperator mEotf; @NonNull private final DoubleUnaryOperator mClampedOetf; @@ -1983,6 +2194,11 @@ public abstract class ColorSpace { private final float mMin; private final float mMax; + private final boolean mIsWideGamut; + private final boolean mIsSrgb; + + @Nullable private TransferParameters mTransferParameters; + /** * <p>Creates a new RGB color space using a 3x3 column-major transform matrix. * The transform matrix must convert from the RGB space to the profile connection @@ -2010,7 +2226,7 @@ public abstract class ColorSpace { @NonNull @Size(9) float[] toXYZ, @NonNull DoubleUnaryOperator oetf, @NonNull DoubleUnaryOperator eotf) { - this(name, computePrimaries(toXYZ, eotf), computeWhitePoint(toXYZ, eotf), + this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), oetf, eotf, 0.0f, 1.0f, MIN_ID); } @@ -2065,6 +2281,251 @@ public abstract class ColorSpace { } /** + * <p>Creates a new RGB color space using a 3x3 column-major transform matrix. + * The transform matrix must convert from the RGB space to the profile connection + * space CIE XYZ.</p> + * + * <p class="note">The range of the color space is imposed to be \([0..1]\).</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param toXYZ 3x3 column-major transform matrix from RGB to the profile + * connection space CIE XYZ as an array of 9 floats, cannot be null + * @param function Parameters for the transfer functions + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>Gamma is negative.</li> + * </ul> + * + * @see #get(Named) + */ + public Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(9) float[] toXYZ, + @NonNull TransferParameters function) { + this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), function, MIN_ID); + } + + /** + * <p>Creates a new RGB color space using a specified set of primaries + * and a specified white point.</p> + * + * <p>The primaries and white point can be specified in the CIE xyY space + * or in CIE XYZ. The length of the arrays depends on the chosen space:</p> + * + * <table summary="Parameters length"> + * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr> + * <tr><td>xyY</td><td>6</td><td>2</td></tr> + * <tr><td>XYZ</td><td>9</td><td>3</td></tr> + * </table> + * + * <p>When the primaries and/or white point are specified in xyY, the Y component + * does not need to be specified and is assumed to be 1.0. Only the xy components + * are required.</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats + * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats + * @param function Parameters for the transfer functions + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>The primaries array is null or has a length that is neither 6 or 9.</li> + * <li>The white point array is null or has a length that is neither 2 or 3.</li> + * <li>The transfer parameters are invalid.</li> + * </ul> + * + * @see #get(Named) + */ + public Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(min = 6, max = 9) float[] primaries, + @NonNull @Size(min = 2, max = 3) float[] whitePoint, + @NonNull TransferParameters function) { + this(name, primaries, whitePoint, function, MIN_ID); + } + + /** + * <p>Creates a new RGB color space using a specified set of primaries + * and a specified white point.</p> + * + * <p>The primaries and white point can be specified in the CIE xyY space + * or in CIE XYZ. The length of the arrays depends on the chosen space:</p> + * + * <table summary="Parameters length"> + * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr> + * <tr><td>xyY</td><td>6</td><td>2</td></tr> + * <tr><td>XYZ</td><td>9</td><td>3</td></tr> + * </table> + * + * <p>When the primaries and/or white point are specified in xyY, the Y component + * does not need to be specified and is assumed to be 1.0. Only the xy components + * are required.</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats + * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats + * @param function Parameters for the transfer functions + * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID} + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>The primaries array is null or has a length that is neither 6 or 9.</li> + * <li>The white point array is null or has a length that is neither 2 or 3.</li> + * <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li> + * <li>The transfer parameters are invalid.</li> + * </ul> + * + * @see #get(Named) + */ + private Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(min = 6, max = 9) float[] primaries, + @NonNull @Size(min = 2, max = 3) float[] whitePoint, + @NonNull TransferParameters function, + @IntRange(from = MIN_ID, to = MAX_ID) int id) { + this(name, primaries, whitePoint, + function.e == 0.0 && function.f == 0.0 ? + x -> rcpResponse(x, function.a, function.b, + function.c, function.d, function.g) : + x -> rcpResponse(x, function.a, function.b, function.c, + function.d, function.e, function.f, function.g), + function.e == 0.0 && function.f == 0.0 ? + x -> response(x, function.a, function.b, + function.c, function.d, function.g) : + x -> response(x, function.a, function.b, function.c, + function.d, function.e, function.f, function.g), + 0.0f, 1.0f, id); + mTransferParameters = function; + } + + /** + * <p>Creates a new RGB color space using a 3x3 column-major transform matrix. + * The transform matrix must convert from the RGB space to the profile connection + * space CIE XYZ.</p> + * + * <p class="note">The range of the color space is imposed to be \([0..1]\).</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param toXYZ 3x3 column-major transform matrix from RGB to the profile + * connection space CIE XYZ as an array of 9 floats, cannot be null + * @param gamma Gamma to use as the transfer function + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>Gamma is negative.</li> + * </ul> + * + * @see #get(Named) + */ + public Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(9) float[] toXYZ, + double gamma) { + this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), gamma, 0.0f, 1.0f, MIN_ID); + } + + /** + * <p>Creates a new RGB color space using a specified set of primaries + * and a specified white point.</p> + * + * <p>The primaries and white point can be specified in the CIE xyY space + * or in CIE XYZ. The length of the arrays depends on the chosen space:</p> + * + * <table summary="Parameters length"> + * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr> + * <tr><td>xyY</td><td>6</td><td>2</td></tr> + * <tr><td>XYZ</td><td>9</td><td>3</td></tr> + * </table> + * + * <p>When the primaries and/or white point are specified in xyY, the Y component + * does not need to be specified and is assumed to be 1.0. Only the xy components + * are required.</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats + * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats + * @param gamma Gamma to use as the transfer function + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>The primaries array is null or has a length that is neither 6 or 9.</li> + * <li>The white point array is null or has a length that is neither 2 or 3.</li> + * <li>Gamma is negative.</li> + * </ul> + * + * @see #get(Named) + */ + public Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(min = 6, max = 9) float[] primaries, + @NonNull @Size(min = 2, max = 3) float[] whitePoint, + double gamma) { + this(name, primaries, whitePoint, gamma, 0.0f, 1.0f, MIN_ID); + } + + /** + * <p>Creates a new RGB color space using a specified set of primaries + * and a specified white point.</p> + * + * <p>The primaries and white point can be specified in the CIE xyY space + * or in CIE XYZ. The length of the arrays depends on the chosen space:</p> + * + * <table summary="Parameters length"> + * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr> + * <tr><td>xyY</td><td>6</td><td>2</td></tr> + * <tr><td>XYZ</td><td>9</td><td>3</td></tr> + * </table> + * + * <p>When the primaries and/or white point are specified in xyY, the Y component + * does not need to be specified and is assumed to be 1.0. Only the xy components + * are required.</p> + * + * @param name Name of the color space, cannot be null, its length must be >= 1 + * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats + * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats + * @param gamma Gamma to use as the transfer function + * @param min The minimum valid value in this color space's RGB range + * @param max The maximum valid value in this color space's RGB range + * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID} + * + * @throws IllegalArgumentException If any of the following conditions is met: + * <ul> + * <li>The name is null or has a length of 0.</li> + * <li>The primaries array is null or has a length that is neither 6 or 9.</li> + * <li>The white point array is null or has a length that is neither 2 or 3.</li> + * <li>The minimum valid value is >= the maximum valid value.</li> + * <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li> + * <li>Gamma is negative.</li> + * </ul> + * + * @see #get(Named) + */ + private Rgb( + @NonNull @Size(min = 1) String name, + @NonNull @Size(min = 6, max = 9) float[] primaries, + @NonNull @Size(min = 2, max = 3) float[] whitePoint, + double gamma, + float min, + float max, + @IntRange(from = MIN_ID, to = MAX_ID) int id) { + this(name, primaries, whitePoint, + gamma == 1.0 ? DoubleUnaryOperator.identity() : + x -> Math.pow(x < 0.0 ? 0.0 : x, 1 / gamma), + gamma == 1.0 ? DoubleUnaryOperator.identity() : + x -> Math.pow(x < 0.0 ? 0.0 : x, gamma), + min, max, id); + mTransferParameters = gamma == 1.0 ? + new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0), gamma) : + new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma); + } + + /** * <p>Creates a new RGB color space using a specified set of primaries * and a specified white point.</p> * @@ -2183,6 +2644,8 @@ public abstract class ColorSpace { mIsWideGamut = colorSpace.mIsWideGamut; mIsSrgb = colorSpace.mIsSrgb; + + mTransferParameters = colorSpace.mTransferParameters; } /** @@ -2360,6 +2823,7 @@ public abstract class ColorSpace { * @return A transfer function that converts from linear space to "gamma space" * * @see #getEotf() + * @see #getTransferParameters() */ @NonNull public DoubleUnaryOperator getOetf() { @@ -2383,12 +2847,31 @@ public abstract class ColorSpace { * @return A transfer function that converts from "gamma space" to linear space * * @see #getOetf() + * @see #getTransferParameters() */ @NonNull public DoubleUnaryOperator getEotf() { return mClampedEotf; } + /** + * <p>Returns the parameters used by the {@link #getEotf() electro-optical} + * and {@link #getOetf() opto-electronic} transfer functions. If the transfer + * functions do not match the ICC parametric curves defined in ICC.1:2004-10 + * (section 10.15), this method returns null.</p> + * + * <p>See {@link TransferParameters} for a full description of the transfer + * functions.</p> + * + * @return An instance of {@link TransferParameters} or null if this color + * space's transfer functions do not match the equation defined in + * {@link TransferParameters} + */ + @Nullable + public TransferParameters getTransferParameters() { + return mTransferParameters; + } + @Override public boolean isSrgb() { return mIsSrgb; @@ -2544,6 +3027,11 @@ public abstract class ColorSpace { if (Float.compare(rgb.mMax, mMax) != 0) return false; if (!Arrays.equals(mWhitePoint, rgb.mWhitePoint)) return false; if (!Arrays.equals(mPrimaries, rgb.mPrimaries)) return false; + if (mTransferParameters != null) { + return mTransferParameters.equals(rgb.mTransferParameters); + } else if (rgb.mTransferParameters == null) { + return true; + } //noinspection SimplifiableIfStatement if (!mOetf.equals(rgb.mOetf)) return false; return mEotf.equals(rgb.mEotf); @@ -2554,10 +3042,14 @@ public abstract class ColorSpace { int result = super.hashCode(); result = 31 * result + Arrays.hashCode(mWhitePoint); result = 31 * result + Arrays.hashCode(mPrimaries); - result = 31 * result + mOetf.hashCode(); - result = 31 * result + mEotf.hashCode(); result = 31 * result + (mMin != +0.0f ? Float.floatToIntBits(mMin) : 0); result = 31 * result + (mMax != +0.0f ? Float.floatToIntBits(mMax) : 0); + result = 31 * result + + (mTransferParameters != null ? mTransferParameters.hashCode() : 0); + if (mTransferParameters == null) { + result = 31 * result + mOetf.hashCode(); + result = 31 * result + mEotf.hashCode(); + } return result; } @@ -2746,18 +3238,15 @@ public abstract class ColorSpace { * range of the color space is [0..1]. * * @param toXYZ The color space's 3x3 transform matrix to XYZ - * @param EOTF The color space's electro-optical transfer function * @return A new array of 6 floats containing the color space's * primaries in CIE xyY */ @NonNull @Size(6) - private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ, - DoubleUnaryOperator EOTF) { - float one = (float) EOTF.applyAsDouble(1.0); - float[] r = mul3x3Float3(toXYZ, new float[] { one, 0.0f, 0.0f }); - float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, one, 0.0f }); - float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, one }); + private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ) { + float[] r = mul3x3Float3(toXYZ, new float[] { 1.0f, 0.0f, 0.0f }); + float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, 1.0f, 0.0f }); + float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, 1.0f }); float rSum = r[0] + r[1] + r[2]; float gSum = g[0] + g[1] + g[2]; @@ -2776,16 +3265,13 @@ public abstract class ColorSpace { * range of the color space is [0..1]. * * @param toXYZ The color space's 3x3 transform matrix to XYZ - * @param EOTF The color space's electro-optical transfer function * @return A new array of 2 floats containing the color space's * white point in CIE xyY */ @NonNull @Size(2) - private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ, - @NonNull DoubleUnaryOperator EOTF) { - float one = (float) EOTF.applyAsDouble(1.0); - float[] w = mul3x3Float3(toXYZ, new float[] { one, one, one }); + private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ) { + float[] w = mul3x3Float3(toXYZ, new float[] { 1.0f, 1.0f, 1.0f }); float sum = w[0] + w[1] + w[2]; return new float[] { w[0] / sum, w[1] / sum }; } @@ -2988,6 +3474,7 @@ public abstract class ColorSpace { * Computes an extra transform to apply in XYZ space depending on the * selected rendering intent. */ + @Nullable private static float[] computeTransform(@NonNull ColorSpace source, @NonNull ColorSpace destination, @NonNull RenderIntent intent) { if (intent != RenderIntent.ABSOLUTE) return null; diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 7c2e78c7d3b8..19063e3768cd 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -133,7 +133,7 @@ public: * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA */ constexpr GLint rgbaInternalFormat(bool needSRGB = true) const { - return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + return extensions().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; } /** diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 00238a25ebde..1e71cb081b39 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -20,6 +20,8 @@ #include "Properties.h" #include "utils/StringUtils.h" +#include <cutils/compiler.h> + #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -28,14 +30,6 @@ namespace android { namespace uirenderer { -// Debug -#if DEBUG_EXTENSIONS - #define EXT_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define EXT_LOGD(...) -#endif - - Extensions::Extensions() { const char* version = (const char*) glGetString(GL_VERSION); @@ -66,17 +60,18 @@ Extensions::Extensions() { mHas4BitStencil = extensions.has("GL_OES_stencil4"); mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); -#ifdef ANDROID_ENABLE_LINEAR_BLENDING mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB"); mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control"); +#ifdef ANDROID_ENABLE_LINEAR_BLENDING // If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB) // and EXT_sRGB_write_control LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB"); LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control"); + + mHasLinearBlending = true; #else - mHasSRGB = false; - mHasSRGBWriteControl = false; + mHasLinearBlending = false; #endif } diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 2c38507bd79a..0ecfdb1b3e0a 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -17,11 +17,6 @@ #ifndef ANDROID_HWUI_EXTENSIONS_H #define ANDROID_HWUI_EXTENSIONS_H -#include <cutils/compiler.h> - -#include <string> -#include <unordered_set> - namespace android { namespace uirenderer { @@ -45,6 +40,7 @@ public: inline bool hasFloatTextures() const { return mVersionMajor >= 3; } inline bool hasSRGB() const { return mHasSRGB; } inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; } + inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; } inline int getMajorGlVersion() const { return mVersionMajor; } inline int getMinorGlVersion() const { return mVersionMinor; } @@ -59,6 +55,7 @@ private: bool mHasUnpackSubImage; bool mHasSRGB; bool mHasSRGBWriteControl; + bool mHasLinearBlending; int mVersionMajor; int mVersionMinor; diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 71bee93fc4c7..18bfcc2bbddf 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -68,7 +68,7 @@ GradientCache::GradientCache(Extensions& extensions) , mMaxSize(Properties::gradientCacheSize) , mUseFloatTexture(extensions.hasFloatTextures()) , mHasNpot(extensions.hasNPot()) - , mHasSRGB(extensions.hasSRGB()) { + , mHasLinearBlending(extensions.hasLinearBlending()) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); @@ -263,7 +263,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, if (mUseFloatTexture) { texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels); } else { - GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + GLint internalFormat = mHasLinearBlending ? GL_SRGB8_ALPHA8 : GL_RGBA; texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 5e35435ed64c..f299a40e994f 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -170,7 +170,7 @@ private: GLint mMaxTextureSize; bool mUseFloatTexture; bool mHasNpot; - bool mHasSRGB; + bool mHasLinearBlending; mutable Mutex mLock; }; // class GradientCache diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index ca056487a3ba..40ab7788f218 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -418,7 +418,7 @@ const char* gBlendOps[18] = { ProgramCache::ProgramCache(Extensions& extensions) : mHasES3(extensions.getMajorGlVersion() >= 3) - , mHasSRGB(extensions.hasSRGB()) { + , mHasLinearBlending(extensions.hasLinearBlending()) { } ProgramCache::~ProgramCache() { @@ -642,11 +642,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) && !description.hasAlpha8Texture)) { - shader.append(gFS_OETF[description.hasLinearTexture && !mHasSRGB]); + shader.append(gFS_OETF[description.hasLinearTexture && !mHasLinearBlending]); } if (description.hasGradient) { shader.append(gFS_Gradient_Functions); - shader.append(gFS_Gradient_Preamble[mHasSRGB]); + shader.append(gFS_Gradient_Preamble[mHasLinearBlending]); } // Begin the shader diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index c2f715de70c3..cedd854bb48b 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -59,7 +59,7 @@ private: std::map<programid, std::unique_ptr<Program>> mCache; const bool mHasES3; - const bool mHasSRGB; + const bool mHasLinearBlending; }; // class ProgramCache }; // namespace uirenderer diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 0dbd7674e2aa..cfc2744e61b2 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -241,21 +241,23 @@ void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType color } } -SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) { +SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp<SkColorSpace> sRGB) { SkBitmap rgbaBitmap; rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr)); + bitmap.info().alphaType(), hasLinearBlending ? sRGB : nullptr)); rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); return rgbaBitmap; } -bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) { +bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending, + SkColorSpace* sRGB) { bool needSRGB = info.colorSpace() == sRGB; return info.colorType() == kARGB_4444_SkColorType || info.colorType() == kIndex_8_SkColorType - || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB); + || (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && needSRGB); } @@ -295,11 +297,11 @@ void Texture::upload(Bitmap& bitmap) { mCaches.textureState().bindTexture(mTarget, mId); // TODO: Handle sRGB gray bitmaps - bool hasSRGB = mCaches.extensions().hasSRGB(); - if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) { + bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); + if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending, sRGB.get()))) { SkBitmap skBitmap; bitmap.getSkBitmap(&skBitmap); - SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB)); + SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), rgbaBitmap.getPixels()); diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index ce9d4dc234a8..e7fbf20cd898 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -40,8 +40,10 @@ class Layer; */ class Texture : public GpuMemoryTracker { public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB); - static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB); + static SkBitmap uploadToN32(const SkBitmap& bitmap, + bool hasLinearBlending, sk_sp<SkColorSpace> sRGB); + static bool hasUnsupportedColorType(const SkImageInfo& info, + bool hasLinearBlending, SkColorSpace* sRGB); static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index a55b061818a3..db4ff39aed83 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -19,6 +19,8 @@ #include <SkBitmap.h> +#include <cutils/compiler.h> + #include <utils/LruCache.h> #include <utils/Mutex.h> diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index f9730c9ca273..49b69eb59741 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -234,7 +234,7 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(); bool needSRGB = skBitmap.info().colorSpace() == sRGB.get(); - bool hasSRGB = caches.extensions().hasSRGB(); + bool hasLinearBlending = caches.extensions().hasLinearBlending(); GLint format, type, internalFormat; uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(), needSRGB, &internalFormat, &format, &type); @@ -252,8 +252,8 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr SkBitmap bitmap; if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), - hasSRGB, sRGB.get()))) { - bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB)); + hasLinearBlending, sRGB.get()))) { + bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); } else { bitmap = skBitmap; } diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 7dfc2ee4fbe5..8bce990129de 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -174,13 +174,15 @@ void RenderState::interruptForFunctorInvoke() { meshState().disableTexCoordsVertexArray(); debugOverdraw(false, false); // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasSRGBWriteControl()) { + if (mCaches->extensions().hasLinearBlending() && + mCaches->extensions().hasSRGBWriteControl()) { glDisable(GL_FRAMEBUFFER_SRGB_EXT); } } void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasSRGBWriteControl()) { + if (mCaches->extensions().hasLinearBlending() && + mCaches->extensions().hasSRGBWriteControl()) { glEnable(GL_FRAMEBUFFER_SRGB_EXT); } diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 0950eb8e7134..4a27ca2f327a 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -121,4 +121,4 @@ namespace uirenderer { } /* namespace uirenderer */ } /* namespace android */ -#endif /* TEST_UTILS_H */ +#endif /* COLOR_H */ |