• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.drawable;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.graphics.Bitmap;
22 import android.graphics.Canvas;
23 import android.graphics.Region;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Parcel;
27 import android.test.AndroidTestCase;
28 import android.util.Log;
29 
30 import androidx.test.filters.SmallTest;
31 
32 import com.android.frameworks.coretests.R;
33 
34 import java.io.ByteArrayOutputStream;
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 
40 public class IconTest extends AndroidTestCase {
41     public static final String TAG = IconTest.class.getSimpleName();
L(String s, Object... parts)42     public static void L(String s, Object... parts) {
43         Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
44     }
45 
46     @SmallTest
testWithBitmap()47     public void testWithBitmap() throws Exception {
48         final Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
49         final Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
50         final Bitmap bm3 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
51                 .getBitmap();
52 
53         final Canvas can1 = new Canvas(bm1);
54         can1.drawColor(0xFFFF0000);
55         final Canvas can2 = new Canvas(bm2);
56         can2.drawColor(0xFF00FF00);
57 
58         final Icon im1 = Icon.createWithBitmap(bm1);
59         final Icon im2 = Icon.createWithBitmap(bm2);
60         final Icon im3 = Icon.createWithBitmap(bm3);
61 
62         final Drawable draw1 = im1.loadDrawable(mContext);
63         final Drawable draw2 = im2.loadDrawable(mContext);
64         final Drawable draw3 = im3.loadDrawable(mContext);
65 
66         final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
67                 draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
68         final Bitmap test2 = Bitmap.createBitmap(draw2.getIntrinsicWidth(),
69                 draw2.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
70         final Bitmap test3 = Bitmap.createBitmap(draw3.getIntrinsicWidth(),
71                 draw3.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
72 
73         draw1.setBounds(0, 0, draw1.getIntrinsicWidth(), draw1.getIntrinsicHeight());
74         draw1.draw(new Canvas(test1));
75 
76         draw2.setBounds(0, 0, draw2.getIntrinsicWidth(), draw2.getIntrinsicHeight());
77         draw2.draw(new Canvas(test2));
78 
79         draw3.setBounds(0, 0, draw3.getIntrinsicWidth(), draw3.getIntrinsicHeight());
80         draw3.draw(new Canvas(test3));
81 
82         final File dir = getContext().getExternalFilesDir(null);
83         L("writing temp bitmaps to %s...", dir);
84 
85         bm1.compress(Bitmap.CompressFormat.PNG, 100,
86                 new FileOutputStream(new File(dir, "bitmap1-original.png")));
87         test1.compress(Bitmap.CompressFormat.PNG, 100,
88                 new FileOutputStream(new File(dir, "bitmap1-test.png")));
89         if (!equalBitmaps(bm1, test1)) {
90             findBitmapDifferences(bm1, test1);
91             fail("bitmap1 differs, check " + dir);
92         }
93 
94         bm2.compress(Bitmap.CompressFormat.PNG, 100,
95                 new FileOutputStream(new File(dir, "bitmap2-original.png")));
96         test2.compress(Bitmap.CompressFormat.PNG, 100,
97                 new FileOutputStream(new File(dir, "bitmap2-test.png")));
98         if (!equalBitmaps(bm2, test2)) {
99             findBitmapDifferences(bm2, test2);
100             fail("bitmap2 differs, check " + dir);
101         }
102 
103         bm3.compress(Bitmap.CompressFormat.PNG, 100,
104                 new FileOutputStream(new File(dir, "bitmap3-original.png")));
105         test3.compress(Bitmap.CompressFormat.PNG, 100,
106                 new FileOutputStream(new File(dir, "bitmap3-test.png")));
107         if (!equalBitmaps(bm3, test3)) {
108             findBitmapDifferences(bm3, test3);
109             fail("bitmap3 differs, check " + dir);
110         }
111     }
112 
113     @SmallTest
testScaleDownIfNecessary()114     public void testScaleDownIfNecessary() throws Exception {
115         final Bitmap bm = Bitmap.createBitmap(4321, 78, Bitmap.Config.ARGB_8888);
116         final Icon ic = Icon.createWithBitmap(bm);
117         ic.scaleDownIfNecessary(40, 20);
118 
119         assertThat(bm.getWidth()).isEqualTo(4321);
120         assertThat(bm.getHeight()).isEqualTo(78);
121 
122         assertThat(ic.getBitmap().getWidth()).isLessThan(41);
123         assertThat(ic.getBitmap().getHeight()).isLessThan(21);
124     }
125 
126     @SmallTest
testWithAdaptiveBitmap()127     public void testWithAdaptiveBitmap() throws Exception {
128         final Bitmap bm1 = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
129 
130         final Canvas can1 = new Canvas(bm1);
131         can1.drawColor(0xFFFF0000);
132 
133         final Icon im1 = Icon.createWithAdaptiveBitmap(bm1);
134 
135         final AdaptiveIconDrawable draw1 = (AdaptiveIconDrawable) im1.loadDrawable(mContext);
136 
137         final Bitmap test1 = Bitmap.createBitmap(
138             (int)(draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
139             (int)(draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
140             Bitmap.Config.ARGB_8888);
141 
142         draw1.setBounds(0, 0,
143             (int) (draw1.getIntrinsicWidth() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())),
144             (int) (draw1.getIntrinsicHeight() * (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())));
145         draw1.draw(new Canvas(test1));
146 
147         final File dir = getContext().getExternalFilesDir(null);
148         L("writing temp bitmaps to %s...", dir);
149 
150         bm1.compress(Bitmap.CompressFormat.PNG, 100,
151             new FileOutputStream(new File(dir, "adaptive-bitmap1-original.png")));
152         test1.compress(Bitmap.CompressFormat.PNG, 100,
153             new FileOutputStream(new File(dir, "adaptive-bitmap1-test.png")));
154         if (!equalBitmaps(bm1, test1, draw1.getSafeZone())) {
155             findBitmapDifferences(bm1, test1);
156             fail("adaptive bitmap1 differs, check " + dir);
157         }
158     }
159 
160     @SmallTest
testWithBitmapResource()161     public void testWithBitmapResource() throws Exception {
162         final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
163                 .getBitmap();
164 
165         final Icon im1 = Icon.createWithResource(getContext(), R.drawable.landscape);
166         final Drawable draw1 = im1.loadDrawable(mContext);
167         final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
168                 draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
169         draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
170         draw1.draw(new Canvas(test1));
171 
172         final File dir = getContext().getExternalFilesDir(null);
173         res1.compress(Bitmap.CompressFormat.PNG, 100,
174                 new FileOutputStream(new File(dir, "res1-original.png")));
175         test1.compress(Bitmap.CompressFormat.PNG, 100,
176                 new FileOutputStream(new File(dir, "res1-test.png")));
177         if (!equalBitmaps(res1, test1)) {
178             findBitmapDifferences(res1, test1);
179             fail("res1 differs, check " + dir);
180         }
181     }
182 
183     /**
184      * Icon resource test that ensures we can load and draw non-bitmaps. (In this case,
185      * stat_sys_adb is assumed, and asserted, to be a vector drawable.)
186      */
187     @SmallTest
testWithStatSysAdbResource()188     public void testWithStatSysAdbResource() throws Exception {
189         // establish reference bitmap
190         final float dp = getContext().getResources().getDisplayMetrics().density;
191         final int stat_sys_adb_width = (int) (24 * dp);
192         final int stat_sys_adb_height = (int) (24 * dp);
193 
194         final Drawable stat_sys_adb = getContext()
195                 .getDrawable(com.android.internal.R.drawable.stat_sys_adb);
196         if (!(stat_sys_adb instanceof VectorDrawable)) {
197             fail("stat_sys_adb is a " + stat_sys_adb.toString()
198                     + ", not a VectorDrawable; stat_sys_adb malformed");
199         }
200 
201         if (stat_sys_adb.getIntrinsicWidth() != stat_sys_adb_width) {
202             fail("intrinsic width of stat_sys_adb is not 24dp; stat_sys_adb malformed");
203         }
204         if (stat_sys_adb.getIntrinsicHeight() != stat_sys_adb_height) {
205             fail("intrinsic height of stat_sys_adb is not 24dp; stat_sys_adb malformed");
206         }
207         final Bitmap referenceBitmap = Bitmap.createBitmap(
208                 stat_sys_adb_width,
209                 stat_sys_adb_height,
210                 Bitmap.Config.ARGB_8888);
211         stat_sys_adb.setBounds(0, 0, stat_sys_adb_width, stat_sys_adb_height);
212         stat_sys_adb.draw(new Canvas(referenceBitmap));
213 
214         final Icon im1 = Icon.createWithResource(getContext(),
215                 com.android.internal.R.drawable.stat_sys_adb);
216         final Drawable draw1 = im1.loadDrawable(getContext());
217 
218         assertEquals(stat_sys_adb.getIntrinsicWidth(), draw1.getIntrinsicWidth());
219         assertEquals(stat_sys_adb.getIntrinsicHeight(), draw1.getIntrinsicHeight());
220         assertEquals(im1.getResId(), com.android.internal.R.drawable.stat_sys_adb);
221 
222         final Bitmap test1 = Bitmap.createBitmap(
223                 draw1.getIntrinsicWidth(),
224                 draw1.getIntrinsicHeight(),
225                 Bitmap.Config.ARGB_8888);
226         draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
227         draw1.draw(new Canvas(test1));
228 
229         final File dir = getContext().getExternalFilesDir(null);
230         test1.compress(Bitmap.CompressFormat.PNG, 100,
231                 new FileOutputStream(new File(dir, "testWithVectorDrawableResource-test.png")));
232         if (!equalBitmaps(referenceBitmap, test1)) {
233             findBitmapDifferences(referenceBitmap, test1);
234             fail("testWithFile: file1 differs, check " + dir);
235         }
236     }
237 
238     @SmallTest
testWithFile()239     public void testWithFile() throws Exception {
240         final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
241                 .getBitmap();
242         final File dir = getContext().getExternalFilesDir(null);
243         final File file1 = new File(dir, "file1-original.png");
244         bit1.compress(Bitmap.CompressFormat.PNG, 100,
245                 new FileOutputStream(file1));
246 
247         final Icon im1 = Icon.createWithFilePath(file1.toString());
248         final Drawable draw1 = im1.loadDrawable(mContext);
249         final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
250                 draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
251         draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
252         draw1.draw(new Canvas(test1));
253 
254         test1.compress(Bitmap.CompressFormat.PNG, 100,
255                 new FileOutputStream(new File(dir, "file1-test.png")));
256         if (!equalBitmaps(bit1, test1)) {
257             findBitmapDifferences(bit1, test1);
258             fail("testWithFile: file1 differs, check " + dir);
259         }
260     }
261 
262     @SmallTest
testAsync()263     public void testAsync() throws Exception {
264         final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
265                 .getBitmap();
266         final File dir = getContext().getExternalFilesDir(null);
267         final File file1 = new File(dir, "async-original.png");
268         bit1.compress(Bitmap.CompressFormat.PNG, 100,
269                 new FileOutputStream(file1));
270 
271         final Icon im1 = Icon.createWithFilePath(file1.toString());
272         final HandlerThread thd = new HandlerThread("testAsync");
273         thd.start();
274         final Handler h = new Handler(thd.getLooper());
275         L(TAG, "asyncTest: dispatching load to thread: " + thd);
276         im1.loadDrawableAsync(mContext, new Icon.OnDrawableLoadedListener() {
277             @Override
278             public void onDrawableLoaded(Drawable draw1) {
279                 L(TAG, "asyncTest: thread: loading drawable");
280                 L(TAG, "asyncTest: thread: loaded: %dx%d", draw1.getIntrinsicWidth(),
281                     draw1.getIntrinsicHeight());
282                 final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
283                         draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
284                 draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
285                 draw1.draw(new Canvas(test1));
286 
287                 try {
288                     test1.compress(Bitmap.CompressFormat.PNG, 100,
289                             new FileOutputStream(new File(dir, "async-test.png")));
290                 } catch (java.io.FileNotFoundException ex) {
291                     fail("couldn't create test file: " + ex);
292                 }
293                 if (!equalBitmaps(bit1, test1)) {
294                     findBitmapDifferences(bit1, test1);
295                     fail("testAsync: file1 differs, check " + dir);
296                 }
297             }
298         }, h);
299         L(TAG, "asyncTest: awaiting result");
300         Thread.sleep(500); // ;_;
301         assertTrue("async-test.png does not exist!", new File(dir, "async-test.png").exists());
302         L(TAG, "asyncTest: done");
303     }
304 
305     @SmallTest
testParcel()306     public void testParcel() throws Exception {
307         final Bitmap originalbits = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
308                 .getBitmap();
309 
310         final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
311                 originalbits.getWidth() * originalbits.getHeight() * 2); // guess 50% compression
312         originalbits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
313         final byte[] pngdata = ostream.toByteArray();
314 
315         L("starting testParcel; bitmap: %d bytes, PNG: %d bytes",
316                 originalbits.getByteCount(),
317                 pngdata.length);
318 
319         final File dir = getContext().getExternalFilesDir(null);
320         final File originalfile = new File(dir, "parcel-original.png");
321         new FileOutputStream(originalfile).write(pngdata);
322 
323         ArrayList<Icon> imgs = new ArrayList<>();
324         final Icon file1 = Icon.createWithFilePath(originalfile.getAbsolutePath());
325         imgs.add(file1);
326         final Icon bit1 = Icon.createWithBitmap(originalbits);
327         imgs.add(bit1);
328         final Icon data1 = Icon.createWithData(pngdata, 0, pngdata.length);
329         imgs.add(data1);
330         final Icon res1 = Icon.createWithResource(getContext(), R.drawable.landscape);
331         imgs.add(res1);
332 
333         ArrayList<Icon> test = new ArrayList<>();
334         final Parcel parcel = Parcel.obtain();
335         int pos = 0;
336         parcel.writeInt(imgs.size());
337         for (Icon img : imgs) {
338             img.writeToParcel(parcel, 0);
339             L("used %d bytes parceling: %s", parcel.dataPosition() - pos, img);
340             pos = parcel.dataPosition();
341         }
342 
343         parcel.setDataPosition(0); // rewind
344         final int N = parcel.readInt();
345         for (int i=0; i<N; i++) {
346             Icon img = Icon.CREATOR.createFromParcel(parcel);
347             L("test %d: read from parcel: %s", i, img);
348             final File testfile = new File(dir,
349                     String.format("parcel-test%02d.png", i));
350 
351             final Drawable draw1 = img.loadDrawable(mContext);
352             if (draw1 == null) {
353                 fail("null drawable from img: " + img);
354             }
355             final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(),
356                     draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
357             draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight());
358             draw1.draw(new Canvas(test1));
359 
360             try {
361                 test1.compress(Bitmap.CompressFormat.PNG, 100,
362                         new FileOutputStream(testfile));
363             } catch (java.io.FileNotFoundException ex) {
364                 fail("couldn't create test file " + testfile + ": " + ex);
365             }
366             if (!equalBitmaps(originalbits, test1)) {
367                 findBitmapDifferences(originalbits, test1);
368                 fail(testfile + " differs from original: " + originalfile);
369             }
370 
371         }
372     }
373 
374 
375     // ======== utils ========
376 
377     static final char[] GRADIENT = " .:;+=xX$#".toCharArray();
378     static float[] hsv = new float[3];
colorToChar(int color)379     static char colorToChar(int color) {
380         int sum = ((color >> 16) & 0xff)
381                 + ((color >> 8)  & 0xff)
382                 + ((color)       & 0xff);
383         return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)];
384     }
printBits(int[] a, int w, int h)385     static void printBits(int[] a, int w, int h) {
386         final StringBuilder sb = new StringBuilder();
387         for (int i=0; i<w; i++) {
388             for (int j=0; j<h; j++) {
389                 sb.append(colorToChar(a[i+w*j]));
390             }
391             sb.append('\n');
392         }
393         L(sb.toString());
394     }
printBits(Bitmap a)395     static void printBits(Bitmap a) {
396         final int w = a.getWidth();
397         final int h = a.getHeight();
398         int[] aPix = new int[w * h];
399         printBits(aPix, w, h);
400     }
equalBitmaps(Bitmap a, Bitmap b)401     boolean equalBitmaps(Bitmap a, Bitmap b) {
402         return equalBitmaps(a, b, null);
403     }
404 
equalBitmaps(Bitmap a, Bitmap b, Region region)405     boolean equalBitmaps(Bitmap a, Bitmap b, Region region) {
406         if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false;
407 
408         final int w = a.getWidth();
409         final int h = a.getHeight();
410         int[] aPix = new int[w * h];
411         int[] bPix = new int[w * h];
412 
413         if (region != null) {
414             for (int i = 0; i < w; i++) {
415                 for (int j = 0; j < h; j++) {
416                     if (region.contains(i, j) && a.getPixel(i, j) != b.getPixel(i, j)) {
417                         return false;
418                     }
419                 }
420             }
421             return true;
422         } else {
423             a.getPixels(aPix, 0, w, 0, 0, w, h);
424             b.getPixels(bPix, 0, w, 0, 0, w, h);
425             return Arrays.equals(aPix, bPix);
426         }
427     }
428 
findBitmapDifferences(Bitmap a, Bitmap b)429     void findBitmapDifferences(Bitmap a, Bitmap b) {
430         if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
431             L("different sizes: %dx%d vs %dx%d",
432                         a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight());
433             return;
434         }
435 
436         final int w = a.getWidth();
437         final int h = a.getHeight();
438         int[] aPix = new int[w * h];
439         int[] bPix = new int[w * h];
440 
441         a.getPixels(aPix, 0, w, 0, 0, w, h);
442         b.getPixels(bPix, 0, w, 0, 0, w, h);
443 
444         L("bitmap a (%dx%d)", w, h);
445         printBits(aPix, w, h);
446         L("bitmap b (%dx%d)", w, h);
447         printBits(bPix, w, h);
448 
449         StringBuffer sb = new StringBuffer("Different pixels: ");
450         for (int i=0; i<w; i++) {
451             for (int j=0; j<h; j++) {
452                 if (aPix[i+w*j] != bPix[i+w*j]) {
453                     sb.append(" ").append(i).append(",").append(j);
454                 }
455             }
456         }
457         L(sb.toString());
458     }
459 }
460