use of com.koushikdutta.ion.bitmap.BitmapInfo in project ion by koush.
the class ImageViewFutureImpl method transform.
@Override
protected void transform(IonDrawable result) throws Exception {
// need to make sure that the IonDrawable calling this future
// is still needed. It may have changed if the user manually
// called setDrawable on the ImageView, etc.
final ImageView imageView = imageViewRef.get();
// check the imageview and the activity context
if (null != imageViewRef.isAlive() || imageView == null) {
cancelSilently();
return;
}
if (imageView.getDrawable() != result) {
// imageview is now waiting for something else now... cancel
cancelSilently();
return;
}
// retrigger the intrinsic dimension check on the drawable
BitmapInfo info = result.getBitmapInfo();
if (info != null && info.exception == null) {
applyScaleMode(imageView, scaleMode);
}
IonBitmapRequestBuilder.doAnimation(imageView, inAnimation, inAnimationResource);
imageView.setImageDrawable(null);
imageView.setImageDrawable(result);
setComplete(imageView);
}
use of com.koushikdutta.ion.bitmap.BitmapInfo in project ion by koush.
the class IonDrawable method draw.
@Override
public void draw(Canvas canvas) {
if (info == null) {
// draw stuff
super.draw(canvas);
// see if we can fetch a bitmap
if (bitmapFetcher != null) {
if (bitmapFetcher.sampleWidth == 0 && bitmapFetcher.sampleHeight == 0) {
if (canvas.getWidth() != 1)
bitmapFetcher.sampleWidth = canvas.getWidth();
if (canvas.getHeight() != 1)
bitmapFetcher.sampleHeight = canvas.getHeight();
// now that we have final dimensions, reattempt to find the image in the cache
bitmapFetcher.recomputeDecodeKey();
BitmapInfo found = ion.bitmapCache.get(bitmapFetcher.bitmapKey);
if (found != null) {
// won't be needing THIS anymore
bitmapFetcher = null;
// found what we're looking for, but can't draw at this very moment,
// since we need to trigger a new measure.
callback.onCompleted(null, found);
return;
}
}
// no image found fetch it.
callback.register(ion, bitmapFetcher.bitmapKey);
// already in progress
if (BitmapFetcher.shouldDeferImageView(ion)) {
bitmapFetcher.defer();
} else {
bitmapFetcher.execute();
}
// won't be needing THIS anymore
bitmapFetcher = null;
}
// well, can't do anything else here.
return;
}
if (info.decoder != null) {
drawDeepZoom(canvas);
return;
}
if (info.drawTime == 0)
info.drawTime = SystemClock.uptimeMillis();
long destAlpha = this.alpha;
if (fadeIn) {
destAlpha = ((SystemClock.uptimeMillis() - info.drawTime) << 8) / FADE_DURATION;
destAlpha = Math.min(destAlpha, this.alpha);
}
// remove plaeholder if not visible
if (destAlpha == this.alpha) {
if (placeholder != null) {
placeholder = null;
setDrawableByLayerId(0, NULL_PLACEHOLDER);
}
} else {
// invalidate to fade in
if (placeholder != null)
invalidateSelf();
}
if (info.gifDecoder != null) {
super.draw(canvas);
GifFrame frame = gifDecoder.getCurrentFrame();
if (frame != null) {
paint.setAlpha((int) destAlpha);
canvas.drawBitmap(frame.image, null, getBounds(), paint);
paint.setAlpha(this.alpha);
invalidateSelf();
}
return;
}
if (info.bitmap != null) {
if (bitmapDrawable != null)
bitmapDrawable.setAlpha((int) destAlpha);
} else {
if (error != null)
error.setAlpha((int) destAlpha);
}
super.draw(canvas);
if (true)
return;
// stolen from picasso
canvas.save();
canvas.rotate(45);
paint.setColor(Color.WHITE);
canvas.drawRect(0, -10, 7.5f, 10, paint);
int sourceColor;
if (servedFrom == ResponseServedFrom.LOADED_FROM_CACHE)
sourceColor = Color.CYAN;
else if (servedFrom == ResponseServedFrom.LOADED_FROM_CONDITIONAL_CACHE)
sourceColor = Color.YELLOW;
else if (servedFrom == ResponseServedFrom.LOADED_FROM_MEMORY)
sourceColor = Color.GREEN;
else
sourceColor = Color.RED;
paint.setColor(sourceColor);
canvas.drawRect(0, -9, 6.5f, 9, paint);
canvas.restore();
}
use of com.koushikdutta.ion.bitmap.BitmapInfo in project ion by koush.
the class IonDrawable method drawDeepZoom.
private void drawDeepZoom(Canvas canvas) {
// 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 / TILE_DIM) / LOG_2;
double hlevel = Math.log(zoomHeight / TILE_DIM) / LOG_2;
double maxLevel = Math.max(wlevel, hlevel);
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.maxLevel, level);
level = Math.max(level, 0);
int levelTiles = 1 << level;
int textureTileDim = textureDim / levelTiles;
// System.out.println("textureTileDim: " + textureTileDim);
// System.out.println(info.key + " visible: " + new Rect(visibleLeft, visibleTop, visibleRight, visibleBottom));
final boolean DEBUG_ZOOM = false;
if (info.bitmap != null) {
canvas.drawBitmap(info.bitmap, null, getBounds(), paint);
if (DEBUG_ZOOM) {
paint.setColor(Color.RED);
paint.setAlpha(0x80);
canvas.drawRect(getBounds(), paint);
paint.setAlpha(0xFF);
}
} else {
paint.setColor(Color.BLACK);
canvas.drawRect(getBounds(), paint);
}
int sampleSize = 1;
while (textureTileDim / sampleSize > TILE_DIM) sampleSize <<= 1;
for (int y = 0; y < levelTiles; y++) {
int top = textureTileDim * y;
int bottom = textureTileDim * (y + 1);
bottom = Math.min(bottom, bounds.bottom);
// TODO: start at visible pos
if (bottom < visibleTop)
continue;
if (top > visibleBottom)
break;
for (int x = 0; x < levelTiles; x++) {
int left = textureTileDim * x;
int right = textureTileDim * (x + 1);
right = Math.min(right, bounds.right);
// TODO: start at visible pos
if (right < visibleLeft)
continue;
if (left > visibleRight)
break;
Rect texRect = new Rect(left, top, right, bottom);
// find, render/fetch
// System.out.println("rendering: " + texRect + " for: " + bounds);
String tileKey = FileCache.toKeyString(info.key, ",", level, ",", x, ",", y);
BitmapInfo tile = ion.bitmapCache.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.bitmap, null, texRect, paint);
continue;
}
// TODO: cancellation of unnecessary regions when fast pan/zooming
if (ion.bitmapsPending.tag(tileKey) == null) {
// fetch it
// System.out.println(info.key + ": fetching region: " + texRect + " sample size: " + sampleSize);
LoadBitmapRegion region = new LoadBitmapRegion(ion, tileKey, info.decoder, texRect, sampleSize);
}
ion.bitmapsPending.add(tileKey, tileCallback);
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(info.key, ",", parentLevel, ",", parentX, ",", parentY);
tile = ion.bitmapCache.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 = textureDim / subLevelTiles;
int subSampleSize = 1;
while (subtileDim / subSampleSize > TILE_DIM) 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(sourceLeft, sourceTop, sourceLeft + subTextureDim, sourceTop + subTextureDim);
canvas.drawBitmap(tile.bitmap, sourceRect, texRect, paint);
if (DEBUG_ZOOM) {
paint.setColor(Color.RED);
paint.setAlpha(0x80);
canvas.drawRect(texRect, paint);
paint.setAlpha(0xFF);
}
}
}
}
use of com.koushikdutta.ion.bitmap.BitmapInfo in project ion by koush.
the class FileLoader method loadBitmap.
@Override
public Future<BitmapInfo> loadBitmap(final Context context, final Ion ion, final String key, final String uri, final int resizeWidth, final int resizeHeight, final boolean animateGif) {
if (uri == null || !uri.startsWith("file:/"))
return null;
final SimpleFuture<BitmapInfo> ret = new SimpleFuture<BitmapInfo>();
// Log.d("FileLoader", "Loading file bitmap " + uri + " " + resizeWidth + "," + resizeHeight);
Ion.getBitmapLoadExecutorService().execute(new Runnable() {
@Override
public void run() {
if (ret.isCancelled()) {
// Log.d("FileLoader", "Bitmap load cancelled (no longer needed)");
return;
}
try {
File file = new File(URI.create(uri));
BitmapFactory.Options options = ion.getBitmapCache().prepareBitmapOptions(file, resizeWidth, resizeHeight);
Point size = new Point(options.outWidth, options.outHeight);
BitmapInfo info;
if (animateGif && TextUtils.equals("image/gif", options.outMimeType)) {
FileInputStream fin = new FileInputStream(file);
try {
info = loadGif(key, size, fin, options);
} finally {
StreamUtility.closeQuietly(fin);
}
} else {
Bitmap bitmap = IonBitmapCache.loadBitmap(file, options);
if (bitmap == null)
throw new Exception("Bitmap failed to load");
info = new BitmapInfo(key, options.outMimeType, bitmap, size);
}
info.servedFrom = ResponseServedFrom.LOADED_FROM_CACHE;
ret.setComplete(info);
} catch (OutOfMemoryError e) {
ret.setComplete(new Exception(e), null);
} catch (Exception e) {
ret.setComplete(e);
}
}
});
return ret;
}
use of com.koushikdutta.ion.bitmap.BitmapInfo in project ion by koush.
the class ResourceLoader method loadBitmap.
@Override
public Future<BitmapInfo> loadBitmap(final Context context, final Ion ion, final String key, final String uri, final int resizeWidth, final int resizeHeight, final boolean animateGif) {
if (uri == null || !uri.startsWith("android.resource:/"))
return null;
final SimpleFuture<BitmapInfo> ret = new SimpleFuture<BitmapInfo>();
// Log.d("FileLoader", "Loading file bitmap " + uri + " " + resizeWidth + "," + resizeHeight);
Ion.getBitmapLoadExecutorService().execute(new Runnable() {
@Override
public void run() {
try {
Resource res = lookupResource(context, uri);
BitmapFactory.Options options = ion.getBitmapCache().prepareBitmapOptions(res.res, res.id, resizeWidth, resizeHeight);
Point size = new Point(options.outWidth, options.outHeight);
BitmapInfo info;
if (animateGif && TextUtils.equals("image/gif", options.outMimeType)) {
InputStream in = res.res.openRawResource(res.id);
try {
info = loadGif(key, size, in, options);
} finally {
StreamUtility.closeQuietly(in);
}
} else {
Bitmap bitmap = IonBitmapCache.loadBitmap(res.res, res.id, options);
if (bitmap == null)
throw new Exception("Bitmap failed to load");
info = new BitmapInfo(key, options.outMimeType, bitmap, size);
}
info.servedFrom = ResponseServedFrom.LOADED_FROM_CACHE;
ret.setComplete(info);
} catch (OutOfMemoryError e) {
ret.setComplete(new Exception(e), null);
} catch (Exception e) {
ret.setComplete(e);
}
}
});
return ret;
}
Aggregations