1 /* 2 * Copyright (C) 2017 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 package com.android.wallpaper.asset; 17 18 import android.graphics.Bitmap; 19 import android.graphics.Point; 20 import android.graphics.Rect; 21 22 import androidx.annotation.WorkerThread; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.io.InputStream; 27 28 /** 29 * Collection of static utility methods for decoding and processing Bitmaps. 30 */ 31 public class BitmapUtils { 32 private static final float DEFAULT_CENTER_ALIGNMENT = 0.5f; 33 34 // Suppress default constructor for noninstantiability. BitmapUtils()35 private BitmapUtils() { 36 throw new AssertionError(); 37 } 38 39 /** 40 * Calculates the highest subsampling factor to scale the source image to the target view without 41 * losing visible quality. Final result is based on powers of 2 because it should be set as 42 * BitmapOptions#inSampleSize. 43 * 44 * @param srcWidth Width of source image. 45 * @param srcHeight Height of source image. 46 * @param targetWidth Width of target view. 47 * @param targetHeight Height of target view. 48 * @return Highest subsampling factor as a power of 2. 49 */ calculateInSampleSize( int srcWidth, int srcHeight, int targetWidth, int targetHeight)50 public static int calculateInSampleSize( 51 int srcWidth, int srcHeight, int targetWidth, int targetHeight) { 52 int shift = 0; 53 int halfHeight = srcHeight / 2; 54 int halfWidth = srcWidth / 2; 55 56 // Calculate the largest inSampleSize value that is a power of 2 and keeps both the result 57 // bitmap's height and width at least as large as the target height and width. 58 while (((halfHeight >> shift) >= targetHeight) && ((halfWidth >> shift) >= targetWidth)) { 59 shift++; 60 } 61 62 return 1 << shift; 63 } 64 65 /** 66 * Generates a hash code for the given bitmap. Computation starts with a nonzero prime number, 67 * then for the integer values of height, width, and a selection of pixel colors, multiplies the 68 * result by 31 and adds said integer value. Multiply by 31 because it is prime and conveniently 1 69 * less than 32 which is 2 ^ 5, allowing the VM to replace multiplication by a bit shift and 70 * subtraction for performance. 71 * <p> 72 * This method should be called off the UI thread. 73 */ generateHashCode(Bitmap bitmap)74 public static long generateHashCode(Bitmap bitmap) { 75 long result = 17; 76 77 int width = bitmap.getWidth(); 78 int height = bitmap.getHeight(); 79 80 result = 31 * result + width; 81 result = 31 * result + height; 82 83 // Traverse pixels exponentially so that hash code generation scales well with large images. 84 for (int x = 0; x < width; x = x * 2 + 1) { 85 for (int y = 0; y < height; y = y * 2 + 1) { 86 result = 31 * result + bitmap.getPixel(x, y); 87 } 88 } 89 90 return result; 91 } 92 93 /** 94 * Calculates horizontal alignment of the rect within the supplied dimensions. 95 * 96 * @return A float value between 0 and 1 specifying horizontal alignment; 0 for left-aligned, 0.5 97 * for horizontal center-aligned, and 1 for right-aligned. 98 */ calculateHorizontalAlignment(Point dimensions, Rect rect)99 public static float calculateHorizontalAlignment(Point dimensions, Rect rect) { 100 int paddingLeft = rect.left; 101 int paddingRight = dimensions.x - rect.right; 102 int totalHorizontalPadding = paddingLeft + paddingRight; 103 // Zero horizontal padding means that there is no room to crop horizontally so we just fall 104 // back to a default center-alignment value. 105 return (totalHorizontalPadding == 0) 106 ? DEFAULT_CENTER_ALIGNMENT 107 : paddingLeft / ((float) paddingLeft + paddingRight); 108 } 109 110 /** 111 * Calculates vertical alignment of the rect within the supplied dimensions. 112 * 113 * @return A float value between 0 and 1 specifying vertical alignment; 0 for top-aligned, 0.5 for 114 * vertical center-aligned, and 1 for bottom-aligned. 115 */ calculateVerticalAlignment(Point dimensions, Rect rect)116 public static float calculateVerticalAlignment(Point dimensions, Rect rect) { 117 int paddingTop = rect.top; 118 int paddingBottom = dimensions.y - rect.bottom; 119 int totalVerticalPadding = paddingTop + paddingBottom; 120 // Zero vertical padding means that there is no room to crop vertically so we just fall back to 121 // a default center-alignment value. 122 return (totalVerticalPadding == 0) 123 ? DEFAULT_CENTER_ALIGNMENT 124 : paddingTop / ((float) paddingTop + paddingBottom); 125 } 126 127 /** 128 * Converts the bitmap into an input stream with 100% quality. 129 * 130 * Should not be called from the main thread. 131 */ 132 @WorkerThread bitmapToInputStream(Bitmap bitmap)133 public static InputStream bitmapToInputStream(Bitmap bitmap) { 134 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 135 if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) { 136 return new ByteArrayInputStream(outputStream.toByteArray()); 137 } else { 138 return null; 139 } 140 } 141 } 142