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
17package com.android.photos.shims;
18
19import android.graphics.Bitmap;
20import android.graphics.Canvas;
21import android.graphics.ColorFilter;
22import android.graphics.Matrix;
23import android.graphics.Paint;
24import android.graphics.PixelFormat;
25import android.graphics.Rect;
26import android.graphics.drawable.Drawable;
27
28import com.android.gallery3d.data.MediaItem;
29import com.android.gallery3d.ui.BitmapLoader;
30import com.android.gallery3d.util.Future;
31import com.android.gallery3d.util.FutureListener;
32import com.android.gallery3d.util.ThreadPool;
33import com.android.photos.data.GalleryBitmapPool;
34
35
36public 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
45    public BitmapJobDrawable() {
46    }
47
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
69    public void run() {
70        Bitmap bitmap = mLoader.getBitmap();
71        if (bitmap != null) {
72            mBitmap = bitmap;
73            updateDrawMatrix();
74        }
75    }
76
77    @Override
78    protected void onBoundsChange(Rect bounds) {
79        super.onBoundsChange(bounds);
80        updateDrawMatrix();
81    }
82
83    @Override
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
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
129    public int getIntrinsicWidth() {
130        return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
131    }
132
133    @Override
134    public int getIntrinsicHeight() {
135        return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
136    }
137
138    @Override
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
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
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
164        public ThumbnailLoader(BitmapJobDrawable parent) {
165            mParent = parent;
166        }
167
168        @Override
169        protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
170            return sThreadPool.submit(
171                    mParent.mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this);
172        }
173
174        @Override
175        protected void onLoadComplete(Bitmap bitmap) {
176            mParent.scheduleSelf(mParent, 0);
177        }
178    }
179
180}
181