1 /* 2 * Copyright (C) 2016 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.car.cluster.sample; 18 19 import static android.graphics.Color.blue; 20 import static android.graphics.Color.green; 21 import static android.graphics.Color.red; 22 23 import android.graphics.Bitmap; 24 import android.graphics.Bitmap.Config; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.Paint; 28 import android.graphics.PorterDuff.Mode; 29 import android.graphics.PorterDuffXfermode; 30 import android.graphics.Rect; 31 import android.graphics.RectF; 32 33 /** 34 * Utility functions to work with bitmaps. 35 */ 36 public class BitmapUtils { 37 38 // Non-transparent color to crop images using Xfermode. 39 private static final int OPAQUE_COLOR = 0xff424242; 40 41 /** 42 * Scales a bitmap while preserving the proportions such that both dimensions are the smallest 43 * values possible that are equal to or larger than the given dimensions. 44 * 45 * This function can be a few times as expensive as Bitmap.createScaledBitmap with 46 * filtering when downscaling, but it produces much nicer results. 47 * 48 * @param bm The bitmap to scale. 49 * @param width The desired width. 50 * @param height The desired height. 51 * @return The scaled bitmap, or the original bitmap if scaling was not necessary. 52 */ 53 public static Bitmap scaleBitmap(Bitmap bm, int width, int height) { 54 if (bm == null || (bm.getHeight() == height && bm.getWidth() == width)) { 55 return bm; 56 } 57 58 float heightScale = (float) height / bm.getHeight(); 59 float widthScale = (float) width / bm.getWidth(); 60 61 float scale = heightScale > widthScale ? heightScale : widthScale; 62 int scaleWidth = (int) Math.ceil(bm.getWidth() * scale); 63 int scaleHeight = (int) Math.ceil(bm.getHeight() * scale); 64 65 Bitmap scaledBm = bm; 66 // If you try to scale an image down too much in one go, you can end up with discontinuous 67 // interpolation. Therefore, if necessary, we scale the image to twice the desired size 68 // and do a second scaling to the desired size, which smooths jaggedness from the first go. 69 if (scale < .5f) { 70 scaledBm = Bitmap.createScaledBitmap(scaledBm, scaleWidth * 2, scaleHeight * 2, true); 71 } 72 73 if (scale != 1f) { 74 Bitmap newScaledBitmap = Bitmap 75 .createScaledBitmap(scaledBm, scaleWidth, scaleHeight, true); 76 if (scaledBm != bm) { 77 scaledBm.recycle(); 78 } 79 scaledBm = newScaledBitmap; 80 } 81 return scaledBm; 82 } 83 84 public static Bitmap squareCropBitmap(Bitmap bitmap) { 85 if (bitmap.getWidth() == bitmap.getHeight()) { 86 return bitmap; 87 } 88 89 int size = Math.min(bitmap.getWidth(), bitmap.getHeight()); 90 91 Bitmap output = Bitmap.createBitmap(size, 92 size, Config.ARGB_8888); 93 Canvas canvas = new Canvas(output); 94 95 int x = size < bitmap.getWidth() ? (bitmap.getWidth() - size ) / 2 : 0; 96 int y = size < bitmap.getHeight() ? (bitmap.getHeight() - size ) / 2 : 0; 97 Rect srcRect = new Rect(x, y, x + size, y + size); 98 Rect dstRect = new Rect(0, 0, output.getWidth(), output.getHeight()); 99 100 canvas.drawBitmap(bitmap, srcRect, dstRect, null); 101 return output; 102 } 103 104 public static Bitmap circleCropBitmap(Bitmap bitmap) { 105 bitmap = squareCropBitmap(bitmap); 106 Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), 107 bitmap.getHeight(), Config.ARGB_8888); 108 Canvas canvas = new Canvas(output); 109 110 Paint paint = new Paint(); 111 Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 112 113 paint.setAntiAlias(true); 114 canvas.drawARGB(0, 0, 0, 0); 115 paint.setColor(OPAQUE_COLOR); 116 canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, 117 bitmap.getWidth() / 2, paint); 118 paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 119 canvas.drawBitmap(bitmap, rect, rect, paint); 120 return output; 121 } 122 123 public static Bitmap generateNavManeuverIcon(int size, int bgColor, Bitmap maneuver) { 124 Bitmap bm = Bitmap.createBitmap(size, size, Config.ARGB_8888); 125 Canvas canvas = new Canvas(bm); 126 drawCircle(canvas, bgColor); 127 128 canvas.drawBitmap(maneuver, 129 (bm.getWidth() - maneuver.getWidth()) / 2, 130 (bm.getHeight() - maneuver.getHeight()) / 2, 131 new Paint(Paint.ANTI_ALIAS_FLAG)); 132 return bm; 133 } 134 135 public static Bitmap generateMediaIcon(int size, int bgColor, int fgColor) { 136 final float goldenRatio = 1.618f; 137 138 Bitmap bm = Bitmap.createBitmap(size, size, Config.ARGB_8888); 139 Canvas canvas = new Canvas(bm); 140 drawCircle(canvas, bgColor); 141 142 // Calculate column parameters relative to the size. 143 int bottom = (int) (size / goldenRatio); 144 int columnWidth = size / 17; 145 int columnSpace = columnWidth / 2; 146 int allColumnsWidth = columnWidth * 3 + columnSpace * 2; 147 int left = size / 2 - (allColumnsWidth) / 2; 148 149 Paint columnPaint = new Paint(); 150 columnPaint.setColor(fgColor); 151 columnPaint.setAntiAlias(true); 152 canvas.drawRect(new RectF(left, bottom - columnWidth * 2, left + columnWidth, bottom), 153 columnPaint); 154 155 left += columnWidth + columnSpace; 156 canvas.drawRect(new RectF(left, bottom - columnWidth * 4, left + columnWidth, bottom), 157 columnPaint); 158 159 left += columnWidth + columnSpace; 160 canvas.drawRect(new RectF(left, bottom - columnWidth * 3, left + columnWidth, bottom), 161 columnPaint); 162 163 return bm; 164 } 165 166 private static Canvas drawCircle(Canvas canvas, int bgColor) { 167 int size = canvas.getWidth(); 168 Paint p = new Paint(); 169 p.setAntiAlias(true); 170 p.setColor(bgColor); 171 canvas.drawCircle(size / 2, size / 2, (size / 2) - 1, p); 172 return canvas; 173 } 174 175 public static int[] getBaseColors(int color) { 176 return new int[] {red(color), green(color), blue(color)}; 177 } 178 179 /** 180 * Scales colors for given bitmap. This could be used as color filter for example. 181 */ 182 public static Bitmap scaleBitmapColors(Bitmap original, int minColor, int maxColor) { 183 int[] pixels = new int[original.getWidth() * original.getHeight()]; 184 original.getPixels(pixels, 0, original.getWidth(), 0, 0, 185 original.getWidth(), original.getHeight()); 186 187 int[] min = getBaseColors(minColor); 188 int[] max = getBaseColors(maxColor); 189 190 for (int i = 0; i < pixels.length; i++) { 191 int pixel = pixels[i]; 192 int[] colors = new int[] {red(pixel), green(pixel), blue(pixel)}; 193 194 for (int j = 0; j < 3; j++) { 195 colors[j] = (int)((colors[j] / 255.0) * (max[j] - min[j]) + min[j]); 196 } 197 198 pixels[i] = Color.rgb(colors[0], colors[1], colors[2]); 199 } 200 201 Bitmap bmp = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Config.ARGB_8888); 202 bmp.setPixels(pixels, 0, original.getWidth(), 0, 0, original.getWidth(), 203 original.getHeight()); 204 return bmp; 205 } 206 } 207