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.test.uibench.leanback;
17 
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Paint;
23 import android.os.AsyncTask;
24 import androidx.collection.LruCache;
25 import android.util.DisplayMetrics;
26 import android.widget.ImageView;
27 
28 /**
29  * This class simulates a typical Bitmap memory cache with up to 1.5 times of screen pixels.
30  * The sample bitmap is generated in worker threads in AsyncTask.THREAD_POOL_EXECUTOR.
31  * The class does not involve decoding, disk cache i/o, network i/o, as the test is mostly focusing
32  * on the graphics side.
33  * There will be two general use cases for cards in leanback test:
34  * 1. As a typical app, each card has its own id and load its own Bitmap, the test result will
35  *    include impact of texture upload.
36  * 2. All cards share same id/Bitmap and there wont be texture upload.
37  */
38 public class BitmapLoader {
39 
40     /**
41      * Caches bitmaps with bytes adds up to 1.5 x screen
42      * DO NOT CHANGE this defines baseline of test result.
43      */
44     static final float CACHE_SIZE_TO_SCREEN = 1.5f;
45     /**
46      * 4 bytes per pixel for RGBA_8888
47      */
48     static final int BYTES_PER_PIXEL = 4;
49 
50     static LruCache<Long, Bitmap> sLruCache;
51     static Paint sTextPaint = new Paint();
52 
53     static {
54         sTextPaint.setColor(Color.BLACK);
55     }
56 
57     /**
58      * get or initialize LruCache, the max is set to full screen pixels.
59      */
getLruCache(Context context)60     static LruCache<Long, Bitmap> getLruCache(Context context) {
61         if (sLruCache == null) {
62             DisplayMetrics metrics = context.getResources().getDisplayMetrics();
63             int width = metrics.widthPixels;
64             int height = metrics.heightPixels;
65             int maxBytes = (int) (width * height * BYTES_PER_PIXEL * CACHE_SIZE_TO_SCREEN);
66             sLruCache = new LruCache<Long, Bitmap>(maxBytes) {
67                 @Override
68                 protected int sizeOf(Long key, Bitmap value) {
69                     return value.getByteCount();
70                 }
71             };
72         }
73         return sLruCache;
74     }
75 
76     static class BitmapAsyncTask extends AsyncTask<Void, Void, Bitmap> {
77 
78         ImageView mImageView;
79         long mId;
80         int mWidth;
81         int mHeight;
82 
BitmapAsyncTask(ImageView view, long id, int width, int height)83         BitmapAsyncTask(ImageView view, long id, int width, int height) {
84             mImageView = view;
85             mId = id;
86             mImageView.setTag(this);
87             mWidth = width;
88             mHeight = height;
89         }
90 
91         @Override
doInBackground(Void... voids)92         protected Bitmap doInBackground(Void... voids) {
93             // generate a sample bitmap: white background and text showing id
94             Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
95             Canvas canvas = new Canvas(bitmap);
96             canvas.drawARGB(0xff, 0xff, 0xff, 0xff);
97             canvas.drawText(Long.toString(mId), 0f, mHeight / 2, sTextPaint);
98             canvas.setBitmap(null);
99             bitmap.prepareToDraw();
100             return bitmap;
101         }
102 
103         @Override
onCancelled()104         protected void onCancelled() {
105             if (mImageView.getTag() == this) {
106                 mImageView.setTag(null);
107             }
108         }
109 
110         @Override
onPostExecute(Bitmap bitmap)111         protected void onPostExecute(Bitmap bitmap) {
112             if (mImageView.getTag() == this) {
113                 mImageView.setTag(null);
114                 sLruCache.put(mId, bitmap);
115                 mImageView.setImageBitmap(bitmap);
116             }
117         }
118     }
119 
loadBitmap(ImageView view, long id, int width, int height)120     public static void loadBitmap(ImageView view, long id, int width, int height) {
121         Context context = view.getContext();
122         Bitmap bitmap = getLruCache(context).get(id);
123         if (bitmap != null) {
124             view.setImageBitmap(bitmap);
125             return;
126         }
127         new BitmapAsyncTask(view, id, width, height)
128                 .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
129     }
130 
cancel(ImageView view)131     public static void cancel(ImageView view) {
132         BitmapAsyncTask task = (BitmapAsyncTask) view.getTag();
133         if (task != null && task.mImageView == view) {
134             task.mImageView.setTag(null);
135             task.cancel(false);
136         }
137     }
138 
clear()139     public static void clear() {
140         if (sLruCache != null) {
141             sLruCache.evictAll();
142         }
143     }
144 }
145