From 53b03c0f44b95f49a08358ec60c79d4ee73b840f Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 11 May 2020 16:28:39 -0400 Subject: [PATCH] Clean up ColorSpace.java Bug: 156282935 Test: I3ce1bbd4285443a4e481a0a1e9267feafbe49d44 Test: DisplayWhiteBalanceTintControllerTest Remove cctToIlluminantdXyz, which was @hide, but unused. Make the @hide constructor package-protected so it can be called by ParcelableColorSpace, but not from outside the package. Make cctToXyz and chromaticAdaptation public. These are used by other parts of the system and may be useful to other clients. Make the public version of chromaticAdaptation throw IllegalArgumentExceptions for passing arrays with the wrong number of arguments. Make mul3x3(float[], float[]) private and copy it over to DisplayWhiteBalanceTintController. There is nothing ColorSpace-specific about this code. Remove Renderer and related. They were originally used to generate the documentation pages, but they are no longer needed. Remove ColorSpaceRenderTest, which tested it. Change-Id: I9094046c8e5e72be0ac208d4d242e6f0b274f386 --- api/current.txt | 2 + .../graphics/ColorSpaceRendererTest.java | 75 -- .../java/android/graphics/ColorSpace.java | 856 +----------------- non-updatable-api/current.txt | 2 + .../DisplayWhiteBalanceTintController.java | 31 +- 5 files changed, 38 insertions(+), 928 deletions(-) delete mode 100644 core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java diff --git a/api/current.txt b/api/current.txt index 83e0f942c69eb..635c79ad7354f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14487,6 +14487,8 @@ package android.graphics { @AnyThread public abstract class ColorSpace { method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[], @NonNull android.graphics.ColorSpace.Adaptation); + method @NonNull @Size(3) public static float[] cctToXyz(@IntRange(from=1) int); + method @NonNull @Size(9) public static float[] chromaticAdaptation(@NonNull android.graphics.ColorSpace.Adaptation, @NonNull @Size(min=2, max=3) float[], @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace.RenderIntent); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace); diff --git a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java deleted file mode 100644 index 8e9b38cc5a1f1..0000000000000 --- a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ColorSpaceRendererTest { - - @Test - public void testRendererSize() { - Bitmap b = ColorSpace.createRenderer() - .size(0) - .render(); - assertEquals(128, b.getWidth()); - assertEquals(128, b.getHeight()); - - b = ColorSpace.createRenderer() - .size(768) - .render(); - assertEquals(768, b.getWidth()); - assertEquals(768, b.getHeight()); - } - - @Test - public void testRenderer() { - Bitmap b = ColorSpace.createRenderer() - .size(1024) - .clip(true) - .showWhitePoint(false) - .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff) - .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffffff) - .add(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), 0.1f, 0.5f, 0.1f, 0xff000000) - .add(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), 0.1f, 0.5f, 0.1f, 0xff000000) - .render(); - assertNotNull(b); - } - - @Test - public void testUcsRenderer() { - Bitmap b = ColorSpace.createRenderer() - .size(1024) - .clip(true) - .showWhitePoint(false) - .uniformChromaticityScale(true) - .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff) - .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffffff) - .add(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), 0.1f, 0.5f, 0.1f, 0xff000000) - .add(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), 0.1f, 0.5f, 0.1f, 0xff000000) - .render(); - assertNotNull(b); - } -} diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 1aeafa391b41a..2f978fc1fc2d0 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -17,19 +17,15 @@ package android.graphics; import android.annotation.AnyThread; -import android.annotation.ColorInt; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; import android.annotation.SuppressAutoDoc; -import android.util.Pair; import libcore.util.NativeAllocationRegistry; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.function.DoubleUnaryOperator; /** @@ -872,8 +868,7 @@ public abstract class ColorSpace { } } - /** @hide */ - ColorSpace( + /*package*/ ColorSpace( @NonNull String name, @NonNull Model model, @IntRange(from = MIN_ID, to = MAX_ID) int id) { @@ -1441,22 +1436,6 @@ public abstract class ColorSpace { return null; } - /** - *

Creates a new {@link Renderer} that can be used to visualize and - * debug color spaces. See the documentation of {@link Renderer} for - * more information.

- * - * @return A new non-null {@link Renderer} instance - * - * @see Renderer - * - * @hide - */ - @NonNull - public static Renderer createRenderer() { - return new Renderer(); - } - static { sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb( "sRGB IEC61966-2.1", @@ -1701,12 +1680,10 @@ public abstract class ColorSpace { * @param rhs 3x3 matrix, as a non-null array of 9 floats * @return A new array of 9 floats containing the result of the multiplication * of rhs by lhs - * - * @hide */ @NonNull @Size(9) - public static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) { + private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) { float[] r = new float[9]; r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2]; r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2]; @@ -1774,28 +1751,6 @@ public abstract class ColorSpace { return new float[] { xyY[0] / xyY[1], 1.0f, (1 - xyY[0] - xyY[1]) / xyY[1] }; } - /** - * 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; - } - } - /** *

Computes the chromatic adaptation transform from the specified * source white point to the specified destination white point.

@@ -1834,8 +1789,6 @@ public abstract class ColorSpace { * @param cct The correlated color temperature, in Kelvin * @return Corresponding XYZ values * @throws IllegalArgumentException If cct is invalid - * - * @hide */ @NonNull @Size(3) @@ -1860,38 +1813,6 @@ public abstract class ColorSpace { return xyYToXyz(new float[] {x, y}); } - /** - *

Computes the chromaticity coordinates of a CIE series D illuminant - * from the specified correlated color temperature (CCT). The specified CCT - * must be greater than 0. A meaningful CCT range is [4000, 25000].

- * - *

The transform is computed using the methods referred to in Kang et - * al., Design of Advanced Color - Temperature Control System for HDTV - * Applications, Journal of Korean Physical Society 41, 865-871 - * (2002).

- * - * @param cct The correlated color temperature, in Kelvin - * @return Corresponding XYZ values - * @throws IllegalArgumentException If cct is invalid - * - * @hide - */ - @NonNull - @Size(3) - public static float[] cctToIlluminantdXyz(@IntRange(from = 1) int cct) { - if (cct < 1) { - throw new IllegalArgumentException("Temperature must be greater than 0"); - } - - final float icct = 1.0f / cct; - final float icct2 = icct * icct; - final float x = cct <= 7000.0f ? - 0.244063f + 0.09911e3f * icct + 2.9678e6f * icct2 - 4.6070e9f * icct2 * icct : - 0.237040f + 0.24748e3f * icct + 1.9018e6f * icct2 - 2.0064e9f * icct2 * icct; - final float y = -3.0f * x * x + 2.87f * x - 0.275f; - return xyYToXyz(new float[] {x, y}); - } - /** *

Computes the chromatic adaptation transform from the specified * source white point to the specified destination white point.

@@ -1905,14 +1826,16 @@ public abstract class ColorSpace { * @param srcWhitePoint The white point to adapt from * @param dstWhitePoint The white point to adapt to * @return A 3x3 matrix as a non-null array of 9 floats - * - * @hide */ @NonNull @Size(9) public static float[] chromaticAdaptation(@NonNull Adaptation adaptation, @NonNull @Size(min = 2, max = 3) float[] srcWhitePoint, @NonNull @Size(min = 2, max = 3) float[] dstWhitePoint) { + if ((srcWhitePoint.length != 2 && srcWhitePoint.length != 3) + || (dstWhitePoint.length != 2 && dstWhitePoint.length != 3)) { + throw new IllegalArgumentException("A white point array must have 2 or 3 floats"); + } float[] srcXyz = srcWhitePoint.length == 3 ? Arrays.copyOf(srcWhitePoint, 3) : xyYToXyz(srcWhitePoint); float[] dstXyz = dstWhitePoint.length == 3 ? @@ -3925,771 +3848,4 @@ public abstract class ColorSpace { }; } } - - /** - *

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 or a CIE 1976 UCS chromaticity diagram.

- * - *

The following code snippet shows how to compare the {@link Named#SRGB} - * and {@link Named#DCI_P3} color spaces in a CIE 1931 diagram:

- * - *
-     * Bitmap bitmap = ColorSpace.createRenderer()
-     *     .size(768)
-     *     .clip(true)
-     *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-     *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-     *     .render();
-     * 
- *

- * - *

sRGB vs DCI-P3
- *

- * - *

A renderer can also be used to show the location of specific colors, - * associated with a color space, in the CIE 1931 xyY chromaticity diagram. - * See {@link #add(ColorSpace, float, float, float, int)} for more information.

- * - * @see ColorSpace#createRenderer() - * - * @hide - */ - 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> mColorSpaces = new ArrayList<>(2); - private final List mPoints = new ArrayList<>(0); - - private Renderer() { - } - - /** - *

Defines whether the chromaticity diagram should be clipped by the first - * registered color space. The default value is false.

- * - *

The following code snippet and image show the default behavior:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-         *     .render();
-         * 
- *

- * - *

Clipping disabled
- *

- * - *

Here is the same example with clipping enabled:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .clip(true)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-         *     .render();
-         * 
- *

- * - *

Clipping enabled
- *

- * - * @param clip True to clip the chromaticity diagram to the first registered color space, - * false otherwise - * @return This instance of {@link Renderer} - */ - @NonNull - public Renderer clip(boolean clip) { - mClip = clip; - return this; - } - - /** - *

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.

- * - *

The following code snippet shows how to enable the uniform chromaticity - * scale. The image below shows the result:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .uniformChromaticityScale(true)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-         *     .render();
-         * 
- *

- * - *

CIE 1976 UCS diagram
- *

- * - * @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. - * - * @param size The size in pixels of the output bitmap - * @return This instance of {@link Renderer} - */ - @NonNull - public Renderer size(@IntRange(from = 128, to = Integer.MAX_VALUE) int size) { - mSize = Math.max(128, size); - return this; - } - - /** - * Shows or hides the white point of each color space in the output bitmap. - * The default is true. - * - * @param show True to show the white point of each color space, false - * otherwise - * @return This instance of {@link Renderer} - */ - @NonNull - public Renderer showWhitePoint(boolean show) { - mShowWhitePoint = show; - return this; - } - - /** - *

Adds a color space to represent on the output CIE 1931 chromaticity - * diagram. The color space is represented as a triangle showing the - * footprint of its color gamut and, optionally, the location of its - * white point.

- * - *

Color spaces with a color model that is not RGB are - * accepted but ignored.

- * - *

The following code snippet and image show an example of calling this - * method to compare {@link Named#SRGB sRGB} and {@link Named#DCI_P3 DCI-P3}:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-         *     .render();
-         * 
- *

- * - *

sRGB vs DCI-P3
- *

- * - *

Adding a color space extending beyond the boundaries of the - * spectral locus will alter the size of the diagram within the output - * bitmap as shown in this example:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
-         *     .add(ColorSpace.get(ColorSpace.Named.ACES), 0xff097ae9)
-         *     .add(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), 0xff000000)
-         *     .render();
-         * 
- *

- * - *

sRGB, DCI-P3, ACES and scRGB
- *

- * - * @param colorSpace The color space whose gamut to render on the diagram - * @param color The sRGB color to use to render the color space's gamut and white point - * @return This instance of {@link Renderer} - * - * @see #clip(boolean) - * @see #showWhitePoint(boolean) - */ - @NonNull - public Renderer add(@NonNull ColorSpace colorSpace, @ColorInt int color) { - mColorSpaces.add(new Pair<>(colorSpace, color)); - return this; - } - - /** - *

Adds a color to represent as a point on the chromaticity diagram. - * The color is associated with a color space which will be used to - * perform the conversion to CIE XYZ and compute the location of the point - * on the diagram. The point is rendered as a colored circle.

- * - *

The following code snippet and image show an example of calling this - * method to render the location of several sRGB colors as white circles:

- *
-         * Bitmap bitmap = ColorSpace.createRenderer()
-         *     .clip(true)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.0f, 0.1f, 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.1f, 0.1f, 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.2f, 0.1f, 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.3f, 0.1f, 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.4f, 0.1f, 0xffffffff)
-         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.5f, 0.1f, 0xffffffff)
-         *     .render();
-         * 
- *

- * - *

- * Locating colors on the chromaticity diagram - *
- *

- * - * @param colorSpace The color space of the color to locate on the diagram - * @param r The first component of the color to locate on the diagram - * @param g The second component of the color to locate on the diagram - * @param b The third component of the color to locate on the diagram - * @param pointColor The sRGB color to use to render the point on the diagram - * @return This instance of {@link Renderer} - */ - @NonNull - public Renderer add(@NonNull ColorSpace colorSpace, float r, float g, float b, - @ColorInt int pointColor) { - mPoints.add(new Point(colorSpace, new float[] { r, g, b }, pointColor)); - return this; - } - - /** - *

Renders the {@link #add(ColorSpace, int) color spaces} and - * {@link #add(ColorSpace, float, float, float, int) points} registered - * with this renderer. The output bitmap is an sRGB image with the - * dimensions specified by calling {@link #size(int)} (1204x1024px by - * default).

- * - * @return A new non-null {@link Bitmap} with the dimensions specified - * by {@link #size(int)} (1024x1024 by default) - */ - @NonNull - public Bitmap render() { - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - Bitmap bitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - - float[] primaries = new float[6]; - float[] whitePoint = new float[2]; - - int width = NATIVE_SIZE; - int height = NATIVE_SIZE; - - Path path = new Path(); - - 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); - - return bitmap; - } - - /** - * Draws registered points at their correct position in the xyY coordinates. - * Each point is positioned according to its associated color space. - * - * @param canvas The canvas to transform - * @param width Width in pixel of the final image - * @param height Height in pixel of the final image - * @param paint A pre-allocated paint used to avoid temporary allocations - */ - private void drawPoints(@NonNull Canvas canvas, int width, int height, - @NonNull Paint paint) { - - 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]; - v[2] = point.mRgb[2]; - point.mColorSpace.toXyz(v); - - paint.setColor(point.mColor); - - // XYZ to xyY, assuming Y=1.0, then to L*u*v* if needed - float sum = v[0] + v[1] + v[2]; - 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); - } - } - - /** - * Draws the color gamuts and white points of all the registered color - * spaces. Only color spaces with an RGB color model are rendered, the - * others are ignored. - * - * @param canvas The canvas to transform - * @param width Width in pixel of the final image - * @param height Height in pixel of the final image - * @param paint A pre-allocated paint used to avoid temporary allocations - * @param path A pre-allocated path used to avoid temporary allocations - * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations - * @param whitePoint A pre-allocated array of 2 floats to avoid temporary allocations - */ - private void drawGamuts( - @NonNull Canvas canvas, int width, int height, - @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 item : mColorSpaces) { - ColorSpace colorSpace = item.first; - int color = item.second; - - if (colorSpace.getModel() != Model.RGB) continue; - - Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries, mUcs); - - path.rewind(); - path.moveTo(width * primaries[0], height - height * primaries[1]); - path.lineTo(width * primaries[2], height - height * primaries[3]); - path.lineTo(width * primaries[4], height - height * primaries[5]); - path.close(); - - paint.setStyle(Paint.Style.STROKE); - paint.setColor(color); - canvas.drawPath(path, paint); - - // 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], radius, paint); - } - } - } - - /** - * Returns the primaries of the specified RGB color space. This method handles - * the special case of the {@link Named#EXTENDED_SRGB} family of color spaces. - * - * @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 void 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))) { - primaries[0] = 1.41f; - primaries[1] = 0.33f; - primaries[2] = 0.27f; - primaries[3] = 1.24f; - primaries[4] = -0.23f; - primaries[5] = -0.57f; - } else { - rgb.getPrimaries(primaries); - } - if (asUcs) xyYToUv(primaries); - } - - /** - * Draws the CIE 1931 chromaticity diagram: the spectral locus and its inside. - * This method respect the clip parameter. - * - * @param canvas The canvas to transform - * @param width Width in pixel of the final image - * @param height Height in pixel of the final image - * @param paint A pre-allocated paint used to avoid temporary allocations - * @param path A pre-allocated path used to avoid temporary allocations - * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations - */ - private void drawLocus( - @NonNull Canvas canvas, int width, int height, @NonNull Paint paint, - @NonNull Path path, @NonNull @Size(6) float[] primaries) { - - int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6; - float[] vertices = new float[vertexCount * 2]; - int[] colors = new int[vertices.length]; - 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) { - for (final Pair item : mColorSpaces) { - ColorSpace colorSpace = item.first; - if (colorSpace.getModel() != Model.RGB) continue; - - Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries, mUcs); - - break; - } - - path.rewind(); - path.moveTo(width * primaries[0], height - height * primaries[1]); - path.lineTo(width * primaries[2], height - height * primaries[3]); - path.lineTo(width * primaries[4], height - height * primaries[5]); - path.close(); - - int[] solid = new int[colors.length]; - Arrays.fill(solid, 0xff6c6c6c); - canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0, - null, 0, solid, 0, null, 0, 0, paint); - - canvas.save(); - canvas.clipPath(path); - - canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0, - null, 0, colors, 0, null, 0, 0, paint); - - canvas.restore(); - } else { - canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0, - null, 0, colors, 0, null, 0, 0, paint); - } - - // Draw the non-spectral locus - int index = (CHROMATICITY_RESOLUTION - 1) * 12; - path.reset(); - path.moveTo(vertices[index], vertices[index + 1]); - for (int x = 2; x < SPECTRUM_LOCUS_X.length; x++) { - index += CHROMATICITY_RESOLUTION * 12; - path.lineTo(vertices[index], vertices[index + 1]); - } - path.close(); - - paint.setStrokeWidth(4.0f / (mUcs ? UCS_SCALE : 1.0f)); - paint.setStyle(Paint.Style.STROKE); - paint.setColor(0xff000000); - canvas.drawPath(path, paint); - } - - /** - * Draws the diagram box, including borders, tick marks, grid lines - * and axis labels. - * - * @param canvas The canvas to transform - * @param width Width in pixel of the final image - * @param height Height in pixel of the final image - * @param paint A pre-allocated paint used to avoid temporary allocations - * @param path A pre-allocated path used to avoid temporary allocations - */ - 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 < 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 < 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 - paint.setStyle(Paint.Style.FILL); - paint.setTextSize(36.0f); - paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL)); - - Rect bounds = new Rect(); - for (int i = 1; i < lineCount - 1; i++) { - String text = "0." + i; - paint.getTextBounds(text, 0, text.length(), bounds); - - float v = i / 10.0f; - float x = (width * v) * scale; - float y = height - (height * v) * scale; - - 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); - } - paint.setStyle(Paint.Style.STROKE); - - // Draw the diagram box - path.moveTo(0.0f, height); - path.lineTo(0.9f * width, height); - path.lineTo(0.9f * width, 0.1f * height); - path.lineTo(0.0f, 0.1f * height); - path.close(); - canvas.drawPath(path, paint); - } - - /** - * Computes and applies the Canvas transforms required to make the color - * gamut of each color space visible in the final image. - * - * @param canvas The canvas to transform - * @param width Width in pixel of the final image - * @param height Height in pixel of the final image - * @param primaries Array of 6 floats used to avoid temporary allocations - */ - private void setTransform(@NonNull Canvas canvas, int width, int height, - @NonNull @Size(6) float[] primaries) { - - RectF primariesBounds = new RectF(); - for (final Pair item : mColorSpaces) { - ColorSpace colorSpace = item.first; - if (colorSpace.getModel() != Model.RGB) continue; - - Rgb rgb = (Rgb) colorSpace; - getPrimaries(rgb, primaries, mUcs); - - primariesBounds.left = Math.min(primariesBounds.left, primaries[4]); - primariesBounds.top = Math.min(primariesBounds.top, primaries[5]); - primariesBounds.right = Math.max(primariesBounds.right, primaries[0]); - 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(max, primariesBounds.right); - primariesBounds.bottom = Math.max(max, primariesBounds.bottom); - - 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() - 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, - 0.146958f, 0.139149f, 0.133536f, 0.126688f, 0.115830f, - 0.109616f, 0.099146f, 0.091310f, 0.078130f, 0.068717f, - 0.054675f, 0.040763f, 0.027497f, 0.016270f, 0.008169f, - 0.004876f, 0.003983f, 0.003859f, 0.004646f, 0.007988f, - 0.013870f, 0.022244f, 0.027273f, 0.032820f, 0.038851f, - 0.045327f, 0.052175f, 0.059323f, 0.066713f, 0.074299f, - 0.089937f, 0.114155f, 0.138695f, 0.154714f, 0.192865f, - 0.229607f, 0.265760f, 0.301588f, 0.337346f, 0.373083f, - 0.408717f, 0.444043f, 0.478755f, 0.512467f, 0.544767f, - 0.575132f, 0.602914f, 0.627018f, 0.648215f, 0.665746f, - 0.680061f, 0.691487f, 0.700589f, 0.707901f, 0.714015f, - 0.719017f, 0.723016f, 0.734674f, 0.717203f, 0.699732f, - 0.682260f, 0.664789f, 0.647318f, 0.629847f, 0.612376f, - 0.594905f, 0.577433f, 0.559962f, 0.542491f, 0.525020f, - 0.507549f, 0.490077f, 0.472606f, 0.455135f, 0.437664f, - 0.420193f, 0.402721f, 0.385250f, 0.367779f, 0.350308f, - 0.332837f, 0.315366f, 0.297894f, 0.280423f, 0.262952f, - 0.245481f, 0.228010f, 0.210538f, 0.193067f, 0.175596f - }; - // Y coordinates of the spectral locus in CIE 1931 - private static final float[] SPECTRUM_LOCUS_Y = { - 0.005295f, 0.004800f, 0.005472f, 0.005976f, 0.014496f, - 0.026643f, 0.035211f, 0.042704f, 0.053441f, 0.073601f, - 0.086866f, 0.112037f, 0.132737f, 0.170464f, 0.200773f, - 0.254155f, 0.317049f, 0.387997f, 0.463035f, 0.538504f, - 0.587196f, 0.610526f, 0.654897f, 0.675970f, 0.715407f, - 0.750246f, 0.779682f, 0.792153f, 0.802971f, 0.812059f, - 0.819430f, 0.825200f, 0.829460f, 0.832306f, 0.833833f, - 0.833316f, 0.826231f, 0.814796f, 0.805884f, 0.781648f, - 0.754347f, 0.724342f, 0.692326f, 0.658867f, 0.624470f, - 0.589626f, 0.554734f, 0.520222f, 0.486611f, 0.454454f, - 0.424252f, 0.396516f, 0.372510f, 0.351413f, 0.334028f, - 0.319765f, 0.308359f, 0.299317f, 0.292044f, 0.285945f, - 0.280951f, 0.276964f, 0.265326f, 0.257200f, 0.249074f, - 0.240948f, 0.232822f, 0.224696f, 0.216570f, 0.208444f, - 0.200318f, 0.192192f, 0.184066f, 0.175940f, 0.167814f, - 0.159688f, 0.151562f, 0.143436f, 0.135311f, 0.127185f, - 0.119059f, 0.110933f, 0.102807f, 0.094681f, 0.086555f, - 0.078429f, 0.070303f, 0.062177f, 0.054051f, 0.045925f, - 0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f - }; - - /** - * Computes a 2D mesh representation of the CIE 1931 chromaticity - * diagram. - * - * @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(@NonNull float[] vertices, - @NonNull int[] colors) { - - ColorSpace colorSpace = get(Named.SRGB); - - float[] color = new float[3]; - - int vertexIndex = 0; - int colorIndex = 0; - - for (int x = 0; x < SPECTRUM_LOCUS_X.length; x++) { - int nextX = (x % (SPECTRUM_LOCUS_X.length - 1)) + 1; - - float a1 = (float) Math.atan2( - SPECTRUM_LOCUS_Y[x] - ONE_THIRD, - SPECTRUM_LOCUS_X[x] - ONE_THIRD); - float a2 = (float) Math.atan2( - SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD, - SPECTRUM_LOCUS_X[nextX] - ONE_THIRD); - - float radius1 = (float) Math.pow( - sqr(SPECTRUM_LOCUS_X[x] - ONE_THIRD) + - sqr(SPECTRUM_LOCUS_Y[x] - ONE_THIRD), - 0.5); - float radius2 = (float) Math.pow( - sqr(SPECTRUM_LOCUS_X[nextX] - ONE_THIRD) + - sqr(SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD), - 0.5); - - // Compute patches; each patch is a quad with a different - // color associated with each vertex - for (int c = 1; c <= CHROMATICITY_RESOLUTION; c++) { - float f1 = c / (float) CHROMATICITY_RESOLUTION; - float f2 = (c - 1) / (float) CHROMATICITY_RESOLUTION; - - double cr1 = radius1 * Math.cos(a1); - double sr1 = radius1 * Math.sin(a1); - double cr2 = radius2 * Math.cos(a2); - double sr2 = radius2 * Math.sin(a2); - - // Compute the XYZ coordinates of the 4 vertices of the patch - float v1x = (float) (ONE_THIRD + cr1 * f1); - float v1y = (float) (ONE_THIRD + sr1 * f1); - float v1z = 1 - v1x - v1y; - - float v2x = (float) (ONE_THIRD + cr1 * f2); - float v2y = (float) (ONE_THIRD + sr1 * f2); - float v2z = 1 - v2x - v2y; - - float v3x = (float) (ONE_THIRD + cr2 * f2); - float v3y = (float) (ONE_THIRD + sr2 * f2); - float v3z = 1 - v3x - v3y; - - float v4x = (float) (ONE_THIRD + cr2 * f1); - float v4y = (float) (ONE_THIRD + sr2 * f1); - float v4z = 1 - v4x - v4y; - - // Compute the sRGB representation of each XYZ coordinate of the patch - colors[colorIndex ] = computeColor(color, v1x, v1y, v1z, colorSpace); - colors[colorIndex + 1] = computeColor(color, v2x, v2y, v2z, colorSpace); - colors[colorIndex + 2] = computeColor(color, v3x, v3y, v3z, colorSpace); - colors[colorIndex + 3] = colors[colorIndex]; - colors[colorIndex + 4] = colors[colorIndex + 2]; - colors[colorIndex + 5] = computeColor(color, v4x, v4y, v4z, colorSpace); - colorIndex += 6; - - // Flip the mesh upside down to match Canvas' coordinates system - 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; - } - } - } - - @ColorInt - private static int computeColor(@NonNull @Size(3) float[] color, - float x, float y, float z, @NonNull ColorSpace cs) { - color[0] = x; - color[1] = y; - color[2] = z; - cs.fromXyz(color); - return 0xff000000 | - (((int) (color[0] * 255.0f) & 0xff) << 16) | - (((int) (color[1] * 255.0f) & 0xff) << 8) | - (((int) (color[2] * 255.0f) & 0xff) ); - } - - private static double sqr(double v) { - return v * v; - } - - private static class Point { - @NonNull final ColorSpace mColorSpace; - @NonNull final float[] mRgb; - final int mColor; - - Point(@NonNull ColorSpace colorSpace, - @NonNull @Size(3) float[] rgb, @ColorInt int color) { - mColorSpace = colorSpace; - mRgb = rgb; - mColor = color; - } - } - } } diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index bb889bea9e714..803b2e1da6de6 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -14469,6 +14469,8 @@ package android.graphics { @AnyThread public abstract class ColorSpace { method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace adapt(@NonNull android.graphics.ColorSpace, @NonNull @Size(min=2, max=3) float[], @NonNull android.graphics.ColorSpace.Adaptation); + method @NonNull @Size(3) public static float[] cctToXyz(@IntRange(from=1) int); + method @NonNull @Size(9) public static float[] chromaticAdaptation(@NonNull android.graphics.ColorSpace.Adaptation, @NonNull @Size(min=2, max=3) float[], @NonNull @Size(min=2, max=3) float[]); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace, @NonNull android.graphics.ColorSpace.RenderIntent); method @NonNull public static android.graphics.ColorSpace.Connector connect(@NonNull android.graphics.ColorSpace); diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java index 3f1c222ab5200..bdbaaa86e9a11 100644 --- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java +++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java @@ -18,6 +18,8 @@ package com.android.server.display.color; import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; +import android.annotation.NonNull; +import android.annotation.Size; import android.content.Context; import android.content.res.Resources; import android.graphics.ColorSpace; @@ -32,7 +34,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; -import java.lang.System; final class DisplayWhiteBalanceTintController extends TintController { @@ -131,6 +132,30 @@ final class DisplayWhiteBalanceTintController extends TintController { : ColorDisplayService.MATRIX_IDENTITY; } + /** + * Multiplies two 3x3 matrices, represented as non-null arrays of 9 floats. + * + * @param lhs 3x3 matrix, as a non-null array of 9 floats + * @param rhs 3x3 matrix, as a non-null array of 9 floats + * @return A new array of 9 floats containing the result of the multiplication + * of rhs by lhs + */ + @NonNull + @Size(9) + private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) { + float[] r = new float[9]; + r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2]; + r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2]; + r[2] = lhs[2] * rhs[0] + lhs[5] * rhs[1] + lhs[8] * rhs[2]; + r[3] = lhs[0] * rhs[3] + lhs[3] * rhs[4] + lhs[6] * rhs[5]; + r[4] = lhs[1] * rhs[3] + lhs[4] * rhs[4] + lhs[7] * rhs[5]; + r[5] = lhs[2] * rhs[3] + lhs[5] * rhs[4] + lhs[8] * rhs[5]; + r[6] = lhs[0] * rhs[6] + lhs[3] * rhs[7] + lhs[6] * rhs[8]; + r[7] = lhs[1] * rhs[6] + lhs[4] * rhs[7] + lhs[7] * rhs[8]; + r[8] = lhs[2] * rhs[6] + lhs[5] * rhs[7] + lhs[8] * rhs[8]; + return r; + } + @Override public void setMatrix(int cct) { if (!mSetUp) { @@ -160,9 +185,9 @@ final class DisplayWhiteBalanceTintController extends TintController { mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); // Convert the adaptation matrix to RGB space - float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, + float[] result = mul3x3(mChromaticAdaptationMatrix, mDisplayColorSpaceRGB.getTransform()); - result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); + result = mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); // Normalize the transform matrix to peak white value in RGB space final float adaptedMaxR = result[0] + result[3] + result[6];