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 
17 package com.android.gallery3d.ui;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.RectF;
21 
22 import com.android.gallery3d.common.Utils;
23 import com.android.photos.data.GalleryBitmapPool;
24 import com.android.gallery3d.glrenderer.GLCanvas;
25 import 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.
34 public 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 
TiledScreenNail(Bitmap bitmap)55     public TiledScreenNail(Bitmap bitmap) {
56         mWidth = bitmap.getWidth();
57         mHeight = bitmap.getHeight();
58         mBitmap = bitmap;
59         mTexture = new TiledTexture(bitmap);
60     }
61 
TiledScreenNail(int width, int height)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 
setPlaceholderColor(int color)71     public static void setPlaceholderColor(int color) {
72         mPlaceholderColor = color;
73     }
74 
setSize(int width, int height)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.
combine(ScreenNail other)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 
updatePlaceholderSize(int width, int height)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
getWidth()121     public int getWidth() {
122         return mWidth;
123     }
124 
125     @Override
getHeight()126     public int getHeight() {
127         return mHeight;
128     }
129 
130     @Override
noDraw()131     public void noDraw() {
132     }
133 
134     @Override
recycle()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 
disableDrawPlaceholder()146     public static void disableDrawPlaceholder() {
147         mDrawPlaceholder = false;
148     }
149 
enableDrawPlaceholder()150     public static void enableDrawPlaceholder() {
151         mDrawPlaceholder = true;
152     }
153 
154     @Override
draw(GLCanvas canvas, int x, int y, int width, int height)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
draw(GLCanvas canvas, RectF source, RectF dest)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 
isAnimating()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 
getRatio()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 
isShowingPlaceholder()207     public boolean isShowingPlaceholder() {
208         return (mBitmap == null) || isAnimating();
209     }
210 
getTexture()211     public TiledTexture getTexture() {
212         return mTexture;
213     }
214 
setMaxSide(int size)215     public static void setMaxSide(int size) {
216         sMaxSide = size;
217     }
218 }
219