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