Home | History | Annotate | Download | only in util
      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 package com.android.internal.util;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Bitmap.Config;
     21 import android.graphics.Canvas;
     22 import android.graphics.Matrix;
     23 import android.graphics.Paint;
     24 import android.graphics.PorterDuff;
     25 import android.graphics.drawable.BitmapDrawable;
     26 import android.graphics.drawable.Drawable;
     27 
     28 /**
     29  * Utility class for image analysis and processing.
     30  *
     31  * @hide
     32  */
     33 public class ImageUtils {
     34 
     35     // Amount (max is 255) that two channels can differ before the color is no longer "gray".
     36     private static final int TOLERANCE = 20;
     37 
     38     // Alpha amount for which values below are considered transparent.
     39     private static final int ALPHA_TOLERANCE = 50;
     40 
     41     // Size of the smaller bitmap we're actually going to scan.
     42     private static final int COMPACT_BITMAP_SIZE = 64; // pixels
     43 
     44     private int[] mTempBuffer;
     45     private Bitmap mTempCompactBitmap;
     46     private Canvas mTempCompactBitmapCanvas;
     47     private Paint mTempCompactBitmapPaint;
     48     private final Matrix mTempMatrix = new Matrix();
     49 
     50     /**
     51      * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
     52      * gray".
     53      *
     54      * Instead of scanning every pixel in the bitmap, we first resize the bitmap to no more than
     55      * COMPACT_BITMAP_SIZE^2 pixels using filtering. The hope is that any non-gray color elements
     56      * will survive the squeezing process, contaminating the result with color.
     57      */
     58     public boolean isGrayscale(Bitmap bitmap) {
     59         int height = bitmap.getHeight();
     60         int width = bitmap.getWidth();
     61 
     62         // shrink to a more manageable (yet hopefully no more or less colorful) size
     63         if (height > COMPACT_BITMAP_SIZE || width > COMPACT_BITMAP_SIZE) {
     64             if (mTempCompactBitmap == null) {
     65                 mTempCompactBitmap = Bitmap.createBitmap(
     66                         COMPACT_BITMAP_SIZE, COMPACT_BITMAP_SIZE, Bitmap.Config.ARGB_8888
     67                 );
     68                 mTempCompactBitmapCanvas = new Canvas(mTempCompactBitmap);
     69                 mTempCompactBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     70                 mTempCompactBitmapPaint.setFilterBitmap(true);
     71             }
     72             mTempMatrix.reset();
     73             mTempMatrix.setScale(
     74                     (float) COMPACT_BITMAP_SIZE / width,
     75                     (float) COMPACT_BITMAP_SIZE / height,
     76                     0, 0);
     77             mTempCompactBitmapCanvas.drawColor(0, PorterDuff.Mode.SRC); // select all, erase
     78             mTempCompactBitmapCanvas.drawBitmap(bitmap, mTempMatrix, mTempCompactBitmapPaint);
     79             bitmap = mTempCompactBitmap;
     80             width = height = COMPACT_BITMAP_SIZE;
     81         }
     82 
     83         final int size = height*width;
     84         ensureBufferSize(size);
     85         bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
     86         for (int i = 0; i < size; i++) {
     87             if (!isGrayscale(mTempBuffer[i])) {
     88                 return false;
     89             }
     90         }
     91         return true;
     92     }
     93 
     94     /**
     95      * Makes sure that {@code mTempBuffer} has at least length {@code size}.
     96      */
     97     private void ensureBufferSize(int size) {
     98         if (mTempBuffer == null || mTempBuffer.length < size) {
     99             mTempBuffer = new int[size];
    100         }
    101     }
    102 
    103     /**
    104      * Classifies a color as grayscale or not. Grayscale here means "very close to a perfect
    105      * gray"; if all three channels are approximately equal, this will return true.
    106      *
    107      * Note that really transparent colors are always grayscale.
    108      */
    109     public static boolean isGrayscale(int color) {
    110         int alpha = 0xFF & (color >> 24);
    111         if (alpha < ALPHA_TOLERANCE) {
    112             return true;
    113         }
    114 
    115         int r = 0xFF & (color >> 16);
    116         int g = 0xFF & (color >> 8);
    117         int b = 0xFF & color;
    118 
    119         return Math.abs(r - g) < TOLERANCE
    120                 && Math.abs(r - b) < TOLERANCE
    121                 && Math.abs(g - b) < TOLERANCE;
    122     }
    123 
    124     /**
    125      * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
    126      */
    127     public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
    128             int maxHeight) {
    129         if (drawable == null) {
    130             return null;
    131         }
    132         int originalWidth = drawable.getIntrinsicWidth();
    133         int originalHeight = drawable.getIntrinsicHeight();
    134 
    135         if ((originalWidth <= maxWidth) && (originalHeight <= maxHeight) &&
    136                 (drawable instanceof BitmapDrawable)) {
    137             return ((BitmapDrawable) drawable).getBitmap();
    138         }
    139         if (originalHeight <= 0 || originalWidth <= 0) {
    140             return null;
    141         }
    142 
    143         // create a new bitmap, scaling down to fit the max dimensions of
    144         // a large notification icon if necessary
    145         float ratio = Math.min((float) maxWidth / (float) originalWidth,
    146                 (float) maxHeight / (float) originalHeight);
    147         ratio = Math.min(1.0f, ratio);
    148         int scaledWidth = (int) (ratio * originalWidth);
    149         int scaledHeight = (int) (ratio * originalHeight);
    150         Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
    151 
    152         // and paint our app bitmap on it
    153         Canvas canvas = new Canvas(result);
    154         drawable.setBounds(0, 0, scaledWidth, scaledHeight);
    155         drawable.draw(canvas);
    156 
    157         return result;
    158     }
    159 }
    160