• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 static org.testng.Assert.assertEquals;
20 import static org.testng.Assert.assertTrue;
21 
22 import android.graphics.Bitmap;
23 import android.graphics.Canvas;
24 import android.graphics.Color;
25 import android.graphics.ColorSpace;
26 import android.graphics.Gainmap;
27 import android.graphics.HardwareBufferRenderer;
28 import android.graphics.Matrix;
29 import android.graphics.Picture;
30 import android.graphics.RecordingCanvas;
31 import android.graphics.RenderNode;
32 import android.hardware.HardwareBuffer;
33 
34 import androidx.annotation.ColorLong;
35 import androidx.test.filters.SmallTest;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.testng.Assert;
41 
42 import java.util.concurrent.CountDownLatch;
43 import java.util.concurrent.TimeUnit;
44 
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class GainmapTests {
48 
49     private static final ColorSpace BT2020_HLG = ColorSpace.get(ColorSpace.Named.BT2020_HLG);
50     private static final ColorSpace BT2020_PQ = ColorSpace.get(ColorSpace.Named.BT2020_PQ);
51 
52     // A 10x6 base image with a 5x3 (so 1/2 res) gainmap that boosts the center 3 pixels
53     // by 0x40, 0x80, and 0xff respectively
54     private static final Bitmap sTestImage;
55     static {
56         Bitmap base = Bitmap.createBitmap(10, 6, Bitmap.Config.ARGB_8888);
57         base.eraseColor(Color.WHITE);
58 
59         Bitmap gainmapImage = Bitmap.createBitmap(5, 3, Bitmap.Config.ARGB_8888);
60         gainmapImage.eraseColor(0);
61         gainmapImage.setPixel(1, 1, 0xFF404040);
62         gainmapImage.setPixel(2, 1, 0xFF808080);
63         gainmapImage.setPixel(3, 1, 0xFFFFFFFF);
64 
65         Gainmap gainmap = new Gainmap(gainmapImage);
66         base.setGainmap(gainmap);
67         sTestImage = base;
68     }
69 
70     private static final Picture sTestPicture;
71     static {
72         sTestPicture = new Picture();
73         Canvas canvas = sTestPicture.beginRecording(sTestImage.getWidth(), sTestImage.getHeight());
canvas.drawBitmap(sTestImage, 0, 0, null)74         canvas.drawBitmap(sTestImage, 0, 0, null);
sTestPicture.endRecording()75         sTestPicture.endRecording();
76     }
77 
assertChannels(Color result, @ColorLong long expected, float delta)78     private static void assertChannels(Color result, @ColorLong long expected, float delta) {
79         ColorSpace.Connector connector = ColorSpace.connect(Color.colorSpace(expected),
80                 result.getColorSpace());
81         float[] mapped = connector.transform(Color.red(expected), Color.green(expected),
82                 Color.blue(expected));
83         Assert.assertEquals(result.red(), mapped[0], delta, "red channel mismatch");
84         Assert.assertEquals(result.green(), mapped[1], delta, "green channel mismatch");
85         Assert.assertEquals(result.blue(), mapped[2], delta, "blue channel mismatch");
86     }
87 
mapWhiteWithGain(Gainmap gainmap, double gain)88     private static @ColorLong long mapWhiteWithGain(Gainmap gainmap, double gain) {
89         double logRatioMin = Math.log(gainmap.getRatioMin()[0]);
90         double logRatioMax = Math.log(gainmap.getRatioMax()[0]);
91         double epsilonSdr = gainmap.getEpsilonSdr()[0];
92         double epsilonHdr = gainmap.getEpsilonHdr()[0];
93         double L = (logRatioMin * (1 - gain)) + (logRatioMax * gain);
94         float D = (float) ((1.0 + epsilonSdr) * Math.exp(L) - epsilonHdr);
95         return Color.pack(D, D, D, 1.f, ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB));
96     }
97 
mapWhiteWithGain(double gain)98     private static @ColorLong long mapWhiteWithGain(double gain) {
99         return mapWhiteWithGain(sTestImage.getGainmap(), gain);
100     }
101 
toleranceForResult(Bitmap result)102     private static float toleranceForResult(Bitmap result) {
103         // 8888 precision ain't so great
104         if (result.getConfig() == Bitmap.Config.ARGB_8888) {
105             // PQ math on GLES2.0 is very poor
106             if (result.getColorSpace().getId() == ColorSpace.Named.BT2020_PQ.ordinal()) {
107                 return 0.06f;
108             }
109             return 0.02f;
110         }
111         return 0.002f;
112     }
113 
assertTestImageResult(Bitmap result)114     private static void assertTestImageResult(Bitmap result) {
115         final float delta = toleranceForResult(result);
116         assertChannels(result.getColor(0, 0), Color.pack(Color.WHITE), delta);
117         assertChannels(result.getColor(2, 2), mapWhiteWithGain(0x40 / 255.f), delta);
118         assertChannels(result.getColor(4, 2), mapWhiteWithGain(0x80 / 255.f), delta);
119         assertChannels(result.getColor(6, 2), mapWhiteWithGain(0xFF / 255.f), delta);
120     }
121 
renderTestImageWithHardware(ColorSpace dest)122     private static Bitmap renderTestImageWithHardware(ColorSpace dest) {
123         return renderTestImageWithHardware(dest, false);
124     }
125 
renderTestImageWithHardware(ColorSpace dest, boolean usePicture)126     private static Bitmap renderTestImageWithHardware(ColorSpace dest, boolean usePicture) {
127         HardwareBuffer buffer = HardwareBuffer.create(sTestImage.getWidth(), sTestImage.getHeight(),
128                 HardwareBuffer.RGBA_8888,
129                 1, HardwareBuffer.USAGE_GPU_COLOR_OUTPUT | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
130         HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer);
131         RenderNode content = new RenderNode("gainmap");
132         content.setPosition(0, 0, sTestImage.getWidth(), sTestImage.getHeight());
133         RecordingCanvas canvas = content.beginRecording();
134         if (usePicture) {
135             canvas.drawPicture(sTestPicture);
136         } else {
137             canvas.drawBitmap(sTestImage, 0, 0, null);
138         }
139         content.endRecording();
140         renderer.setContentRoot(content);
141         CountDownLatch latch = new CountDownLatch(1);
142         renderer.obtainRenderRequest().setColorSpace(dest).draw(Runnable::run, result -> {
143             result.getFence().awaitForever();
144             latch.countDown();
145         });
146         try {
147             Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
148         } catch (InterruptedException ex) {
149             Assert.fail(ex.getMessage());
150         }
151         return Bitmap.wrapHardwareBuffer(buffer, dest).copy(Bitmap.Config.ARGB_8888, false);
152     }
153 
154     @Test
gainmapToHlgSoftware()155     public void gainmapToHlgSoftware() {
156         Bitmap result = Bitmap.createBitmap(10, 6, Bitmap.Config.RGBA_F16, false, BT2020_HLG);
157         Canvas canvas = new Canvas(result);
158         canvas.drawBitmap(sTestImage, 0f, 0f, null);
159         assertTestImageResult(result);
160     }
161 
162     @Test
gainmapToPqSoftware()163     public void gainmapToPqSoftware() {
164         Bitmap result = Bitmap.createBitmap(10, 6, Bitmap.Config.RGBA_F16, false, BT2020_PQ);
165         Canvas canvas = new Canvas(result);
166         canvas.drawBitmap(sTestImage, 0f, 0f, null);
167         assertTestImageResult(result);
168     }
169 
170     @Test
gainmapToHlgPictureSoftware()171     public void gainmapToHlgPictureSoftware() {
172         Bitmap result = Bitmap.createBitmap(10, 6, Bitmap.Config.RGBA_F16, false, BT2020_HLG);
173         Canvas canvas = new Canvas(result);
174         canvas.drawPicture(sTestPicture);
175         assertTestImageResult(result);
176     }
177 
178     @Test
gainmapToPqPictureSoftware()179     public void gainmapToPqPictureSoftware() {
180         Bitmap result = Bitmap.createBitmap(10, 6, Bitmap.Config.RGBA_F16, false, BT2020_HLG);
181         Canvas canvas = new Canvas(result);
182         canvas.drawPicture(sTestPicture);
183         assertTestImageResult(result);
184     }
185 
186     @Test
gainmapToHlgHardware()187     public void gainmapToHlgHardware() throws Exception {
188         Bitmap result = renderTestImageWithHardware(BT2020_HLG);
189         assertTestImageResult(result);
190     }
191 
192     @Test
gainmapToPqHardware()193     public void gainmapToPqHardware() {
194         Bitmap result = renderTestImageWithHardware(BT2020_PQ);
195         assertTestImageResult(result);
196     }
197 
198     @Test
gainmapToHlgPictureHardware()199     public void gainmapToHlgPictureHardware() throws Exception {
200         Bitmap result = renderTestImageWithHardware(BT2020_HLG, true);
201         assertTestImageResult(result);
202     }
203 
204     @Test
gainmapToPqPictureHardware()205     public void gainmapToPqPictureHardware() {
206         Bitmap result = renderTestImageWithHardware(BT2020_PQ, true);
207         assertTestImageResult(result);
208     }
209 
210     @Test
createScaledBitmap()211     public void createScaledBitmap() {
212         Bitmap result = Bitmap.createScaledBitmap(sTestImage, 20, 12, false);
213         assertEquals(result.getWidth(), 20);
214         assertEquals(result.getHeight(), 12);
215         assertTrue(result.hasGainmap());
216         Bitmap gainmapContents = result.getGainmap().getGainmapContents();
217         assertEquals(gainmapContents.getWidth(), 10);
218         assertEquals(gainmapContents.getHeight(), 6);
219 
220         assertChannels(gainmapContents.getColor(0, 0), Color.pack(Color.BLACK), 0f);
221         assertChannels(gainmapContents.getColor(1, 1), Color.pack(Color.BLACK), 0f);
222 
223         assertChannels(gainmapContents.getColor(2, 2), Color.pack(0xFF404040), 0f);
224         assertChannels(gainmapContents.getColor(3, 3), Color.pack(0xFF404040), 0f);
225 
226         assertChannels(gainmapContents.getColor(4, 2), Color.pack(0xFF808080), 0f);
227         assertChannels(gainmapContents.getColor(5, 3), Color.pack(0xFF808080), 0f);
228 
229         assertChannels(gainmapContents.getColor(6, 2), Color.pack(0xFFFFFFFF), 0f);
230         assertChannels(gainmapContents.getColor(7, 3), Color.pack(0xFFFFFFFF), 0f);
231 
232         assertChannels(gainmapContents.getColor(8, 4), Color.pack(Color.BLACK), 0f);
233         assertChannels(gainmapContents.getColor(9, 5), Color.pack(Color.BLACK), 0f);
234     }
235 
236     @Test
applyRotation180Matrix()237     public void applyRotation180Matrix() {
238         Matrix m = new Matrix();
239         m.setRotate(180.0f, 5.f, 3.f);
240         Bitmap result = Bitmap.createBitmap(sTestImage, 0, 0, 10, 6, m, false);
241         assertEquals(result.getWidth(), 10);
242         assertEquals(result.getHeight(), 6);
243         assertTrue(result.hasGainmap());
244         Bitmap gainmapContents = result.getGainmap().getGainmapContents();
245         assertEquals(gainmapContents.getWidth(), 5);
246         assertEquals(gainmapContents.getHeight(), 3);
247         assertChannels(gainmapContents.getColor(0, 0), Color.pack(Color.BLACK), 0f);
248         assertChannels(gainmapContents.getColor(0, 1), Color.pack(Color.BLACK), 0f);
249         assertChannels(gainmapContents.getColor(1, 1), Color.pack(0xFFFFFFFF), 0f);
250         assertChannels(gainmapContents.getColor(2, 1), Color.pack(0xFF808080), 0f);
251         assertChannels(gainmapContents.getColor(3, 1), Color.pack(0xFF404040), 0f);
252         assertChannels(gainmapContents.getColor(4, 1), Color.pack(Color.BLACK), 0f);
253         assertChannels(gainmapContents.getColor(4, 2), Color.pack(Color.BLACK), 0f);
254     }
255 
256     @Test
applyRotation90Matrix()257     public void applyRotation90Matrix() {
258         Matrix m = new Matrix();
259         m.setRotate(90.0f, 5.f, 3.f);
260         Bitmap result = Bitmap.createBitmap(sTestImage, 0, 0, 10, 6, m, false);
261         assertEquals(result.getWidth(), 6);
262         assertEquals(result.getHeight(), 10);
263         assertTrue(result.hasGainmap());
264         Bitmap gainmapContents = result.getGainmap().getGainmapContents();
265         assertEquals(gainmapContents.getWidth(), 3);
266         assertEquals(gainmapContents.getHeight(), 5);
267         assertChannels(gainmapContents.getColor(0, 0), Color.pack(Color.BLACK), 0f);
268         assertChannels(gainmapContents.getColor(1, 0), Color.pack(Color.BLACK), 0f);
269         assertChannels(gainmapContents.getColor(1, 1), Color.pack(0xFF404040), 0f);
270         assertChannels(gainmapContents.getColor(1, 2), Color.pack(0xFF808080), 0f);
271         assertChannels(gainmapContents.getColor(1, 3), Color.pack(0xFFFFFFFF), 0f);
272         assertChannels(gainmapContents.getColor(1, 4), Color.pack(Color.BLACK), 0f);
273         assertChannels(gainmapContents.getColor(2, 4), Color.pack(Color.BLACK), 0f);
274     }
275 }
276