1b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown/* 2b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Copyright (C) 2011 The Android Open Source Project 3b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * 4b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * you may not use this file except in compliance with the License. 6b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * You may obtain a copy of the License at 7b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * 8b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * http://www.apache.org/licenses/LICENSE-2.0 9b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * 10b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Unless required by applicable law or agreed to in writing, software 11b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * distributed under the License is distributed on an "AS IS" BASIS, 12b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * See the License for the specific language governing permissions and 14b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * limitations under the License. 15b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown */ 16b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 17b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownpackage com.android.phone; 18b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 19b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownimport android.graphics.Bitmap; 20b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownimport android.os.SystemClock; 21b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownimport android.os.SystemProperties; 22b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownimport android.util.Log; 23b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 24b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 25b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown/** 26b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Image effects used by the in-call UI. 27b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown */ 28b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brownpublic class BitmapUtils { 29b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final String TAG = "BitmapUtils"; 30b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final boolean DBG = 31b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 32b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 33b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown /** This class is never instantiated. */ 34b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private BitmapUtils() { 35b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 36b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 37b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 38b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Gaussian blur effect 39b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 40b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // gaussianBlur() and related methods are borrowed from 41b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // BackgroundUtils.java in the Music2 code (which itself was based on 42b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // code from the old Cooliris android Gallery app.) 43b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 44b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // TODO: possibly consider caching previously-generated blurred bitmaps; 45b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // see getAdaptedBitmap() and mAdaptedBitmapCache in the music app code. 46b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 47b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 48b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final int RED_MASK = 0xff0000; 49b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final int RED_MASK_SHIFT = 16; 50b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final int GREEN_MASK = 0x00ff00; 51b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final int GREEN_MASK_SHIFT = 8; 52b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static final int BLUE_MASK = 0x0000ff; 53b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 54b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown /** 55b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Creates a blurred version of the given Bitmap. 56b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * 57b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * @param bitmap the input bitmap, presumably a 96x96 pixel contact 58b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * thumbnail. 59b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown */ 60b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown public static Bitmap createBlurredBitmap(Bitmap bitmap) { 61b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("createBlurredBitmap()..."); 62b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown long startTime = SystemClock.uptimeMillis(); 63b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (bitmap == null) { 64b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown Log.w(TAG, "createBlurredBitmap: null bitmap"); 65b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown return null; 66b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 67b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 68b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("- input bitmap: " + bitmap.getWidth() + " x " + bitmap.getHeight()); 69b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 70b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // The bitmap we pass to gaussianBlur() needs to have a width 71b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // that's a power of 2, so scale up to 128x128. 72b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown final int scaledSize = 128; 73b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown bitmap = Bitmap.createScaledBitmap(bitmap, 74b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown scaledSize, scaledSize, 75b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown true /* filter */); 76b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("- after resize: " + bitmap.getWidth() + " x " + bitmap.getHeight()); 77b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 78b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown bitmap = gaussianBlur(bitmap); 79b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("- after blur: " + bitmap.getWidth() + " x " + bitmap.getHeight()); 80b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 81b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown long endTime = SystemClock.uptimeMillis(); 82b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("createBlurredBitmap() done (elapsed = " + (endTime - startTime) + " msec)"); 83b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown return bitmap; 84b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 85b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 86b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown /** 87b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * Apply a gaussian blur filter, and return a new (blurred) bitmap 88b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * that's the same size as the input bitmap. 89b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * 90b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown * @param source input bitmap, whose width must be a power of 2 91b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown */ 92b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown public static Bitmap gaussianBlur(Bitmap source) { 93b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int width = source.getWidth(); 94b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int height = source.getHeight(); 95b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown if (DBG) log("gaussianBlur(): input: " + width + " x " + height); 96b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 97b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Create a source and destination buffer for the image. 98b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int numPixels = width * height; 99b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int[] in = new int[numPixels]; 100b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int[] tmp = new int[numPixels]; 101b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 102b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Get the source pixels as 32-bit ARGB. 103b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown source.getPixels(in, 0, width, 0, 0, width, height); 104b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 105b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Gaussian is a separable kernel, so it is decomposed into a horizontal 106b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // and vertical pass. 107b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // The filter function applies the kernel across each row and transposes 108b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // the output. 109b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Hence we apply it twice to provide efficient horizontal and vertical 110b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // convolution. 111b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // The filter discards the alpha channel. 112b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown gaussianBlurFilter(in, tmp, width, height); 113b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown gaussianBlurFilter(tmp, in, width, height); 114b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 115b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Return a bitmap scaled to the desired size. 116b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown Bitmap filtered = Bitmap.createBitmap(in, width, height, Bitmap.Config.ARGB_8888); 117b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown source.recycle(); 118b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown return filtered; 119b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 120b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 121b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static void gaussianBlurFilter(int[] in, int[] out, int width, int height) { 122b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // This function is currently hardcoded to blur with RADIUS = 4. 123b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // (If you change RADIUS, you'll have to change the weights[] too.) 124b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown final int RADIUS = 4; 125b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown final int[] weights = { 13, 23, 32, 39, 42, 39, 32, 23, 13}; // Adds up to 256 126b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int inPos = 0; 127b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int widthMask = width - 1; // width must be a power of two. 128b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown for (int y = 0; y < height; ++y) { 129b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Compute the alpha value. 130b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int alpha = 0xff; 131b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Compute output values for the row. 132b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int outPos = y; 133b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown for (int x = 0; x < width; ++x) { 134b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int red = 0; 135b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int green = 0; 136b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int blue = 0; 137b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown for (int i = -RADIUS; i <= RADIUS; ++i) { 138b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int argb = in[inPos + (widthMask & (x + i))]; 139b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown int weight = weights[i+RADIUS]; 140b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown red += weight *((argb & RED_MASK) >> RED_MASK_SHIFT); 141b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown green += weight *((argb & GREEN_MASK) >> GREEN_MASK_SHIFT); 142b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown blue += weight *(argb & BLUE_MASK); 143b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 144b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Output the current pixel. 145b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown out[outPos] = (alpha << 24) | ((red >> 8) << RED_MASK_SHIFT) 146b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown | ((green >> 8) << GREEN_MASK_SHIFT) 147b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown | (blue >> 8); 148b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown outPos += height; 149b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 150b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown inPos += width; 151b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 152b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 153b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 154b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 155b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // Debugging 156b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown // 157b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown 158b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown private static void log(String msg) { 159b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown Log.d(TAG, msg); 160b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown } 161b424ca2073702aff4ac1d9297d8bbe73ee4cd8b9David Brown} 162