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