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