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.module;
17 
18 import android.graphics.Bitmap;
19 import android.graphics.Rect;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.util.Log;
23 
24 import com.android.wallpaper.asset.Asset;
25 import com.android.wallpaper.asset.Asset.BitmapReceiver;
26 
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 
30 /**
31  * Default implementation of BitmapCropper, which actually crops and scales bitmaps.
32  */
33 public class DefaultBitmapCropper implements BitmapCropper {
34     private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor();
35     private static final String TAG = "DefaultBitmapCropper";
36     private static final boolean FILTER_SCALED_BITMAP = true;
37 
38     @Override
cropAndScaleBitmap(Asset asset, float scale, Rect cropRect, boolean isRtl, Callback callback)39     public void cropAndScaleBitmap(Asset asset, float scale, Rect cropRect,
40             boolean isRtl, Callback callback) {
41         // Crop rect in pixels of source image.
42         Rect scaledCropRect = new Rect(
43                 (int) Math.floor((float) cropRect.left / scale),
44                 (int) Math.floor((float) cropRect.top / scale),
45                 (int) Math.floor((float) cropRect.right / scale),
46                 (int) Math.floor((float) cropRect.bottom / scale));
47 
48         asset.decodeBitmapRegion(scaledCropRect, cropRect.width(), cropRect.height(), isRtl,
49                 new BitmapReceiver() {
50                     @Override
51                     public void onBitmapDecoded(Bitmap bitmap) {
52                         if (bitmap == null) {
53                             callback.onError(null);
54                             return;
55                         }
56                         // Asset provides a bitmap which is appropriate for the target width &
57                         // height, but since it does not guarantee an exact size we need to fit
58                         // the bitmap to the cropRect.
59                         sExecutorService.execute(() -> {
60                             try {
61                                 // Fit bitmap to exact dimensions of crop rect.
62                                 Bitmap result = Bitmap.createScaledBitmap(
63                                         bitmap,
64                                         cropRect.width(),
65                                         cropRect.height(),
66                                         FILTER_SCALED_BITMAP);
67                                 new Handler(Looper.getMainLooper()).post(
68                                         () -> callback.onBitmapCropped(result));
69                             } catch (OutOfMemoryError e) {
70                                 Log.w(TAG,
71                                         "Not enough memory to fit the final cropped and "
72                                                 + "scaled bitmap to size", e);
73                                 new Handler(Looper.getMainLooper()).post(() -> callback.onError(e));
74                             }
75                         });
76                     }
77                 });
78     }
79 }
80