Home | History | Annotate | Download | only in imagepool
      1 /*
      2  * Copyright (C) 2018 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 android.util.imagepool;
     18 
     19 import com.android.tools.layoutlib.annotations.Nullable;
     20 
     21 import android.util.imagepool.Bucket.BucketCreationMetaData;
     22 import android.util.imagepool.ImagePool.Image.Orientation;
     23 import android.util.imagepool.ImagePool.ImagePoolPolicy;
     24 
     25 import java.awt.image.BufferedImage;
     26 import java.util.Map;
     27 
     28 /* private package */ class ImagePoolHelper {
     29 
     30     @Nullable
     31     public static BucketCreationMetaData getBucketCreationMetaData(int w, int h, int type, ImagePoolPolicy poolPolicy, ImagePoolStats stats) {
     32         // Find the bucket sizes for both dimensions
     33         int widthBucket = -1;
     34         int heightBucket = -1;
     35         int index = 0;
     36 
     37         for (int bucketMinSize : poolPolicy.mBucketSizes) {
     38             if (widthBucket == -1 && w <= bucketMinSize) {
     39                 widthBucket = bucketMinSize;
     40 
     41                 if (heightBucket != -1) {
     42                     break;
     43                 }
     44             }
     45             if (heightBucket == -1 && h <= bucketMinSize) {
     46                 heightBucket = bucketMinSize;
     47 
     48                 if (widthBucket != -1) {
     49                     break;
     50                 }
     51             }
     52             ++index;
     53         }
     54 
     55         stats.recordBucketRequest(w, h);
     56 
     57         if (index >= poolPolicy.mNumberOfCopies.length) {
     58             return null;
     59         }
     60 
     61         // TODO: Apply orientation
     62 //        if (widthBucket < heightBucket) {
     63 //            return new BucketCreationMetaData(heightBucket, widthBucket, type, poolPolicy.mNumberOfCopies[index],
     64 //                    Orientation.CW_90, poolPolicy.mBucketMaxCacheSize);
     65 //        }
     66         return new BucketCreationMetaData(widthBucket, heightBucket, type, poolPolicy.mNumberOfCopies[index],
     67                 Orientation.NONE, poolPolicy.mBucketMaxCacheSize);
     68     }
     69 
     70     @Nullable
     71     public static BufferedImage getBufferedImage(
     72             Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
     73 
     74         // strongref is just for gc.
     75         BufferedImage strongRef = populateBucket(bucket, metaData, stats);
     76 
     77         // pool is too small to create the requested buffer.
     78         if (bucket.isEmpty()) {
     79             assert strongRef == null;
     80             return null;
     81         }
     82 
     83         // Go through the bucket of soft references to find the first buffer that's not null.
     84         // Even if gc is triggered here, strongref should survive.
     85         BufferedImage img = bucket.remove();
     86         while (img == null && !bucket.isEmpty()) {
     87             img = bucket.remove();
     88         }
     89 
     90         if (img == null && bucket.isEmpty()) {
     91             // Whole buffer was null. Recurse.
     92             return getBufferedImage(bucket, metaData, stats);
     93         }
     94         return img;
     95     }
     96 
     97     /**
     98      * Populate the bucket in greedy manner to avoid fragmentation.
     99      * Behaviour is controlled by {@link ImagePoolPolicy}.
    100      * Returns one strong referenced buffer to avoid getting results gc'd. Null if pool is not large
    101      * enough.
    102      */
    103     @Nullable
    104     private static BufferedImage populateBucket(
    105             Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
    106         if (!bucket.isEmpty()) {
    107             // If not empty no need to populate.
    108             return null;
    109         }
    110 
    111         BufferedImage strongRef = null;
    112         for (int i = 0; i < metaData.mNumberOfCopies; i++) {
    113             if (!stats.fitsMaxCacheSize(
    114                     metaData.mWidth, metaData.mHeight, metaData.mMaxCacheSize)) {
    115                 break;
    116             }
    117             strongRef =new BufferedImage(metaData.mWidth, metaData.mHeight,
    118                     metaData.mType);
    119             bucket.offer(strongRef);
    120             stats.recordBucketCreation(metaData.mWidth, metaData.mHeight);
    121         }
    122         return strongRef;
    123     }
    124 
    125     private static String toKey(int w, int h, int type) {
    126         return new StringBuilder()
    127                 .append(w)
    128                 .append('x')
    129                 .append(h)
    130                 .append(':')
    131                 .append(type)
    132                 .toString();
    133     }
    134 
    135     public static Bucket getBucket(Map<String, Bucket> map, BucketCreationMetaData metaData, ImagePoolPolicy mPolicy) {
    136         String key = toKey(metaData.mWidth, metaData.mHeight, metaData.mType);
    137         Bucket bucket = map.get(key);
    138         if (bucket == null) {
    139             bucket = new Bucket();
    140             map.put(key, bucket);
    141         }
    142         return bucket;
    143     }
    144 }