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