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 getBucketCreationMetaData(int w, int h, int type, ImagePoolPolicy poolPolicy, ImagePoolStats stats)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 getBufferedImage( Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats)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 populateBucket( Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats)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 toKey(int w, int h, int type)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 getBucket(Map<String, Bucket> map, BucketCreationMetaData metaData, ImagePoolPolicy mPolicy)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 }