TiledScreenNail.java revision d71a718afe02282153d86b78f6a44c4783203d54
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 TiledScreenNail implements ScreenNail {
34    @SuppressWarnings("unused")
35    private static final String TAG = "TiledScreenNail";
36
37    // The duration of the fading animation in milliseconds
38    private static final int DURATION = 180;
39
40    private static int sMaxSide = 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 long mAnimationStartTime = ANIMATION_NOT_NEEDED;
50
51    private Bitmap mBitmap;
52    private TiledTexture mTexture;
53
54    public TiledScreenNail(Bitmap bitmap) {
55        mWidth = bitmap.getWidth();
56        mHeight = bitmap.getHeight();
57        mBitmap = bitmap;
58        mTexture = new TiledTexture(bitmap);
59    }
60
61    public TiledScreenNail(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 = sMaxSide;
77            height = sMaxSide * 3 / 4;
78        }
79        float scale = Math.min(1, (float) sMaxSide / 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 TiledScreenNail)) {
97            recycle();
98            return other;
99        }
100
101        // Now both are TiledScreenNail. Move over the information about width,
102        // height, and Bitmap, then recycle the other.
103        TiledScreenNail newer = (TiledScreenNail) other;
104        mWidth = newer.mWidth;
105        mHeight = newer.mHeight;
106        if (newer.mTexture != null) {
107            recycleBitmap(MediaItem.getThumbPool(), mBitmap);
108            if (mTexture != null) mTexture.recycle();
109            mBitmap = newer.mBitmap;
110            mTexture = newer.mTexture;
111            newer.mBitmap = null;
112            newer.mTexture = null;
113        }
114        newer.recycle();
115        return this;
116    }
117
118    public void updatePlaceholderSize(int width, int height) {
119        if (mBitmap != null) return;
120        if (width == 0 || height == 0) return;
121        setSize(width, height);
122    }
123
124    @Override
125    public int getWidth() {
126        return mWidth;
127    }
128
129    @Override
130    public int getHeight() {
131        return mHeight;
132    }
133
134    @Override
135    public void noDraw() {
136    }
137
138    @Override
139    public void recycle() {
140        if (mTexture != null) {
141            mTexture.recycle();
142            mTexture = null;
143        }
144        recycleBitmap(MediaItem.getThumbPool(), mBitmap);
145        mBitmap = null;
146    }
147
148    public static void disableDrawPlaceholder() {
149        mDrawPlaceholder = false;
150    }
151
152    public static void enableDrawPlaceholder() {
153        mDrawPlaceholder = true;
154    }
155
156    @Override
157    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
158        if (mTexture == null || !mTexture.isReady()) {
159            if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
160                mAnimationStartTime = ANIMATION_NEEDED;
161            }
162            if(mDrawPlaceholder) {
163                canvas.fillRect(x, y, width, height, mPlaceholderColor);
164            }
165            return;
166        }
167
168        if (mAnimationStartTime == ANIMATION_NEEDED) {
169            mAnimationStartTime = AnimationTime.get();
170        }
171
172        if (isAnimating()) {
173            mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
174                    width, height);
175        } else {
176            mTexture.draw(canvas, x, y, width, height);
177        }
178    }
179
180    @Override
181    public void draw(GLCanvas canvas, RectF source, RectF dest) {
182        if (mTexture == null || !mTexture.isReady()) {
183            canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
184                    mPlaceholderColor);
185            return;
186        }
187
188        mTexture.draw(canvas, source, dest);
189    }
190
191    public boolean isAnimating() {
192        // The TiledTexture may not be uploaded completely yet.
193        // In that case, we count it as animating state and we will draw
194        // the placeholder in TileImageView.
195        if (mTexture == null || !mTexture.isReady()) return true;
196        if (mAnimationStartTime < 0) return false;
197        if (AnimationTime.get() - mAnimationStartTime >= DURATION) {
198            mAnimationStartTime = ANIMATION_DONE;
199            return false;
200        }
201        return true;
202    }
203
204    private float getRatio() {
205        float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
206        return Utils.clamp(1.0f - r, 0.0f, 1.0f);
207    }
208
209    public boolean isShowingPlaceholder() {
210        return (mBitmap == null) || isAnimating();
211    }
212
213    public TiledTexture getTexture() {
214        return mTexture;
215    }
216
217    public static void setMaxSide(int size) {
218        sMaxSide = size;
219    }
220}
221