Home | History | Annotate | Download | only in quickcontact
      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