1fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu/*
2fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * Copyright (C) 2013 The Android Open Source Project
3fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu *
4fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * Licensed under the Apache License, Version 2.0 (the "License");
5fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * you may not use this file except in compliance with the License.
6fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * You may obtain a copy of the License at
7fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu *
8fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu *      http://www.apache.org/licenses/LICENSE-2.0
9fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu *
10fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * Unless required by applicable law or agreed to in writing, software
11fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * distributed under the License is distributed on an "AS IS" BASIS,
12fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * See the License for the specific language governing permissions and
14fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu * limitations under the License.
15fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu */
16fb6132b0b3079153fd1d15acf1dc3c6100aa2e00Bobby Georgescu
17d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckpackage com.android.photos.shims;
18d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
19d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.Bitmap;
20d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.Canvas;
21d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.ColorFilter;
22d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.Matrix;
23d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.Paint;
24d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.PixelFormat;
25d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.Rect;
26d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport android.graphics.drawable.Drawable;
27d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
28d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport com.android.gallery3d.data.MediaItem;
29d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport com.android.gallery3d.ui.BitmapLoader;
30d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport com.android.gallery3d.util.Future;
31d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport com.android.gallery3d.util.FutureListener;
32d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckimport com.android.gallery3d.util.ThreadPool;
332ff8d80018c27abdd475a7610ab3d684c4b33acbJohn Reckimport com.android.photos.data.GalleryBitmapPool;
34d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
35d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
36d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reckpublic class BitmapJobDrawable extends Drawable implements Runnable {
37d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
38d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private ThumbnailLoader mLoader;
39d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private MediaItem mItem;
40d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private Bitmap mBitmap;
41d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private Paint mPaint = new Paint();
42d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private Matrix mDrawMatrix = new Matrix();
43d3d2e2763bb4187a2dd3ac350d3a3c51d092890aJohn Reck    private int mRotation = 0;
44d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
45d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public BitmapJobDrawable() {
46d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
47d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
48d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public void setMediaItem(MediaItem item) {
49638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck        if (mItem == item) return;
50638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck
51d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (mLoader != null) {
52d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mLoader.cancelLoad();
53d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
54d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        mItem = item;
552ff8d80018c27abdd475a7610ab3d684c4b33acbJohn Reck        if (mBitmap != null) {
562ff8d80018c27abdd475a7610ab3d684c4b33acbJohn Reck            GalleryBitmapPool.getInstance().put(mBitmap);
572ff8d80018c27abdd475a7610ab3d684c4b33acbJohn Reck            mBitmap = null;
582ff8d80018c27abdd475a7610ab3d684c4b33acbJohn Reck        }
59638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck        if (mItem != null) {
60638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck            // TODO: Figure out why ThumbnailLoader doesn't like to be re-used
61638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck            mLoader = new ThumbnailLoader(this);
62638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck            mLoader.startLoad();
63d3d2e2763bb4187a2dd3ac350d3a3c51d092890aJohn Reck            mRotation = mItem.getRotation();
64638d9ec56c26d9f3cff55145ccbd20b35bb964f5John Reck        }
65d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        invalidateSelf();
66d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
67d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
68d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
69d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public void run() {
70d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        Bitmap bitmap = mLoader.getBitmap();
71d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (bitmap != null) {
72d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mBitmap = bitmap;
73d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            updateDrawMatrix();
74d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
75d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
76d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
77d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
78d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    protected void onBoundsChange(Rect bounds) {
79d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        super.onBoundsChange(bounds);
80d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        updateDrawMatrix();
81d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
82d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
83d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
84d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public void draw(Canvas canvas) {
85d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        Rect bounds = getBounds();
86d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (mBitmap != null) {
87d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.save();
88d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.clipRect(bounds);
89d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.concat(mDrawMatrix);
90d3d2e2763bb4187a2dd3ac350d3a3c51d092890aJohn Reck            canvas.rotate(mRotation, bounds.centerX(), bounds.centerY());
91d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
92d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.restore();
93d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        } else {
94d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mPaint.setColor(0xFFCCCCCC);
95d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            canvas.drawRect(bounds, mPaint);
96d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
97d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
98d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
99d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private void updateDrawMatrix() {
100d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        Rect bounds = getBounds();
101d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (mBitmap == null || bounds.isEmpty()) {
102d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mDrawMatrix.reset();
103d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            return;
104d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
105d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
106d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        float scale;
107d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        float dx = 0, dy = 0;
108d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
109d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        int dwidth = mBitmap.getWidth();
110d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        int dheight = mBitmap.getHeight();
111d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        int vwidth = bounds.width();
112d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        int vheight = bounds.height();
113d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
114d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        // Calculates a matrix similar to ScaleType.CENTER_CROP
115d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (dwidth * vheight > vwidth * dheight) {
116d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            scale = (float) vheight / (float) dheight;
117d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            dx = (vwidth - dwidth * scale) * 0.5f;
118d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        } else {
119d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            scale = (float) vwidth / (float) dwidth;
120d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            dy = (vheight - dheight * scale) * 0.5f;
121d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
122d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
123d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        mDrawMatrix.setScale(scale, scale);
124d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
125d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        invalidateSelf();
126d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
127d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
128d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
129d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public int getIntrinsicWidth() {
130d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
131d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
132d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
133d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
134d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public int getIntrinsicHeight() {
135d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
136d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
137d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
138d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
139d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public int getOpacity() {
140d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        Bitmap bm = mBitmap;
141d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        return (bm == null || bm.hasAlpha() || mPaint.getAlpha() < 255) ?
142d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
143d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
144d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
145d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
146d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public void setAlpha(int alpha) {
147d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        int oldAlpha = mPaint.getAlpha();
148d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        if (alpha != oldAlpha) {
149d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mPaint.setAlpha(alpha);
150d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            invalidateSelf();
151d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
152d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
153d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
154d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    @Override
155d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    public void setColorFilter(ColorFilter cf) {
156d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        mPaint.setColorFilter(cf);
157d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        invalidateSelf();
158d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
159d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
160d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    private static class ThumbnailLoader extends BitmapLoader {
161d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        private static final ThreadPool sThreadPool = new ThreadPool(0, 2);
162d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        private BitmapJobDrawable mParent;
163d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
164d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        public ThumbnailLoader(BitmapJobDrawable parent) {
165d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mParent = parent;
166d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
167d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
168d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        @Override
169d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
170d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            return sThreadPool.submit(
171d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck                    mParent.mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this);
172d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
173d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
174d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        @Override
175d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        protected void onLoadComplete(Bitmap bitmap) {
176d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck            mParent.scheduleSelf(mParent, 0);
177d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck        }
178d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck    }
179d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck
180d228222e5efca7df8c7f2fe49e5ec76696cbbc05John Reck}
181