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