2 * Copyright (C) 2012 Andrew Neal
3 * Copyright (C) 2014 The CyanogenMod Project
4 * Licensed under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with the
6 * License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
8 * or agreed to in writing, software distributed under the License is
9 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
10 * KIND, either express or implied. See the License for the specific language
11 * governing permissions and limitations under the License.
14 package com.cyanogenmod.eleven.utils;
16 import android.graphics.Bitmap;
17 import android.graphics.Canvas;
18 import android.graphics.Paint;
21 * {@link Bitmap} specific helpers.
23 * @author Andrew Neal (andrewdneal@gmail.com)
25 public final class BitmapUtils {
27 /* Initial blur radius. */
28 private static final int DEFAULT_BLUR_RADIUS = 8;
30 /** This class is never instantiated */
31 private BitmapUtils() {
35 * Takes a bitmap and creates a new slightly blurry version of it.
37 * @param sentBitmap The {@link Bitmap} to blur.
38 * @return A blurred version of the given {@link Bitmap}.
40 public static final Bitmap createBlurredBitmap(final Bitmap sentBitmap) {
41 if (sentBitmap == null) {
45 // Stack Blur v1.0 from
46 // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
48 // Java Author: Mario Klingemann <mario at quasimondo.com>
49 // http://incubator.quasimondo.com
50 // created Feburary 29, 2004
51 // Android port : Yahel Bouaziz <yahel at kayenko.com>
52 // http://www.kayenko.com
53 // ported april 5th, 2012
55 // This is a compromise between Gaussian Blur and Box blur
56 // It creates much better looking blurs than Box Blur, but is
57 // 7x faster than my Gaussian Blur implementation.
59 // I called it Stack Blur because this describes best how this
60 // filter works internally: it creates a kind of moving stack
61 // of colors whilst scanning through the image. Thereby it
62 // just has to add one new block of color to the right side
63 // of the stack and remove the leftmost color. The remaining
64 // colors on the topmost layer of the stack are either added on
65 // or reduced by one, depending on if they are on the right or
66 // on the left side of the stack.
68 // If you are using this algorithm in your code please add
69 // the following line:
71 // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
73 final Bitmap mBitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
75 final int w = mBitmap.getWidth();
76 final int h = mBitmap.getHeight();
78 final int[] pix = new int[w * h];
79 mBitmap.getPixels(pix, 0, w, 0, 0, w, h);
84 final int div = DEFAULT_BLUR_RADIUS + DEFAULT_BLUR_RADIUS + 1;
86 final int r[] = new int[wh];
87 final int g[] = new int[wh];
88 final int b[] = new int[wh];
89 final int vmin[] = new int[Math.max(w, h)];
90 int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
92 int divsum = div + 1 >> 1;
94 final int dv[] = new int[256 * divsum];
95 for (i = 0; i < 256 * divsum; i++) {
101 final int[][] stack = new int[div][3];
106 final int r1 = DEFAULT_BLUR_RADIUS + 1;
107 int routsum, goutsum, boutsum;
108 int rinsum, ginsum, binsum;
110 for (y = 0; y < h; y++) {
111 rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
112 for (i = -DEFAULT_BLUR_RADIUS; i <= DEFAULT_BLUR_RADIUS; i++) {
113 p = pix[yi + Math.min(wm, Math.max(i, 0))];
114 sir = stack[i + DEFAULT_BLUR_RADIUS];
115 sir[0] = (p & 0xff0000) >> 16;
116 sir[1] = (p & 0x00ff00) >> 8;
117 sir[2] = p & 0x0000ff;
118 rbs = r1 - Math.abs(i);
119 rsum += sir[0] * rbs;
120 gsum += sir[1] * rbs;
121 bsum += sir[2] * rbs;
132 stackpointer = DEFAULT_BLUR_RADIUS;
134 for (x = 0; x < w; x++) {
144 stackstart = stackpointer - DEFAULT_BLUR_RADIUS + div;
145 sir = stack[stackstart % div];
152 vmin[x] = Math.min(x + DEFAULT_BLUR_RADIUS + 1, wm);
154 p = pix[yw + vmin[x]];
156 sir[0] = (p & 0xff0000) >> 16;
157 sir[1] = (p & 0x00ff00) >> 8;
158 sir[2] = p & 0x0000ff;
168 stackpointer = (stackpointer + 1) % div;
169 sir = stack[stackpointer % div];
183 for (x = 0; x < w; x++) {
184 rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
185 yp = -DEFAULT_BLUR_RADIUS * w;
186 for (i = -DEFAULT_BLUR_RADIUS; i <= DEFAULT_BLUR_RADIUS; i++) {
187 yi = Math.max(0, yp) + x;
189 sir = stack[i + DEFAULT_BLUR_RADIUS];
195 rbs = r1 - Math.abs(i);
216 stackpointer = DEFAULT_BLUR_RADIUS;
217 for (y = 0; y < h; y++) {
218 pix[yi] = 0xff000000 | dv[rsum] << 16 | dv[gsum] << 8 | dv[bsum];
224 stackstart = stackpointer - DEFAULT_BLUR_RADIUS + div;
225 sir = stack[stackstart % div];
232 vmin[y] = Math.min(y + r1, hm) * w;
248 stackpointer = (stackpointer + 1) % div;
249 sir = stack[stackpointer];
263 mBitmap.setPixels(pix, 0, w, 0, 0, w, h);
268 * This is only used when the launcher shortcut is created.
270 * @param bitmap The artist, album, genre, or playlist image that's going to
272 * @param size The new size.
273 * @return A {@link Bitmap} that has been resized and cropped for a launcher
276 public static final Bitmap resizeAndCropCenter(final Bitmap bitmap, final int size) {
277 final int w = bitmap.getWidth();
278 final int h = bitmap.getHeight();
279 if (w == size && h == size) {
283 final float mScale = (float)size / Math.min(w, h);
285 final Bitmap mTarget = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
286 final int mWidth = Math.round(mScale * bitmap.getWidth());
287 final int mHeight = Math.round(mScale * bitmap.getHeight());
288 final Canvas mCanvas = new Canvas(mTarget);
289 mCanvas.translate((size - mWidth) / 2f, (size - mHeight) / 2f);
290 mCanvas.scale(mScale, mScale);
291 final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
292 mCanvas.drawBitmap(bitmap, 0, 0, paint);