1 /*
2  * Copyright (C) 2006 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.graphics;
18 
19 import android.hardware.HardwareBuffer;
20 
21 import androidx.test.filters.SmallTest;
22 
23 import junit.framework.TestCase;
24 
25 import java.nio.ByteBuffer;
26 import java.nio.IntBuffer;
27 import java.nio.ShortBuffer;
28 
29 public class BitmapTest extends TestCase {
30 
31     @SmallTest
testBasic()32     public void testBasic() throws Exception {
33         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
34         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
35         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
36 
37         assertTrue("mutability", bm1.isMutable());
38         assertTrue("mutability", bm2.isMutable());
39         assertTrue("mutability", bm3.isMutable());
40 
41         assertEquals("width", 100, bm1.getWidth());
42         assertEquals("width", 100, bm2.getWidth());
43         assertEquals("width", 100, bm3.getWidth());
44 
45         assertEquals("rowbytes", 400, bm1.getRowBytes());
46         assertEquals("rowbytes", 200, bm2.getRowBytes());
47         assertEquals("rowbytes", 400, bm3.getRowBytes());
48 
49         assertEquals("byteCount", 80000, bm1.getByteCount());
50         assertEquals("byteCount", 40000, bm2.getByteCount());
51         assertEquals("byteCount", 80000, bm3.getByteCount());
52 
53         assertEquals("height", 200, bm1.getHeight());
54         assertEquals("height", 200, bm2.getHeight());
55         assertEquals("height", 200, bm3.getHeight());
56 
57         assertTrue("hasAlpha", bm1.hasAlpha());
58         assertFalse("hasAlpha", bm2.hasAlpha());
59         assertTrue("hasAlpha", bm3.hasAlpha());
60 
61         assertTrue("getConfig", bm1.getConfig() == Bitmap.Config.ARGB_8888);
62         assertTrue("getConfig", bm2.getConfig() == Bitmap.Config.RGB_565);
63         assertTrue("getConfig", bm3.getConfig() == Bitmap.Config.ARGB_8888);
64     }
65 
66     @SmallTest
testMutability()67     public void testMutability() throws Exception {
68         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
69         Bitmap bm2 = Bitmap.createBitmap(new int[100 * 200], 100, 200,
70                                          Bitmap.Config.ARGB_8888);
71 
72         assertTrue("mutability", bm1.isMutable());
73         assertFalse("mutability", bm2.isMutable());
74 
75         bm1.eraseColor(0);
76 
77         try {
78             bm2.eraseColor(0);
79             fail("eraseColor should throw exception");
80         } catch (IllegalStateException ex) {
81             // safe to catch and ignore this
82         }
83     }
84 
85     @SmallTest
testGetPixelsWithAlpha()86     public void testGetPixelsWithAlpha() throws Exception {
87         int[] colors = new int[100];
88         for (int i = 0; i < 100; i++) {
89             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
90         }
91 
92         Bitmap bm = Bitmap.createBitmap(colors, 10, 10,
93                                         Bitmap.Config.ARGB_8888);
94 
95         int[] pixels = new int[100];
96         bm.getPixels(pixels, 0, 10, 0, 0, 10, 10);
97         for (int i = 0; i < 100; i++) {
98             int p = bm.getPixel(i % 10, i / 10);
99             assertEquals("getPixels", p, pixels[i]);
100         }
101 
102         for (int i = 0; i < 100; i++) {
103             int p = bm.getPixel(i % 10, i / 10);
104             assertEquals("getPixel", p, colors[i]);
105             assertEquals("pixel value", p,
106                          ((0xFF << 24) | (i << 16) | (i << 8) | i));
107         }
108 
109     }
110 
111     @SmallTest
testGetPixelsWithoutAlpha()112     public void testGetPixelsWithoutAlpha() throws Exception {
113         int[] colors = new int[100];
114         for (int i = 0; i < 100; i++) {
115             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
116         }
117 
118         Bitmap bm = Bitmap.createBitmap(colors, 10, 10, Bitmap.Config.RGB_565);
119 
120         int[] pixels = new int[100];
121         bm.getPixels(pixels, 0, 10, 0, 0, 10, 10);
122         for (int i = 0; i < 100; i++) {
123             int p = bm.getPixel(i % 10, i / 10);
124             assertEquals("getPixels", p, pixels[i]);
125         }
126     }
127 
128     @SmallTest
testSetPixelsWithAlpha()129     public void testSetPixelsWithAlpha() throws Exception {
130         int[] colors = new int[100];
131         for (int i = 0; i < 100; i++) {
132             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
133         }
134 
135         Bitmap.Config config = Bitmap.Config.ARGB_8888;
136         Bitmap bm1 = Bitmap.createBitmap(colors, 10, 10, config);
137         Bitmap bm2 = Bitmap.createBitmap(10, 10, config);
138 
139         for (int i = 0; i < 100; i++) {
140             bm2.setPixel(i % 10, i / 10, colors[i]);
141         }
142 
143         for (int i = 0; i < 100; i++) {
144             assertEquals("setPixel",
145                     bm1.getPixel(i % 10, i / 10), bm2.getPixel(i % 10, i / 10));
146         }
147 
148         for (int i = 0; i < 100; i++) {
149             assertEquals("setPixel value",
150                          bm1.getPixel(i % 10, i / 10), colors[i]);
151         }
152     }
153 
154     @SmallTest
testSetPixelsWithoutAlpha()155     public void testSetPixelsWithoutAlpha() throws Exception {
156         int[] colors = new int[100];
157         for (int i = 0; i < 100; i++) {
158             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
159         }
160 
161         Bitmap.Config config = Bitmap.Config.RGB_565;
162         Bitmap bm1 = Bitmap.createBitmap(colors, 10, 10, config);
163         Bitmap bm2 = Bitmap.createBitmap(10, 10, config);
164 
165         for (int i = 0; i < 100; i++) {
166             bm2.setPixel(i % 10, i / 10, colors[i]);
167         }
168 
169         for (int i = 0; i < 100; i++) {
170             assertEquals("setPixel", bm1.getPixel(i % 10, i / 10),
171                          bm2.getPixel(i % 10, i / 10));
172         }
173     }
174 
computePrePostMul(int alpha, int comp)175     private static int computePrePostMul(int alpha, int comp) {
176         if (alpha == 0) {
177             return 0;
178         }
179         int premul = Math.round(alpha * comp / 255.f);
180         int unpre = Math.round(255.0f * premul / alpha);
181         return unpre;
182     }
183 
184     @SmallTest
testSetPixelsWithNonOpaqueAlpha()185     public void testSetPixelsWithNonOpaqueAlpha() throws Exception {
186         int[] colors = new int[256];
187         for (int i = 0; i < 256; i++) {
188             colors[i] = (i << 24) | (0xFF << 16) | (0x80 << 8) | 0;
189         }
190 
191         Bitmap.Config config = Bitmap.Config.ARGB_8888;
192 
193         // create a bitmap with the color array specified
194         Bitmap bm1 = Bitmap.createBitmap(colors, 16, 16, config);
195 
196         // create a bitmap with no colors, but then call setPixels
197         Bitmap bm2 = Bitmap.createBitmap(16, 16, config);
198         bm2.setPixels(colors, 0, 16, 0, 0, 16, 16);
199 
200         // now check that we did a good job returning the unpremultiplied alpha
201         final int tolerance = 1;
202         for (int i = 0; i < 256; i++) {
203             int c0 = colors[i];
204             int c1 = bm1.getPixel(i % 16, i / 16);
205             int c2 = bm2.getPixel(i % 16, i / 16);
206 
207             // these two should always be identical
208             assertEquals("getPixel", c1, c2);
209 
210             // comparing the original (c0) with the returned color is tricky,
211             // since it gets premultiplied during the set(), and unpremultiplied
212             // by the get().
213             int a0 = Color.alpha(c0);
214             int a1 = Color.alpha(c1);
215             assertEquals("alpha", a0, a1);
216 
217             int r0 = Color.red(c0);
218             int r1 = Color.red(c1);
219             int rr = computePrePostMul(a0, r0);
220             assertTrue("red", Math.abs(rr - r1) <= tolerance);
221 
222             int g0 = Color.green(c0);
223             int g1 = Color.green(c1);
224             int gg = computePrePostMul(a0, g0);
225             assertTrue("green", Math.abs(gg - g1) <= tolerance);
226 
227             int b0 = Color.blue(c0);
228             int b1 = Color.blue(c1);
229             int bb = computePrePostMul(a0, b0);
230             assertTrue("blue", Math.abs(bb - b1) <= tolerance);
231 
232             if (false) {
233                 int cc = Color.argb(a0, rr, gg, bb);
234                 android.util.Log.d("skia", "original " + Integer.toHexString(c0) +
235                                 " set+get " + Integer.toHexString(c1) +
236                                " local " + Integer.toHexString(cc));
237             }
238         }
239     }
240 
241     @SmallTest
testWrapHardwareBufferWithSrgbColorSpace()242     public void testWrapHardwareBufferWithSrgbColorSpace() {
243         GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
244                 GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
245         Canvas canvas = buffer.lockCanvas();
246         canvas.drawColor(Color.YELLOW);
247         buffer.unlockCanvasAndPost(canvas);
248         Bitmap hardwareBitmap =
249                 Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer), null);
250         assertTrue(hardwareBitmap.isPremultiplied());
251         assertFalse(hardwareBitmap.isMutable());
252         assertEquals(ColorSpace.get(ColorSpace.Named.SRGB), hardwareBitmap.getColorSpace());
253     }
254 
255     @SmallTest
testWrapHardwareBufferWithDisplayP3ColorSpace()256     public void testWrapHardwareBufferWithDisplayP3ColorSpace() {
257         GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888,
258                 GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_SOFTWARE_MASK);
259         Canvas canvas = buffer.lockCanvas();
260         canvas.drawColor(Color.YELLOW);
261         buffer.unlockCanvasAndPost(canvas);
262         Bitmap hardwareBitmap = Bitmap.wrapHardwareBuffer(
263                 HardwareBuffer.createFromGraphicBuffer(buffer),
264                 ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
265         assertTrue(hardwareBitmap.isPremultiplied());
266         assertFalse(hardwareBitmap.isMutable());
267         assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), hardwareBitmap.getColorSpace());
268     }
269 
270     @SmallTest
testCopyWithDirectByteBuffer()271     public void testCopyWithDirectByteBuffer() {
272         // Initialize Bitmap
273         final int width = 2;
274         final int height = 2;
275         final int bytesPerPixel = 2;
276         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
277         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
278 
279         // Copy bytes to direct buffer, buffer is padded by fixed amount (pad bytes) either side
280         // of bitmap.
281         final int pad = 1;
282         final byte padValue = 0x5a;
283         final int bytesPerElement = 1;
284         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
285         ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize);
286 
287         // Write padding
288         directBuffer.put(0, padValue);
289         directBuffer.put(directBuffer.limit() - 1, padValue);
290 
291         // Copy bitmap
292         directBuffer.position(pad);
293         bm1.copyPixelsToBuffer(directBuffer);
294         assertEquals(directBuffer.position(),
295                      pad + width * height * bytesPerPixel / bytesPerElement);
296 
297         // Check padding
298         assertEquals(directBuffer.get(0), padValue);
299         assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
300 
301         // Create bitmap from direct buffer and check match.
302         directBuffer.position(pad);
303         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
304         bm2.copyPixelsFromBuffer(directBuffer);
305         assertTrue(bm2.sameAs(bm1));
306     }
307 
308     @SmallTest
testCopyWithDirectShortBuffer()309     public void testCopyWithDirectShortBuffer() {
310         // Initialize Bitmap
311         final int width = 2;
312         final int height = 2;
313         final int bytesPerPixel = 2;
314         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
315         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
316 
317         // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
318         // of bitmap.
319         final int pad = 1;
320         final short padValue = 0x55aa;
321         final int bytesPerElement = 2;
322         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
323         ShortBuffer directBuffer =
324                 ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asShortBuffer();
325 
326         // Write padding
327         directBuffer.put(0, padValue);
328         directBuffer.put(directBuffer.limit() - 1, padValue);
329 
330         // Copy bitmap
331         directBuffer.position(pad);
332         bm1.copyPixelsToBuffer(directBuffer);
333         assertEquals(directBuffer.position(),
334                      pad + width * height * bytesPerPixel / bytesPerElement);
335 
336         // Check padding
337         assertEquals(directBuffer.get(0), padValue);
338         assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
339 
340         // Create bitmap from heap buffer and check match.
341         directBuffer.position(pad);
342         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
343         bm2.copyPixelsFromBuffer(directBuffer);
344         assertTrue(bm2.sameAs(bm1));
345     }
346 
347     @SmallTest
testCopyWithDirectIntBuffer()348     public void testCopyWithDirectIntBuffer() {
349         // Initialize Bitmap
350         final int width = 2;
351         final int height = 2;
352         final int bytesPerPixel = 2;
353         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
354         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
355 
356         // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
357         // of bitmap.
358         final int pad = 1;
359         final int padValue = 0x55aa5a5a;
360         final int bytesPerElement = 4;
361         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
362         IntBuffer directBuffer =
363                 ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asIntBuffer();
364 
365         // Write padding
366         directBuffer.put(0, padValue);
367         directBuffer.put(directBuffer.limit() - 1, padValue);
368 
369         // Copy bitmap
370         directBuffer.position(pad);
371         bm1.copyPixelsToBuffer(directBuffer);
372         assertEquals(directBuffer.position(),
373                      pad + width * height * bytesPerPixel / bytesPerElement);
374 
375         // Check padding
376         assertEquals(directBuffer.get(0), padValue);
377         assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue);
378 
379         // Create bitmap from heap buffer and check match.
380         directBuffer.position(pad);
381         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
382         bm2.copyPixelsFromBuffer(directBuffer);
383         assertTrue(bm2.sameAs(bm1));
384     }
385 
386     @SmallTest
testCopyWithHeapByteBuffer()387     public void testCopyWithHeapByteBuffer() {
388         // Initialize Bitmap
389         final int width = 2;
390         final int height = 2;
391         final int bytesPerPixel = 2;
392         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
393         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
394 
395         // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
396         // of bitmap.
397         final int pad = 1;
398         final byte padValue = 0x5a;
399         final int bytesPerElement = 1;
400         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
401         ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize);
402 
403         // Write padding
404         heapBuffer.put(0, padValue);
405         heapBuffer.put(heapBuffer.limit() - 1, padValue);
406 
407         // Copy bitmap
408         heapBuffer.position(pad);
409         bm1.copyPixelsToBuffer(heapBuffer);
410         assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
411 
412         // Check padding
413         assertEquals(heapBuffer.get(0), padValue);
414         assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
415 
416         // Create bitmap from heap buffer and check match.
417         heapBuffer.position(pad);
418         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
419         bm2.copyPixelsFromBuffer(heapBuffer);
420         assertTrue(bm2.sameAs(bm1));
421     }
422 
423     @SmallTest
testCopyWithHeapShortBuffer()424     public void testCopyWithHeapShortBuffer() {
425         // Initialize Bitmap
426         final int width = 2;
427         final int height = 2;
428         final int bytesPerPixel = 2;
429         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
430         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
431 
432         // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
433         // of bitmap.
434         final int pad = 1;
435         final short padValue = 0x55aa;
436         final int bytesPerElement = 2;
437         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
438         ShortBuffer heapBuffer = ShortBuffer.allocate(bufferSize);
439 
440         // Write padding
441         heapBuffer.put(0, padValue);
442         heapBuffer.put(heapBuffer.limit() - 1, padValue);
443 
444         // Copy bitmap
445         heapBuffer.position(pad);
446         bm1.copyPixelsToBuffer(heapBuffer);
447         assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
448 
449         // Check padding
450         assertEquals(heapBuffer.get(0), padValue);
451         assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
452 
453         // Create bitmap from heap buffer and check match.
454         heapBuffer.position(pad);
455         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
456         bm2.copyPixelsFromBuffer(heapBuffer);
457         assertTrue(bm2.sameAs(bm1));
458     }
459 
460     @SmallTest
testCopyWithHeapIntBuffer()461     public void testCopyWithHeapIntBuffer() {
462         // Initialize Bitmap
463         final int width = 2;
464         final int height = 2;
465         final int bytesPerPixel = 2;
466         Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
467         bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2);
468 
469         // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side
470         // of bitmap.
471         final int pad = 1;
472         final int padValue = 0x55aa5a5a;
473         final int bytesPerElement = 4;
474         final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad;
475         IntBuffer heapBuffer = IntBuffer.allocate(bufferSize);
476 
477         // Write padding
478         heapBuffer.put(0, padValue);
479         heapBuffer.put(heapBuffer.limit() - 1, padValue);
480 
481         // Copy bitmap
482         heapBuffer.position(pad);
483         bm1.copyPixelsToBuffer(heapBuffer);
484         assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement);
485 
486         // Check padding
487         assertEquals(heapBuffer.get(0), padValue);
488         assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue);
489 
490         // Create bitmap from heap buffer and check match.
491         heapBuffer.position(pad);
492         Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
493         bm2.copyPixelsFromBuffer(heapBuffer);
494         assertTrue(bm2.sameAs(bm1));
495     }
496 }
497