1 /*
2  * Copyright (C) 2013 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 com.android.photos.shims;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.ColorFilter;
22 import android.graphics.Matrix;
23 import android.graphics.Paint;
24 import android.graphics.PixelFormat;
25 import android.graphics.Rect;
26 import android.graphics.drawable.Drawable;
27 
28 import com.android.gallery3d.data.MediaItem;
29 import com.android.gallery3d.ui.BitmapLoader;
30 import com.android.gallery3d.util.Future;
31 import com.android.gallery3d.util.FutureListener;
32 import com.android.gallery3d.util.ThreadPool;
33 import com.android.photos.data.GalleryBitmapPool;
34 
35 
36 public class BitmapJobDrawable extends Drawable implements Runnable {
37 
38     private ThumbnailLoader mLoader;
39     private MediaItem mItem;
40     private Bitmap mBitmap;
41     private Paint mPaint = new Paint();
42     private Matrix mDrawMatrix = new Matrix();
43     private int mRotation = 0;
44 
BitmapJobDrawable()45     public BitmapJobDrawable() {
46     }
47 
setMediaItem(MediaItem item)48     public void setMediaItem(MediaItem item) {
49         if (mItem == item) return;
50 
51         if (mLoader != null) {
52             mLoader.cancelLoad();
53         }
54         mItem = item;
55         if (mBitmap != null) {
56             GalleryBitmapPool.getInstance().put(mBitmap);
57             mBitmap = null;
58         }
59         if (mItem != null) {
60             // TODO: Figure out why ThumbnailLoader doesn't like to be re-used
61             mLoader = new ThumbnailLoader(this);
62             mLoader.startLoad();
63             mRotation = mItem.getRotation();
64         }
65         invalidateSelf();
66     }
67 
68     @Override
run()69     public void run() {
70         Bitmap bitmap = mLoader.getBitmap();
71         if (bitmap != null) {
72             mBitmap = bitmap;
73             updateDrawMatrix();
74         }
75     }
76 
77     @Override
onBoundsChange(Rect bounds)78     protected void onBoundsChange(Rect bounds) {
79         super.onBoundsChange(bounds);
80         updateDrawMatrix();
81     }
82 
83     @Override
draw(Canvas canvas)84     public void draw(Canvas canvas) {
85         Rect bounds = getBounds();
86         if (mBitmap != null) {
87             canvas.save();
88             canvas.clipRect(bounds);
89             canvas.concat(mDrawMatrix);
90             canvas.rotate(mRotation, bounds.centerX(), bounds.centerY());
91             canvas.drawBitmap(mBitmap, 0, 0, mPaint);
92             canvas.restore();
93         } else {
94             mPaint.setColor(0xFFCCCCCC);
95             canvas.drawRect(bounds, mPaint);
96         }
97     }
98 
updateDrawMatrix()99     private void updateDrawMatrix() {
100         Rect bounds = getBounds();
101         if (mBitmap == null || bounds.isEmpty()) {
102             mDrawMatrix.reset();
103             return;
104         }
105 
106         float scale;
107         float dx = 0, dy = 0;
108 
109         int dwidth = mBitmap.getWidth();
110         int dheight = mBitmap.getHeight();
111         int vwidth = bounds.width();
112         int vheight = bounds.height();
113 
114         // Calculates a matrix similar to ScaleType.CENTER_CROP
115         if (dwidth * vheight > vwidth * dheight) {
116             scale = (float) vheight / (float) dheight;
117             dx = (vwidth - dwidth * scale) * 0.5f;
118         } else {
119             scale = (float) vwidth / (float) dwidth;
120             dy = (vheight - dheight * scale) * 0.5f;
121         }
122 
123         mDrawMatrix.setScale(scale, scale);
124         mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
125         invalidateSelf();
126     }
127 
128     @Override
getIntrinsicWidth()129     public int getIntrinsicWidth() {
130         return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
131     }
132 
133     @Override
getIntrinsicHeight()134     public int getIntrinsicHeight() {
135         return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
136     }
137 
138     @Override
getOpacity()139     public int getOpacity() {
140         Bitmap bm = mBitmap;
141         return (bm == null || bm.hasAlpha() || mPaint.getAlpha() < 255) ?
142                 PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
143     }
144 
145     @Override
setAlpha(int alpha)146     public void setAlpha(int alpha) {
147         int oldAlpha = mPaint.getAlpha();
148         if (alpha != oldAlpha) {
149             mPaint.setAlpha(alpha);
150             invalidateSelf();
151         }
152     }
153 
154     @Override
setColorFilter(ColorFilter cf)155     public void setColorFilter(ColorFilter cf) {
156         mPaint.setColorFilter(cf);
157         invalidateSelf();
158     }
159 
160     private static class ThumbnailLoader extends BitmapLoader {
161         private static final ThreadPool sThreadPool = new ThreadPool(0, 2);
162         private BitmapJobDrawable mParent;
163 
ThumbnailLoader(BitmapJobDrawable parent)164         public ThumbnailLoader(BitmapJobDrawable parent) {
165             mParent = parent;
166         }
167 
168         @Override
submitBitmapTask(FutureListener<Bitmap> l)169         protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
170             return sThreadPool.submit(
171                     mParent.mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this);
172         }
173 
174         @Override
onLoadComplete(Bitmap bitmap)175         protected void onLoadComplete(Bitmap bitmap) {
176             mParent.scheduleSelf(mParent, 0);
177         }
178     }
179 
180 }
181