BitmapScreenNail.java revision 915c2c5b2c367df71599370613af0924bd7c4887
1/*
2 * Copyright (C) 2012 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.gallery3d.ui;
18
19import android.graphics.Bitmap;
20import android.graphics.RectF;
21
22import com.android.gallery3d.common.Utils;
23import com.android.gallery3d.data.BitmapPool;
24import com.android.gallery3d.data.MediaItem;
25
26// This is a ScreenNail wraps a Bitmap. There are some extra functions:
27//
28// - If we need to draw before the bitmap is available, we draw a rectange of
29// placeholder color (gray).
30//
31// - When the the bitmap is available, and we have drawn the placeholder color
32// before, we will do a fade-in animation.
33public class BitmapScreenNail implements ScreenNail {
34    @SuppressWarnings("unused")
35    private static final String TAG = "BitmapScreenNail";
36
37    // The duration of the fading animation in milliseconds
38    private static final int DURATION = 180;
39
40    private static final int MAX_SIDE = 640;
41
42    // These are special values for mAnimationStartTime
43    private static final long ANIMATION_NOT_NEEDED = -1;
44    private static final long ANIMATION_NEEDED = -2;
45    private static final long ANIMATION_DONE = -3;
46
47    private int mWidth;
48    private int mHeight;
49    private Bitmap mBitmap;
50    private BitmapTexture mTexture;
51    private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
52
53    public BitmapScreenNail(Bitmap bitmap) {
54        mWidth = bitmap.getWidth();
55        mHeight = bitmap.getHeight();
56        mBitmap = bitmap;
57        // We create mTexture lazily, so we don't incur the cost if we don't
58        // actually need it.
59    }
60
61    public BitmapScreenNail(int width, int height) {
62        setSize(width, height);
63    }
64
65    // This gets overridden by bitmap_screennail_placeholder
66    // in GalleryUtils.initialize
67    private static int mPlaceholderColor = 0xFF222222;
68    private static boolean mDrawPlaceholder = true;
69
70    public static void setPlaceholderColor(int color) {
71        mPlaceholderColor = color;
72    }
73
74    private void setSize(int width, int height) {
75        if (width == 0 || height == 0) {
76            width = 640;
77            height = 480;
78        }
79        float scale = Math.min(1, (float) MAX_SIDE / Math.max(width, height));
80        mWidth = Math.round(scale * width);
81        mHeight = Math.round(scale * height);
82    }
83
84    private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
85        if (pool == null || bitmap == null) return;
86        pool.recycle(bitmap);
87    }
88
89    // Combines the two ScreenNails.
90    // Returns the used one and recycle the unused one.
91    public ScreenNail combine(ScreenNail other) {
92        if (other == null) {
93            return this;
94        }
95
96        if (!(other instanceof BitmapScreenNail)) {
97            recycle();
98            return other;
99        }
100
101        // Now both are BitmapScreenNail. Move over the information about width,
102        // height, and Bitmap, then recycle the other.
103        BitmapScreenNail newer = (BitmapScreenNail) other;
104        mWidth = newer.mWidth;
105        mHeight = newer.mHeight;
106        if (newer.mBitmap != null) {
107            recycleBitmap(MediaItem.getThumbPool(), mBitmap);
108            mBitmap = newer.mBitmap;
109            newer.mBitmap = null;
110
111            if (mTexture != null) {
112                mTexture.recycle();
113                mTexture = null;
114            }
115        }
116
117        newer.recycle();
118        return this;
119    }
120
121    public void updatePlaceholderSize(int width, int height) {
122        if (mBitmap != null) return;
123        if (width == 0 || height == 0) return;
124        setSize(width, height);
125    }
126
127    @Override
128    public int getWidth() {
129        return mWidth;
130    }
131
132    @Override
133    public int getHeight() {
134        return mHeight;
135    }
136
137    @Override
138    public void noDraw() {
139    }
140
141    @Override
142    public void recycle() {
143        if (mTexture != null) {
144            mTexture.recycle();
145            mTexture = null;
146        }
147        recycleBitmap(MediaItem.getThumbPool(), mBitmap);
148        mBitmap = null;
149    }
150
151    public static void disableDrawPlaceholder() {
152        mDrawPlaceholder = false;
153    }
154
155    public static void enableDrawPlaceholder() {
156        mDrawPlaceholder = true;
157    }
158
159    @Override
160    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
161        if (mBitmap == null) {
162            if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
163                mAnimationStartTime = ANIMATION_NEEDED;
164            }
165            if(mDrawPlaceholder) {
166                canvas.fillRect(x, y, width, height, mPlaceholderColor);
167            }
168            return;
169        }
170
171        if (mTexture == null) {
172            mTexture = new BitmapTexture(mBitmap);
173        }
174
175        if (mAnimationStartTime == ANIMATION_NEEDED) {
176            mAnimationStartTime = now();
177        }
178
179        if (isAnimating()) {
180            canvas.drawMixed(mTexture, mPlaceholderColor, getRatio(), x, y,
181                    width, height);
182        } else {
183            mTexture.draw(canvas, x, y, width, height);
184        }
185    }
186
187    @Override
188    public void draw(GLCanvas canvas, RectF source, RectF dest) {
189        if (mBitmap == null) {
190            canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
191                    mPlaceholderColor);
192            return;
193        }
194
195        if (mTexture == null) {
196            mTexture = new BitmapTexture(mBitmap);
197        }
198
199        canvas.drawTexture(mTexture, source, dest);
200    }
201
202    public boolean isAnimating() {
203        if (mAnimationStartTime < 0) return false;
204        if (now() - mAnimationStartTime >= DURATION) {
205            mAnimationStartTime = ANIMATION_DONE;
206            return false;
207        }
208        return true;
209    }
210
211    private static long now() {
212        return AnimationTime.get();
213    }
214
215    private float getRatio() {
216        float r = (float)(now() - mAnimationStartTime) / DURATION;
217        return Utils.clamp(1.0f - r, 0.0f, 1.0f);
218    }
219
220    public boolean isShowingPlaceholder() {
221        return (mBitmap == null) || isAnimating();
222    }
223}
224