blob: d1edaac5f6d30fcfffd73ad9fdbb28a2a06de136 [file] [log] [blame]
/*
* Copyright (C) 2017 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.wallpaper.asset;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
/**
* Collection of static utility methods for decoding and processing Bitmaps.
*/
public class BitmapUtils {
private static final float DEFAULT_CENTER_ALIGNMENT = 0.5f;
// Suppress default constructor for noninstantiability.
private BitmapUtils() {
throw new AssertionError();
}
/**
* Calculates the highest subsampling factor to scale the source image to the target view without
* losing visible quality. Final result is based on powers of 2 because it should be set as
* BitmapOptions#inSampleSize.
*
* @param srcWidth Width of source image.
* @param srcHeight Height of source image.
* @param targetWidth Width of target view.
* @param targetHeight Height of target view.
* @return Highest subsampling factor as a power of 2.
*/
public static int calculateInSampleSize(
int srcWidth, int srcHeight, int targetWidth, int targetHeight) {
int shift = 0;
int halfHeight = srcHeight / 2;
int halfWidth = srcWidth / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both the result
// bitmap's height and width at least as large as the target height and width.
while (((halfHeight >> shift) >= targetHeight) && ((halfWidth >> shift) >= targetWidth)) {
shift++;
}
return 1 << shift;
}
/**
* Generates a hash code for the given bitmap. Computation starts with a nonzero prime number,
* then for the integer values of height, width, and a selection of pixel colors, multiplies the
* result by 31 and adds said integer value. Multiply by 31 because it is prime and conveniently 1
* less than 32 which is 2 ^ 5, allowing the VM to replace multiplication by a bit shift and
* subtraction for performance.
* <p>
* This method should be called off the UI thread.
*/
public static long generateHashCode(Bitmap bitmap) {
long result = 17;
if (bitmap == null) {
return result;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
result = 31 * result + width;
result = 31 * result + height;
// Traverse pixels exponentially so that hash code generation scales well with large images.
for (int x = 0; x < width; x = x * 2 + 1) {
for (int y = 0; y < height; y = y * 2 + 1) {
result = 31 * result + bitmap.getPixel(x, y);
}
}
return result;
}
/**
* Calculates horizontal alignment of the rect within the supplied dimensions.
*
* @return A float value between 0 and 1 specifying horizontal alignment; 0 for left-aligned, 0.5
* for horizontal center-aligned, and 1 for right-aligned.
*/
public static float calculateHorizontalAlignment(Point dimensions, Rect rect) {
int paddingLeft = rect.left;
int paddingRight = dimensions.x - rect.right;
int totalHorizontalPadding = paddingLeft + paddingRight;
// Zero horizontal padding means that there is no room to crop horizontally so we just fall
// back to a default center-alignment value.
return (totalHorizontalPadding == 0)
? DEFAULT_CENTER_ALIGNMENT
: paddingLeft / ((float) paddingLeft + paddingRight);
}
/**
* Calculates vertical alignment of the rect within the supplied dimensions.
*
* @return A float value between 0 and 1 specifying vertical alignment; 0 for top-aligned, 0.5 for
* vertical center-aligned, and 1 for bottom-aligned.
*/
public static float calculateVerticalAlignment(Point dimensions, Rect rect) {
int paddingTop = rect.top;
int paddingBottom = dimensions.y - rect.bottom;
int totalVerticalPadding = paddingTop + paddingBottom;
// Zero vertical padding means that there is no room to crop vertically so we just fall back to
// a default center-alignment value.
return (totalVerticalPadding == 0)
? DEFAULT_CENTER_ALIGNMENT
: paddingTop / ((float) paddingTop + paddingBottom);
}
}