• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
18 package com.android.contacts.quickcontact;
19 
20 import android.graphics.Bitmap;
21 import android.graphics.Color;
22 import android.os.Trace;
23 
24 /**
25  * Utility class for determining whether Bitmaps contain a lot of white pixels in locations
26  * where QuickContactActivity will want to place white text or buttons.
27  *
28  * This class liberally considers bitmaps white. All constants are chosen with a small amount of
29  * experimentation. Despite a lack of rigour, this class successfully allows QuickContactsActivity
30  * to detect when Bitmap are obviously *not* white. Therefore, it is better than nothing.
31  */
32 public class WhitenessUtils {
33 
34     /**
35      * Analyze this amount of the top and bottom of the bitmap.
36      */
37     private static final float HEIGHT_PERCENT_ANALYZED = 0.2f;
38 
39     /**
40      * An image with more than this amount white, is considered to be a whitish image.
41      */
42     private static final float PROPORTION_WHITE_CUTOFF = 0.1f;
43 
44     private static final float THIRD = 0.33f;
45 
46     /**
47      * Colors with luma greater than this are considered close to white. This value is lower than
48      * the value used in Palette's ColorUtils, since we want to liberally declare images white.
49      */
50     private static final float LUMINANCE_OF_WHITE =  0.90f;
51 
52     /**
53      * Returns true if 20% of the image's top right corner is white, or 20% of the bottom
54      * of the image is white.
55      */
isBitmapWhiteAtTopOrBottom(Bitmap largeBitmap)56     public static boolean isBitmapWhiteAtTopOrBottom(Bitmap largeBitmap) {
57         Trace.beginSection("isBitmapWhiteAtTopOrBottom");
58         try {
59             final Bitmap smallBitmap = scaleBitmapDown(largeBitmap);
60 
61             final int[] rgbPixels = new int[smallBitmap.getWidth() * smallBitmap.getHeight()];
62             smallBitmap.getPixels(rgbPixels, 0, smallBitmap.getWidth(), 0, 0,
63                     smallBitmap.getWidth(), smallBitmap.getHeight());
64 
65             // look at top right corner of the bitmap
66             int whiteCount = 0;
67             for (int y = 0; y < smallBitmap.getHeight() * HEIGHT_PERCENT_ANALYZED; y++) {
68                 for (int x = (int) (smallBitmap.getWidth() * (1 - THIRD));
69                         x < smallBitmap.getWidth(); x++) {
70                     final int rgb = rgbPixels[y * smallBitmap.getWidth() + x];
71                     if (isWhite(rgb)) {
72                         whiteCount ++;
73                     }
74                 }
75             }
76             int totalPixels = (int) (smallBitmap.getHeight() * smallBitmap.getWidth()
77                     * THIRD * HEIGHT_PERCENT_ANALYZED);
78             if (whiteCount / (float) totalPixels > PROPORTION_WHITE_CUTOFF) {
79                 return true;
80             }
81 
82             // look at bottom portion of bitmap
83             whiteCount = 0;
84             for (int y = (int) (smallBitmap.getHeight() * (1 - HEIGHT_PERCENT_ANALYZED));
85                     y <  smallBitmap.getHeight(); y++) {
86                 for (int x = 0; x < smallBitmap.getWidth(); x++) {
87                     final int rgb = rgbPixels[y * smallBitmap.getWidth() + x];
88                     if (isWhite(rgb)) {
89                         whiteCount ++;
90                     }
91                 }
92             }
93 
94             totalPixels = (int) (smallBitmap.getHeight()
95                     * smallBitmap.getWidth() * HEIGHT_PERCENT_ANALYZED);
96 
97             return whiteCount / (float) totalPixels > PROPORTION_WHITE_CUTOFF;
98         } finally {
99             Trace.endSection();
100         }
101     }
102 
isWhite(int rgb)103     private static boolean isWhite(int rgb) {
104         return calculateXyzLuma(rgb) > LUMINANCE_OF_WHITE;
105     }
106 
calculateXyzLuma(int rgb)107     private static float calculateXyzLuma(int rgb) {
108         return (0.2126f * Color.red(rgb) +
109                 0.7152f * Color.green(rgb) +
110                 0.0722f * Color.blue(rgb)) / 255f;
111     }
112 
113     /**
114      * Scale down the bitmap in order to make color analysis faster. Taken from Palette.
115      */
scaleBitmapDown(Bitmap bitmap)116     private static Bitmap scaleBitmapDown(Bitmap bitmap) {
117         final int CALCULATE_BITMAP_MIN_DIMENSION = 100;
118         final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
119 
120         if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
121             // If the bitmap is small enough already, just return it
122             return bitmap;
123         }
124 
125         final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
126         return Bitmap.createScaledBitmap(bitmap,
127                 Math.round(bitmap.getWidth() * scaleRatio),
128                 Math.round(bitmap.getHeight() * scaleRatio),
129                 false);
130     }
131 }
132