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 */ 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