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 */
16package com.android.car.apps.common;
17
18import android.graphics.Bitmap;
19import android.util.Log;
20
21public class BitmapUtils {
22    private static final String TAG = "BitmapUtils";
23
24    /**
25     * Scales a bitmap while preserving the proportions such that both dimensions are the smallest
26     * values possible that are equal to or larger than the given dimensions.
27     *
28     * This function can be a few times as expensive as Bitmap.createScaledBitmap with
29     * filtering when downscaling, but it produces much nicer results.
30     *
31     * @param bm The bitmap to scale.
32     * @param width The desired width.
33     * @param height The desired height.
34     * @return The scaled bitmap, or the original bitmap if scaling was not necessary.
35     */
36    public static Bitmap scaleBitmap(Bitmap bm, int width, int height) {
37        if (bm == null || (bm.getHeight() == height && bm.getWidth() == width)) {
38            return bm;
39        }
40
41        float heightScale = 1f;
42        if (bm.getHeight() > height) {
43            heightScale = (float) height / bm.getHeight();
44        }
45        float widthScale = 1f;
46        if (bm.getWidth() > width) {
47            widthScale = (float) width / bm.getWidth();
48        }
49        float scale = heightScale > widthScale ? heightScale : widthScale;
50        int scaleWidth = (int) Math.ceil(bm.getWidth() * scale);
51        int scaleHeight = (int) Math.ceil(bm.getHeight() * scale);
52
53        Bitmap scaledBm = bm;
54        // If you try to scale an image down too much in one go, you can end up with discontinuous
55        // interpolation. Therefore, if necessary, we scale the image to twice the desired size
56        // and do a second scaling to the desired size, which smooths jaggedness from the first go.
57        if (scale < .5f) {
58            scaledBm = Bitmap.createScaledBitmap(scaledBm, scaleWidth * 2, scaleHeight * 2, true);
59        }
60
61        if (scale != 1f) {
62            Bitmap newScaledBitmap = Bitmap
63                    .createScaledBitmap(scaledBm, scaleWidth, scaleHeight, true);
64            if (scaledBm != bm) {
65                scaledBm.recycle();
66            }
67            scaledBm = newScaledBitmap;
68        }
69        return scaledBm;
70    }
71
72    /**
73     * Crops the given bitmap to a centered rectangle of the given dimensions.
74     *
75     * @param bm the bitmap to crop.
76     * @param width the width to crop to.
77     * @param height the height to crop to.
78     * @return The cropped bitmap, or the original if no cropping was necessary.
79     */
80    public static Bitmap cropBitmap(Bitmap bm, int width, int height) {
81        if (bm == null) {
82            return bm;
83        }
84        if (bm.getHeight() < height || bm.getWidth() < width) {
85            if (Log.isLoggable(TAG, Log.INFO)) {
86                Log.i(TAG, String.format(
87                        "Can't crop bitmap to larger dimensions (%d, %d) -> (%d, %d).",
88                        bm.getWidth(), bm.getHeight(), width, height));
89            }
90            return bm;
91        }
92        int x = (bm.getWidth() - width) / 2;
93        int y =(bm.getHeight() - height) / 2;
94        return Bitmap.createBitmap(bm, x, y, width, height);
95    }
96}
97