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 .001f)); 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