WhitenessUtils.java revision c55a684635178aac53d0e8bf2361f7c88335769d
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
18package com.android.contacts.quickcontact;
19
20import android.graphics.Bitmap;
21import android.graphics.Color;
22import 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 */
32public 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     */
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
103    private static boolean isWhite(int rgb) {
104        return calculateXyzLuma(rgb) > LUMINANCE_OF_WHITE;
105    }
106
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     */
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