Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.koushikdutta.ion;
  
  import  android.content.res.Resources;
  import  android.graphics.Bitmap;
  import  android.graphics.Canvas;
  import  android.graphics.Color;
  import  android.graphics.ColorFilter;
  import  android.graphics.Matrix;
  import  android.graphics.Paint;
 import  android.graphics.PixelFormat;
 import  android.graphics.Rect;
 import  android.graphics.RectF;
 import  android.graphics.drawable.BitmapDrawable;
 import  android.graphics.drawable.Drawable;
 import  android.os.Looper;
 import  android.os.SystemClock;
 import  android.text.TextUtils;
 import  android.view.animation.Animation;
 import  android.widget.ImageView;
 
 
Created by koush on 6/8/13.
 
 class IonDrawable extends Drawable {
     private Paint paint;
     private BitmapInfo info;
     private int placeholderResource;
     private Drawable placeholder;
     private int errorResource;
     private Drawable error;
     private Resources resources;
     private int loadedFrom;
     private IonDrawableCallback callback;
     private boolean disableFadeIn;
     private int resizeWidth;
     private int resizeHeight;
     private Ion ion;
 
     public IonDrawable ion(Ion ion) {
         this. = ion;
         return this;
     }
 
     public Drawable getCurrentDrawable() {
         if ( == null) {
             if ( != 0)
                 return .getDrawable();
         }
         if ( != null && . != null)
             return new BitmapDrawable(.[0]);
         if ( != 0)
             return .getDrawable();
         return null;
     }
 
     public BitmapInfo getBitmapInfo() {
         return ;
     }
 
     public static class ImageViewFutureImpl extends SimpleFuture<ImageView> implements ImageViewFuture {
         @Override
         public Future<ImageViewBitmapInfowithBitmapInfo() {
             final SimpleFuture<ImageViewBitmapInforet = new SimpleFuture<ImageViewBitmapInfo>();
             setCallback(new FutureCallback<ImageView>() {
                 @Override
                 public void onCompleted(Exception e, ImageView result) {
                     ImageViewBitmapInfo val = new ImageViewBitmapInfo();
                     Drawable d = null;
                     if (result != null)
                         d = result.getDrawable();
                     if (d instanceof IonDrawable) {
                         IonDrawable id = (IonDrawable)d;
                         val.info = id.info;
                     }
                     val.exception = e;
                     val.imageView = result;
                     ret.setComplete(val);
                 }
             });
             ret.setParent(this);
             return ret;
         }
     }
 
     public ImageViewFutureImpl getFuture() {
         return .;
     }
     
     public IonDrawable setDisableFadeIn(boolean disableFadeIn) {
         this. = disableFadeIn;
        return this;
    }
    public IonDrawable setInAnimation(Animation inAnimationint inAnimationResource) {
        . = inAnimation;
        . = inAnimationResource;
        return this;
    }
    // create an internal static class that can act as a callback.
    // dont let it hold strong references to anything.
    static class IonDrawableCallback implements FutureCallback<BitmapInfo> {
        private WeakReference<IonDrawableionDrawableRef;
        private String bitmapKey;
        private ImageViewFutureImpl imageViewFuture = new ImageViewFutureImpl();
        private Animation inAnimation;
        private int inAnimationResource;
        public IonDrawableCallback(IonDrawable drawable, ImageView imageView) {
             = new WeakReference<IonDrawable>(drawable);
             = new ContextReference.ImageViewContextReference(imageView);
        }
        @Override
        public void onCompleted(Exception eBitmapInfo result) {
            assert Thread.currentThread() == Looper.getMainLooper().getThread();
            assert result != null;
            // see if the imageview is still alive and cares about this result
            ImageView imageView = .get();
            if (imageView == null)
                return;
            IonDrawable drawable = .get();
            if (drawable == null)
                return;
            if (imageView.getDrawable() != drawable)
                return;
            imageView.setImageDrawable(null);
            drawable.setBitmap(resultresult.loadedFrom);
            imageView.setImageDrawable(drawable);
            IonBitmapRequestBuilder.doAnimation(imageView);
            if (null != .isAlive()) {
                .cancelSilently();
                return;
            }
            .setComplete(eimageView);
        }
    }
    public void cancel() {
        . = null;
    }
    private static void unregister(Ion ionString keyIonDrawableCallback callback) {
        if (key == null)
            return;
        // unregister this drawable from the bitmaps that are
        // pending.
        // if this drawable was the only thing waiting for this bitmap,
        // then the removeItem call will return the TransformBitmap/LoadBitmap instance
        // that was providing the result.
        if (ion.bitmapsPending.removeItem(keycallback)) {
            // find out who owns this thing, to see if it is a candidate for removal
            Object owner = ion.bitmapsPending.tag(key);
            if (owner instanceof TransformBitmap) {
                TransformBitmap info = (TransformBitmap)owner;
                ion.bitmapsPending.remove(info.key);
                // this transform is also backed by a LoadBitmap* or a DeferredLoadBitmap, grab that
                // if it is the only waiter
                if (ion.bitmapsPending.removeItem(info.downloadKeyinfo))
                    owner = ion.bitmapsPending.tag(info.downloadKey);
            }
            // only cancel deferred loads... LoadBitmap means a download is already in progress.
            // due to view recycling, cancelling that may be bad, as it may be rerequested again
            // during the recycle process.
            if (owner instanceof DeferredLoadBitmap) {
                DeferredLoadBitmap defer = (DeferredLoadBitmap)owner;
                ion.bitmapsPending.remove(defer.key);
            }
        }
        ion.processDeferred();
    }
    public void register(Ion ionString bitmapKey) {
        String previousKey = .;
        if (TextUtils.equals(previousKeybitmapKey))
            return;
        . = bitmapKey;
        ion.bitmapsPending.add(bitmapKey);
        unregister(ionpreviousKey);
    }
    private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
    public IonDrawable(Resources resources, ImageView imageView) {
        this. = resources;
         = new Paint();
         = new IonDrawableCallback(thisimageView);
    }
    int currentFrame;
    private boolean invalidateScheduled;
    private int textureDim;
    private int maxLevel;
    public IonDrawable setBitmap(BitmapInfo infoint loadedFrom) {
        if (this. == info)
            return this;
        cancel();
        this. = loadedFrom;
        this. = info;
         = 0;
         = false;
        invalidateSelf();
        if (info == null) {
            . = null;
            return this;
        }
        if (info.decoder != null) {
            // find number of tiles across to fit
            double wlevel = (double)info.originalSize.x / ;
            double hlevel = (double)info.originalSize.y / ;
            // find the level: find how many power of 2 tiles are necessary
            // to fit the entire image. ie, fit it into a square.
            double level = Math.max(wlevelhlevel);
            level = Math.log(level) / ;
             = (int)Math.ceil(level);
            // now, we know the entire image will fit in a square image of
            // this dimension:
             =  << ;
        }
        . = info.key;
        return this;
    }
    public IonDrawable setSize(int resizeWidthint resizeHeight) {
        if (this. == resizeWidth && this. == resizeHeight)
            return this;
        this. = resizeWidth;
        this. = resizeHeight;
        invalidateSelf();
        return this;
    }
    public IonDrawable setError(int resource, Drawable drawable) {
        if ((drawable != null && drawable == ) || (resource != 0 && resource == ))
            return this;
         = resource;
        if ( != null)
            .setCallback(null);
         = drawable;
        invalidateSelf();
        return this;
    }
    public IonDrawable setPlaceholder(int resource, Drawable drawable) {
        if ((drawable != null && drawable == ) || (resource != 0 && resource == ))
            return this;
         = resource;
        if ( != null)
            .setCallback(null);
         = drawable;
        invalidateSelf();
        return this;
    }
    @Override
    public void setFilterBitmap(boolean filter) {
        .setFilterBitmap(filter);
        invalidateSelf();
    }
    @Override
    public void setDither(boolean dither) {
        .setDither(dither);
        invalidateSelf();
    }
    Callback drawableCallback = new Callback() {
        @Override
        public void invalidateDrawable(Drawable who) {
            IonDrawable.this.invalidateSelf();
        }
        @Override
        public void scheduleDrawable(Drawable whoRunnable whatlong when) {
            IonDrawable.this.scheduleSelf(whatwhen);
        }
        @Override
        public void unscheduleDrawable(Drawable whoRunnable what) {
            IonDrawable.this.unscheduleSelf(what);
        }
    };
    private Drawable tryGetErrorResource() {
        if ( != null)
            return ;
        if ( == 0)
            return null;
         = .getDrawable();
        .setCallback();
        return ;
    }
    private Drawable tryGetPlaceholderResource() {
        if ( != null)
            return ;
        if ( == 0)
            return null;
         = .getDrawable();
        .setCallback();
        return ;
    }
    @Override
    public int getIntrinsicWidth() {
        // first check if image was loaded
        if ( != null) {
            if (. != null)
                return ..;
            if (. != null)
                return .[0].getScaledWidth(.getDisplayMetrics().densityDpi);
        }
        // check eventual image size...
        if ( > 0)
            return ;
        // no image, but there was an error
        if ( != null) {
            Drawable error = tryGetErrorResource();
            if (error != null)
                return error.getIntrinsicWidth();
        }
        // check placeholder
        Drawable placeholder = tryGetPlaceholderResource();
        if (placeholder != null)
            return placeholder.getIntrinsicWidth();
        // we're SOL
        return -1;
    }
    @Override
    public int getIntrinsicHeight() {
        if ( != null) {
            if (. != null)
                return ..;
            if (. != null)
                return .[0].getScaledHeight(.getDisplayMetrics().densityDpi);
        }
        if ( > 0)
            return ;
        if ( != null) {
            Drawable error = tryGetErrorResource();
            if (error != null)
                return error.getIntrinsicHeight();
        }
        Drawable placeholder = tryGetPlaceholderResource();
        if (placeholder != null)
            return placeholder.getIntrinsicHeight();
        return -1;
    }
    public static final long FADE_DURATION = 200;
    private Runnable invalidate = new Runnable() {
        @Override
        public void run() {
             = false;
            ++;
            invalidateSelf();
        }
    };
    private static final double LOG_2 = Math.log(2);
    private static final int TILE_DIM = 256;
        @Override
        public void onCompleted(Exception eBitmapInfo result) {
            invalidateSelf();
        }
    };
    private void drawDrawable(Canvas canvas, Drawable d) {
        if (d == null)
            return;
        if (false) {
            // this centers inside and draws the drawable
            d.setBounds(0 , 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            int count = canvas.save();
            Matrix matrix = new Matrix();
            matrix.setRectToRect(new RectF(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()),
            new RectF(canvas.getClipBounds()), Matrix.ScaleToFit.CENTER);
            canvas.concat(matrix);
            float scale = (float)canvas.getClipBounds().width() / canvas.getWidth();
            canvas.scale(scalescaled.getIntrinsicWidth() / 2, d.getIntrinsicHeight() / 2);
            d.draw(canvas);
            canvas.restoreToCount(count);
            return;
        }
        else if (false) {
            // this centers fits and draws the drawable
            int iw = d.getIntrinsicWidth();
            int ih = d.getIntrinsicHeight();
            Rect b = copyBounds();
            int w = b.width();
            int h = b.height();
            if (iw >= 0) {
                int wp = (w - iw) / 2;
                b.left += wp;
                b.right = b.left + iw;
            }
            if (ih >= 0) {
                int hp = (h - ih) / 2;
                b.top += hp;
                b.bottom = b.top + ih;
            }
            d.setBounds(b);
        }
        else {
            // fitxy the drwable
            d.setBounds(getBounds());
        }
        d.draw(canvas);
    }
    @Override
    public void draw(Canvas canvas) {
        // TODO: handle animated drawables
        if ( == null) {
            drawDrawable(canvastryGetPlaceholderResource());
            return;
        }
        if (. == 0)
            . = SystemClock.uptimeMillis();
        long destAlpha = 0xFF;
        if(!) {
            destAlpha = ((SystemClock.uptimeMillis() - .) << 8) / ;
            destAlpha = Math.min(destAlpha, 0xFF);
        }
        if (destAlpha != 255) {
            Drawable placeholder = tryGetPlaceholderResource();
            if (placeholder != null) {
                drawDrawable(canvasplaceholder);
            }
        }
        if (. != null) {
            // zoom 0: entire image fits in a TILE_DIMxTILE_DIM square
            // draw base bitmap for empty tiles
            // figure out zoom level
            // figure out which tiles need rendering
            // draw stuff that needs drawing
            // missing tile? fetch it
            // use parent level tiles for tiles that do not exist
            // TODO: crossfading?
            Rect clip = canvas.getClipBounds();
            Rect bounds = getBounds();
            float zoom = (float)canvas.getWidth() / (float)clip.width();
            float zoomWidth = zoom * bounds.width();
            float zoomHeight = zoom * bounds.height();
            double wlevel = Math.log(zoomWidth / ) / ;
            double hlevel = Math.log(zoomHeight) / ;
            double maxLevel = Math.max(wlevelhlevel);
            int visibleLeft = Math.max(0, clip.left);
            int visibleRight = Math.min(bounds.width(), clip.right);
            int visibleTop = Math.max(0, clip.top);
            int visibleBottom = Math.min(bounds.height(), clip.bottom);
            int level = (int)Math.floor(maxLevel);
            level = Math.min(this.level);
            level = Math.max(level, 0);
            int levelTiles = 1 << level;
            int textureTileDim =  / levelTiles;
//            System.out.println("textureTileDim: " + textureTileDim);
//            System.out.println(info.key + " visible: " + new Rect(visibleLeft, visibleTop, visibleRight, visibleBottom));
            final boolean DEBUG_ZOOM = false;
            if (. != null && .[0] != null) {
                canvas.drawBitmap(.[0], nullgetBounds(), );
                if (DEBUG_ZOOM) {
                    .setColor(Color.RED);
                    .setAlpha(0x80);
                    canvas.drawRect(getBounds(), );
                    .setAlpha(0xFF);
                }
            }
            else {
                .setColor(Color.BLACK);
                canvas.drawRect(getBounds(), );
            }
            int sampleSize = 1;
            while (textureTileDim / sampleSize > )
                sampleSize <<= 1;
            for (int y = 0; y < levelTilesy++) {
                int top = textureTileDim * y;
                int bottom = textureTileDim * (y + 1);
                bottom = Math.min(bottombounds.bottom);
                // TODO: start at visible pos
                if (bottom < visibleTop)
                    continue;
                if (top > visibleBottom)
                    break;
                for (int x = 0; x < levelTilesx++) {
                    int left = textureTileDim * x;
                    int right = textureTileDim * (x + 1);
                    right = Math.min(rightbounds.right);
                    // TODO: start at visible pos
                    if (right < visibleLeft)
                        continue;
                    if (left > visibleRight)
                        break;
                    Rect texRect = new Rect(lefttoprightbottom);
                    // find, render/fetch
//                    System.out.println("rendering: " + texRect + " for: " + bounds);
                    String tileKey = FileCache.toKeyString(.","level","x","y);
                    BitmapInfo tile = ..get(tileKey);
                    if (tile != null && tile.bitmaps != null) {
                        // render it
//                        System.out.println("bitmap is: " + tile.bitmaps[0].getWidth() + "x" + tile.bitmaps[0].getHeight());
                        canvas.drawBitmap(tile.bitmaps[0], nulltexRect);
                        continue;
                    }
                    // TODO: cancellation of unnecessary regions when fast pan/zooming
                    if (..tag(tileKey) == null) {
                        // fetch it
//                        System.out.println(info.key + ": fetching region: " + texRect + " sample size: " + sampleSize);
                        LoadBitmapRegion region = new LoadBitmapRegion(tileKey.texRectsampleSize);
                    }
                    ..add(tileKey);
                    int parentLeft = 0;
                    int parentTop = 0;
                    int parentUp = 1;
                    int parentLevel = level - parentUp;
                    if (x % 2 == 1)
                        parentLeft++;
                    if (y % 2 == 1)
                        parentTop++;
                    int parentX = x >> 1;
                    int parentY = y >> 1;
                    while (parentLevel >= 0) {
                        tileKey = FileCache.toKeyString(.","parentLevel","parentX","parentY);
                        tile = ..get(tileKey);
                        if (tile != null && tile.bitmaps != null)
                            break;
                        if (parentX % 2 == 1) {
                            parentLeft += 1 << parentUp;
                        }
                        if (parentY % 2 == 1) {
                            parentTop += 1 << parentUp;
                        }
                        parentLevel--;
                        parentUp++;
                        parentX >>= 1;
                        parentY >>= 1;
                    }
                    // well, i give up
                    if (tile == null || tile.bitmaps == null)
                        continue;
                    int subLevelTiles = 1 << parentLevel;
                    int subtileDim =  / subLevelTiles;
                    int subSampleSize = 1;
                    while (subtileDim / subSampleSize > )
                        subSampleSize <<= 1;
                    int subTextureDim = subtileDim / subSampleSize;
//                    System.out.println(String.format("falling back for %s,%s,%s to %s,%s,%s: %s,%s (%s to %s)", x, y, level, parentX, parentY, parentLevel, parentLeft, parentTop, subTextureDim, subTextureDim >> parentUp));
                    subTextureDim >>= parentUp;
                    int sourceLeft = subTextureDim * parentLeft;
                    int sourceTop = subTextureDim * parentTop;
                    Rect sourceRect = new Rect(sourceLeftsourceTopsourceLeft + subTextureDimsourceTop + subTextureDim);
                    canvas.drawBitmap(tile.bitmaps[0], sourceRecttexRect);
                    if (DEBUG_ZOOM) {
                        .setColor(Color.RED);
                        .setAlpha(0x80);
                        canvas.drawRect(texRect);
                        .setAlpha(0xFF);
                    }
                }
            }
        }
        else if (. != null) {
            .setAlpha((int)destAlpha);
            canvas.drawBitmap(.[ % ..], nullgetBounds(), );
            .setAlpha(0xFF);
            if (. != null) {
                int delay = .[ % ..];
                if (!) {
                     = true;
                    unscheduleSelf();
                    scheduleSelf(, SystemClock.uptimeMillis() + Math.max(delay, 100));
                }
            }
        }
        else {
            Drawable error = tryGetErrorResource();
            if (error != null) {
                error.setAlpha((int)destAlpha);
                drawDrawable(canvaserror);
                error.setAlpha(0xFF);
            }
        }
        if (destAlpha != 255)
            invalidateSelf();
        if (true)
            return;
        // stolen from picasso
        canvas.save();
        canvas.rotate(45);
        .setColor(Color.WHITE);
        canvas.drawRect(0, -10, 7.5f, 10, );
        int sourceColor;
        switch () {
            case ..:
                sourceColor = Color.CYAN;
                break;
                sourceColor = Color.YELLOW;
                break;
            case ..:
                sourceColor = Color.GREEN;
                break;
            default:
                sourceColor = Color.RED;
                break;
        }
        .setColor(sourceColor);
        canvas.drawRect(0, -9, 6.5f, 9, );
        canvas.restore();
    }
    @Override
    public void setAlpha(int alpha) {
       .setAlpha(alpha);
    }
    @Override
    public void setColorFilter(ColorFilter cf) {
        .setColorFilter(cf);
    }
    @Override
    public int getOpacity() {
        return ( == null || . == null || .[0].hasAlpha() || .getAlpha() < 255) ?
                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
    }
    static IonDrawable getOrCreateIonDrawable(ImageView imageView) {
        Drawable current = imageView.getDrawable();
        IonDrawable ret;
        if (current == null || !(current instanceof IonDrawable))
            ret = new IonDrawable(imageView.getResources(), imageView);
        else
            ret = (IonDrawable)current;
        // invalidate self doesn't seem to trigger the dimension check to be called by imageview.
        // are drawable dimensions supposed to be immutable?
        imageView.setImageDrawable(null);
        return ret;
    }
New to GrepCode? Check out our FAQ X