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