Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.koushikdutta.ion;
  
  import  android.content.res.Resources;
  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.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 boolean fadeIn;
     private int resizeWidth;
     private int resizeHeight;
     private boolean repeatAnimation;
     private Ion ion;
     private BitmapFetcher bitmapFetcher;
     private IonDrawableCallback callback;
     private IonGifDecoder gifDecoder;
 
         return ;
     }
 
     public IonDrawable setLoadCallback(FutureCallback<IonDrawableloadCallback) {
         this. = loadCallback;
         return this;
     }
 
     public IonDrawable ion(Ion ion) {
         if (ion == null)
             throw new AssertionError("null ion");
         this. = ion;
         return this;
     }
 
     public Drawable getCurrentDrawable() {
         if ( == null) {
             if ( != 0)
                 return .getDrawable();
         }
         if ( != null) {
             if (. != null)
                 return new BitmapDrawable(.);
             else if (. != null) {
                 GifFrame last = ..getLastFrame();
                 if (last != null)
                     return new BitmapDrawable(last.image);
                 if ( != 0)
                     return .getDrawable();
                 return null;
             }
         }
         if ( != 0)
             return .getDrawable();
         return null;
     }
 
     public BitmapInfo getBitmapInfo() {
         return ;
     }
 
     // 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 Ion ion;
         public IonDrawableCallback(IonDrawable drawable) {
              = new WeakReference<IonDrawable>(drawable);
 //            imageViewRef = new ContextReference.ImageViewContextReference(imageView);
        }
        public void register(Ion ionString bitmapKey) {
            String previousKey = this.;
            Ion previousIon = this.;
            if (TextUtils.equals(previousKeybitmapKey) && this. == ion)
                return;
            this. = ion;
            this. = bitmapKey;
            if (ion != null)
                ion.bitmapsPending.add(bitmapKeythis);
            unregister(previousIonpreviousKey);
        }
        private void unregister(Ion ionString key) {
            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(keythis)) {
                // 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();
        }
        @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
            IonDrawable drawable = .get();
            if (drawable == null)
                return;
            drawable.setBitmap(resultresult.loadedFrom);
            FutureCallback<IonDrawablecallback = drawable.loadCallback;
            if (callback != null)
                callback.onCompleted(edrawable);
        }
    }
    class IonGifDecoder {
        GifDecoder gifDecoder;
        Exception exception;
        public IonGifDecoder(BitmapInfo info){
             = info.gifDecoder.mutate();
        }
        Runnable loader = new Runnable() {
            @Override
            public void run() {
                try {
                    .nextFrame();
                }
                catch (Exception e) {
                     = e;
                }
                ..post();
            }
        };
        Runnable postLoad = new Runnable() {
            @Override
            public void run() {
                 = false;
                if (!)
                    invalidateSelf();
            }
        };
        boolean isLoading;
        public synchronized void scheduleNextFrame() {
            if ()
                return;
            if ( != null)
                return;
             = true;
            Ion.getBitmapLoadExecutorService().execute();
        }
    }
    public IonDrawable setFadeIn(boolean fadeIn) {
        this. = fadeIn;
        return this;
    }
    public IonDrawable setBitmapFetcher(BitmapFetcher bitmapFetcher) {
        this. = bitmapFetcher;
        if ( == null)
            throw new AssertionError("null ion");
        return this;
    }
    public void cancel() {
        .register(nullnull);
         = null;
    }
    private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
    public IonDrawable(Resources resources) {
        this. = resources;
         = new Paint();
         = new IonDrawableCallback(this);
    }
    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;
         = null;
         = false;
        unscheduleSelf();
        invalidateSelf();
        if (info == 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:
             =  << ;
        }
        else if (info.gifDecoder != null) {
             = new IonGifDecoder(info);
        }
        return this;
    }
    public IonDrawable setRepeatAnimation(boolean repeatAnimation) {
        this. = repeatAnimation;
        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 ..getScaledWidth(.getDisplayMetrics().densityDpi);
        }
        if ( != null)
            return ..getWidth();
        // 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 ..getScaledHeight(.getDisplayMetrics().densityDpi);
        }
        if ( != null)
            return ..getHeight();
        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
        // check if we have a bitmap, otherwise do the placeholder and bail
        if ( == null) {
            // first things first, draw a placeholder
            drawDrawable(canvastryGetPlaceholderResource());
            // see if we can fetch a bitmap
            if ( != null) {
                if (. == 0 && . == 0) {
                    if (canvas.getWidth() != 1)
                        . = canvas.getWidth();
                    if (canvas.getHeight() != 1)
                        . = canvas.getHeight();
                    // now that we have final dimensions, reattempt to find the image in the cache
                    .recomputeDecodeKey();
                    BitmapInfo found = ..get(.);
                    if (found != null) {
                        // found what we're looking for, but can't draw at this very moment,
                        // since we need to trigger a new measure.
                        drawDrawable(canvastryGetPlaceholderResource());
                        .onCompleted(nullfound);
                        return;
                    }
                }
                // no image found fetch it.
                .register(.);
                // check to see if there's too many imageview loads
                // already in progress
                if (BitmapFetcher.shouldDeferImageView()) {
                    .defer();
                }
                else {
                    .execute();
                }
                // won't be needing THIS anymore
                 = null;
            }
            // well, can't do anything else here.
            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) {
                canvas.drawBitmap(.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.bitmap != null) {
                        // render it
//                        System.out.println("bitmap is: " + tile.bitmaps[0].getWidth() + "x" + tile.bitmaps[0].getHeight());
                        canvas.drawBitmap(tile.bitmapnulltexRect);
                        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.bitmap != 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.bitmap == 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.bitmapsourceRecttexRect);
                    if (DEBUG_ZOOM) {
                        .setColor(Color.RED);
                        .setAlpha(0x80);
                        canvas.drawRect(texRect);
                        .setAlpha(0xFF);
                    }
                }
            }
        }
        else if (. != null) {
            GifFrame lastFrame = ..getLastFrame();
            if (lastFrame != null) {
                .setAlpha((int)destAlpha);
                canvas.drawBitmap(lastFrame.imagenullgetBounds(), );
                .setAlpha(0xFF);
                long delay = lastFrame.delay;
                if (!) {
                     = true;
                    unscheduleSelf();
                    scheduleSelf(, SystemClock.uptimeMillis() + Math.max(delay, 16));
                }
            }
            if (..getStatus() == . && )
                ..restart();
            .scheduleNextFrame();
        }
        else if (. != null) {
            .setAlpha((int)destAlpha);
            canvas.drawBitmap(.nullgetBounds(), );
            .setAlpha(0xFF);
        }
        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 || ..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());
        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);
        ret.unscheduleSelf(ret.invalidate);
        ret.invalidateScheduled = false;
        return ret;
    }
New to GrepCode? Check out our FAQ X