/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.photos.shims; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.ui.BitmapLoader; import com.android.gallery3d.util.Future; import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.ThreadPool; import com.android.photos.data.GalleryBitmapPool; public class BitmapJobDrawable extends Drawable implements Runnable { private ThumbnailLoader mLoader; private MediaItem mItem; private Bitmap mBitmap; private Paint mPaint = new Paint(); private Matrix mDrawMatrix = new Matrix(); private int mRotation = 0; public BitmapJobDrawable() { } public void setMediaItem(MediaItem item) { if (mItem == item) return; if (mLoader != null) { mLoader.cancelLoad(); } mItem = item; if (mBitmap != null) { GalleryBitmapPool.getInstance().put(mBitmap); mBitmap = null; } if (mItem != null) { // TODO: Figure out why ThumbnailLoader doesn't like to be re-used mLoader = new ThumbnailLoader(this); mLoader.startLoad(); mRotation = mItem.getRotation(); } invalidateSelf(); } @Override public void run() { Bitmap bitmap = mLoader.getBitmap(); if (bitmap != null) { mBitmap = bitmap; updateDrawMatrix(); } } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); updateDrawMatrix(); } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); if (mBitmap != null) { canvas.save(); canvas.clipRect(bounds); canvas.concat(mDrawMatrix); canvas.rotate(mRotation, bounds.centerX(), bounds.centerY()); canvas.drawBitmap(mBitmap, 0, 0, mPaint); canvas.restore(); } else { mPaint.setColor(0xFFCCCCCC); canvas.drawRect(bounds, mPaint); } } private void updateDrawMatrix() { Rect bounds = getBounds(); if (mBitmap == null || bounds.isEmpty()) { mDrawMatrix.reset(); return; } float scale; float dx = 0, dy = 0; int dwidth = mBitmap.getWidth(); int dheight = mBitmap.getHeight(); int vwidth = bounds.width(); int vheight = bounds.height(); // Calculates a matrix similar to ScaleType.CENTER_CROP if (dwidth * vheight > vwidth * dheight) { scale = (float) vheight / (float) dheight; dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; dy = (vheight - dheight * scale) * 0.5f; } mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); invalidateSelf(); } @Override public int getIntrinsicWidth() { return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); } @Override public int getIntrinsicHeight() { return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); } @Override public int getOpacity() { Bitmap bm = mBitmap; return (bm == null || bm.hasAlpha() || mPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; } @Override public void setAlpha(int alpha) { int oldAlpha = mPaint.getAlpha(); if (alpha != oldAlpha) { mPaint.setAlpha(alpha); invalidateSelf(); } } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); invalidateSelf(); } private static class ThumbnailLoader extends BitmapLoader { private static final ThreadPool sThreadPool = new ThreadPool(0, 2); private BitmapJobDrawable mParent; public ThumbnailLoader(BitmapJobDrawable parent) { mParent = parent; } @Override protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) { return sThreadPool.submit( mParent.mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this); } @Override protected void onLoadComplete(Bitmap bitmap) { mParent.scheduleSelf(mParent, 0); } } }