1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.uirendering.cts.testclasses;
18 
19 import android.content.Context;
20 import android.content.res.AssetManager;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.BlendMode;
24 import android.graphics.Color;
25 import android.graphics.ColorSpace;
26 import android.graphics.Point;
27 import android.uirendering.cts.R;
28 import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
29 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
30 import android.uirendering.cts.bitmapverifiers.SamplePointWideGamutVerifier;
31 import android.uirendering.cts.testclasses.view.BitmapView;
32 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
33 import android.view.Display;
34 import android.view.View;
35 import android.view.WindowManager;
36 
37 import androidx.test.InstrumentationRegistry;
38 import androidx.test.filters.MediumTest;
39 import androidx.test.runner.AndroidJUnit4;
40 
41 import org.junit.Assert;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.util.Arrays;
49 
50 @MediumTest
51 @RunWith(AndroidJUnit4.class)
52 public class WideColorGamutTests extends ActivityTestBase {
53     private static final ColorSpace DISPLAY_P3 = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
54     private static final ColorSpace SCRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
55 
56     private static final Point[] POINTS = {
57             new Point(16, 16),
58             new Point(48, 16),
59             new Point(80, 16),
60     };
61 
62     // The colors are defined as found in wide-gamut-test.png, which is Display P3
63     // Since the UI toolkit renders in scRGB, we want to convert here to compare values
64     // directly in the sample point verifier
65     private static final Color[] COLORS = {
66             Color.valueOf(0.937f, 0.000f, 0.000f, 1.0f, DISPLAY_P3).convert(SCRGB),
67             Color.valueOf(1.000f, 0.000f, 0.000f, 1.0f, DISPLAY_P3).convert(SCRGB),
68             Color.valueOf(0.918f, 0.200f, 0.137f, 1.0f, DISPLAY_P3).convert(SCRGB)
69     };
70 
71     private Bitmap mBitmap;
72 
73     @Override
isWideColorGamut()74     protected boolean isWideColorGamut() {
75         return true;
76     }
77 
78     @Before
loadBitmap()79     public void loadBitmap() {
80         try (InputStream in = getActivity().getAssets().open("wide-gamut-test.png")) {
81             mBitmap = BitmapFactory.decodeStream(in);
82         } catch (IOException e) {
83             Assert.fail("Could not load wide-gamut-test.png");
84         }
85     }
86 
87     @SuppressWarnings("SameParameterValue")
getVerifier(Point[] points, Color[] colors, float eps)88     private BitmapVerifier getVerifier(Point[] points, Color[] colors, float eps) {
89         if (getActivity().getWindow().isWideColorGamut()) {
90             return new SamplePointWideGamutVerifier(points, colors, eps);
91         }
92         return new SamplePointVerifier(points,
93                 Arrays.stream(colors).mapToInt(Color::toArgb).toArray(),
94                 (int) (eps * 255.0f + 0.5f));
95     }
96 
97     @Test
testDraw()98     public void testDraw() {
99         createTest()
100                 .addLayout(R.layout.wide_gamut_bitmap_layout, view -> {
101                     BitmapView bv = (BitmapView) view;
102                     bv.setBitmap(mBitmap);
103                 }, true)
104                 .runWithVerifier(getVerifier(POINTS, COLORS, 1e-2f));
105     }
106 
107     @Test
testSaveLayer()108     public void testSaveLayer() {
109         createTest()
110                 .addLayout(R.layout.wide_gamut_bitmap_layout, view -> {
111                     BitmapView bv = (BitmapView) view;
112                     bv.setBitmap(mBitmap);
113                     bv.setSaveLayer(true);
114                 }, true)
115                 .runWithVerifier(getVerifier(POINTS, COLORS, 1e-2f));
116     }
117 
118     @Test
testHardwareLayer()119     public void testHardwareLayer() {
120         createTest()
121                 .addLayout(R.layout.wide_gamut_bitmap_layout, view -> {
122                     BitmapView bv = (BitmapView) view;
123                     bv.setBitmap(mBitmap);
124                     bv.setLayerType(View.LAYER_TYPE_HARDWARE, null);
125                 }, true)
126                 .runWithVerifier(getVerifier(POINTS, COLORS, 1e-2f));
127     }
128 
129     @Test
testSaveLayerInHardwareLayer()130     public void testSaveLayerInHardwareLayer() {
131         createTest()
132                 .addLayout(R.layout.wide_gamut_bitmap_layout, view -> {
133                     BitmapView bv = (BitmapView) view;
134                     bv.setBitmap(mBitmap);
135                     bv.setSaveLayer(true);
136                     bv.setLayerType(View.LAYER_TYPE_HARDWARE, null);
137                 }, true)
138                 .runWithVerifier(getVerifier(POINTS, COLORS, 1e-2f));
139     }
140 
141     @Test
testCanvasDrawColorLong()142     public void testCanvasDrawColorLong() {
143         final Color greenP3 = Color.valueOf(0, 1.0f, 0, 1.0f, DISPLAY_P3);
144         createTest()
145                 .addCanvasClient((canvas, width, height) -> {
146                     canvas.drawColor(greenP3.pack());
147                 }, true)
148                 .runWithVerifier(getVerifier(
149                             new Point[] { new Point(0, 0), new Point(50, 50) },
150                             new Color[] { greenP3, greenP3 },
151                             .002f));
152     }
153 
plus(Color a, Color b)154     private static Color plus(Color a, Color b) {
155         final ColorSpace cs = a.getColorSpace();
156         Assert.assertSame(cs, b.getColorSpace());
157 
158         float[] ac = a.getComponents();
159         float[] bc = b.getComponents();
160         float[] result = new float[ac.length];
161         for (int i = 0; i < ac.length; ++i) {
162             // BlendMode.PLUS clamps to [0,1]
163             result[i] = Math.max(Math.min(ac[i] + bc[i], 1.0f), 0.0f);
164         }
165         return Color.valueOf(result, cs);
166     }
167 
168     @Test
testCanvasDrawColorLongBlendMode()169     public void testCanvasDrawColorLongBlendMode() {
170         final Color greenP3 = Color.valueOf(0, 1.0f, 0, 1.0f, DISPLAY_P3);
171         final Color redP3 = Color.valueOf(1.0f, 0, 0, 1.0f, DISPLAY_P3);
172 
173         final ColorSpace displaySpace = displaySpace();
174         final Color greenDisplay = greenP3.convert(displaySpace);
175         final Color redDisplay = redP3.convert(displaySpace);
176 
177         final Color expected = plus(greenDisplay, redDisplay);
178         createTest()
179                 .addCanvasClient((canvas, width, height) -> {
180                     canvas.drawColor(greenP3.pack());
181                     canvas.drawColor(redP3.pack(), BlendMode.PLUS);
182                 }, true)
183                 .runWithVerifier(getVerifier(
184                             new Point[] { new Point(0, 0), new Point(50, 50) },
185                             new Color[] { expected, expected },
186                             .002f));
187     }
188 
displaySpace()189     private ColorSpace displaySpace() {
190         Context context = InstrumentationRegistry.getInstrumentation().getContext();
191         WindowManager window = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
192         Display defaultDisplay = window.getDefaultDisplay();
193         ColorSpace displaySpace = defaultDisplay.getPreferredWideGamutColorSpace();
194         return displaySpace == null ? ColorSpace.get(ColorSpace.Named.SRGB) : displaySpace;
195     }
196 
197     @Test
testProPhoto()198     public void testProPhoto() {
199         Color blueProPhoto = Color.valueOf(0, 0, 1, 1,
200                 ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
201         final Color blueDisplay = blueProPhoto.convert(displaySpace());
202         createTest()
203                 .addCanvasClient("RGBA16F_ProPhoto", (canvas, width, height) -> {
204                     AssetManager assets = getActivity().getResources().getAssets();
205                     try (InputStream in = assets.open("blue-16bit-prophoto.png")) {
206                         Bitmap bitmap = BitmapFactory.decodeStream(in);
207                         canvas.scale(
208                                 width / (float) bitmap.getWidth(),
209                                 height / (float) bitmap.getHeight());
210                         canvas.drawBitmap(bitmap, 0, 0, null);
211                     } catch (IOException e) {
212                         throw new RuntimeException("Test failed: ", e);
213                     }
214                 }, true)
215                 .runWithVerifier(getVerifier(
216                         new Point[] { new Point(0, 0) },
217                         new Color[] { blueDisplay },
218                 0.6f));
219     }
220 }
221