Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 54 additions & 39 deletions picasso/src/main/java/com/squareup/picasso/BitmapHunter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
*/
package com.squareup.picasso;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.graphics.*;
import android.net.NetworkInfo;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
Expand Down Expand Up @@ -51,46 +49,49 @@ class BitmapHunter implements Runnable {
private static final Object DECODE_LOCK = new Object();

private static final ThreadLocal<StringBuilder> NAME_BUILDER = new ThreadLocal<StringBuilder>() {
@Override protected StringBuilder initialValue() {
@Override
protected StringBuilder initialValue() {
return new StringBuilder(Utils.THREAD_PREFIX);
}
};

private static final AtomicInteger SEQUENCE_GENERATOR = new AtomicInteger();

private static final RequestHandler ERRORING_HANDLER = new RequestHandler() {
@Override public boolean canHandleRequest(Request data) {
@Override
public boolean canHandleRequest(Request data) {
return true;
}

@Override public Result load(Request request, int networkPolicy) throws IOException {
@Override
public Result load(Request request, int networkPolicy) throws IOException {
throw new IllegalStateException("Unrecognized type of request: " + request);
}
};

final int sequence;
final Picasso picasso;
final int sequence;
final Picasso picasso;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final int memoryPolicy;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final int memoryPolicy;
int networkPolicy;
final RequestHandler requestHandler;

Action action;
List<Action> actions;
Bitmap result;
Future<?> future;
Action action;
List<Action> actions;
Bitmap result;
Future<?> future;
Picasso.LoadedFrom loadedFrom;
Exception exception;
int exifRotation; // Determined during decoding of original resource.
int retryCount;
Priority priority;
Exception exception;
int exifRotation; // Determined during decoding of original resource.
int retryCount;
Priority priority;

BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
RequestHandler requestHandler) {
RequestHandler requestHandler) {
this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
this.picasso = picasso;
this.dispatcher = dispatcher;
Expand Down Expand Up @@ -128,12 +129,13 @@ static Bitmap decodeStream(InputStream stream, Request request) throws IOExcepti
byte[] bytes = Utils.toByteArray(stream);
if (calculateSize) {
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
if (request.hasSize()) {
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
}
}
if (request.cropRect != null) {
if (request.cropRect != null && validateCrop(options, request.cropRect)) {
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(bytes, 0, bytes.length, false);
// TODO check if cropRect fits in bitmap
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(request.cropRect, options);
bitmapRegionDecoder.recycle();
return bitmap;
Expand All @@ -143,16 +145,16 @@ static Bitmap decodeStream(InputStream stream, Request request) throws IOExcepti
} else {
if (calculateSize) {
BitmapFactory.decodeStream(stream, null, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);

if (request.hasSize()) {
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
}
markStream.reset(mark);
}
final Bitmap bitmap;

if (request.cropRect != null) {
if (request.cropRect != null && validateCrop(options, request.cropRect)) {
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(stream, false);
// TODO check if cropRect fits in bitmap
bitmap = bitmapRegionDecoder.decodeRegion(request.cropRect, options);
bitmapRegionDecoder.recycle();
} else {
Expand All @@ -167,7 +169,16 @@ static Bitmap decodeStream(InputStream stream, Request request) throws IOExcepti
}
}

@Override public void run() {
static boolean validateCrop(BitmapFactory.Options options, Rect cropRect) throws IOException {
if (cropRect.width() > 0 && cropRect.height() > 0 && cropRect.width() <= options.outWidth && cropRect.height() <= options.outHeight) {
return true;
} else {
throw new IOException("Invalid crop dimensions.");
}
}

@Override
public void run() {
try {
updateThreadName(data);

Expand Down Expand Up @@ -424,7 +435,7 @@ static void updateThreadName(Request data) {
}

static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
Action action) {
Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();

Expand All @@ -448,7 +459,8 @@ static Bitmap applyCustomTransformations(List<Transformation> transformations, B
newResult = transformation.transform(result);
} catch (final RuntimeException e) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
@Override
public void run() {
throw new RuntimeException(
"Transformation " + transformation.key() + " crashed with exception.", e);
}
Expand All @@ -467,7 +479,8 @@ static Bitmap applyCustomTransformations(List<Transformation> transformations, B
builder.append(t.key()).append('\n');
}
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
@Override
public void run() {
throw new NullPointerException(builder.toString());
}
});
Expand All @@ -476,7 +489,8 @@ static Bitmap applyCustomTransformations(List<Transformation> transformations, B

if (newResult == result && result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
@Override
public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " returned input Bitmap but recycled it.");
Expand All @@ -488,7 +502,8 @@ static Bitmap applyCustomTransformations(List<Transformation> transformations, B
// If the transformation returned a new bitmap ensure they recycled the original.
if (newResult != result && !result.isRecycled()) {
Picasso.HANDLER.post(new Runnable() {
@Override public void run() {
@Override
public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " mutated input Bitmap but failed to recycle the original.");
Expand Down Expand Up @@ -584,7 +599,7 @@ static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
}

private static boolean shouldResize(boolean onlyScaleDown, int inWidth, int inHeight,
int targetWidth, int targetHeight) {
int targetWidth, int targetHeight) {
return !onlyScaleDown || inWidth > targetWidth || inHeight > targetHeight;
}
}
10 changes: 10 additions & 0 deletions picasso/src/main/java/com/squareup/picasso/RequestCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;
Expand Down Expand Up @@ -239,6 +240,15 @@ public RequestCreator centerInside() {
return this;
}

/**
* Crops image using provided rectangle dimensions.
* @param cropRect - dimensions used for crop
*/
public RequestCreator crop(Rect cropRect) {
data.crop(cropRect);
return this;
}

/**
* Only resize an image if the original image size is bigger than the target size
* specified by {@link #resize(int, int)}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ boolean supportsReplay() {
* {@link Request}, only instantiating them if needed.
*/
static BitmapFactory.Options createBitmapOptions(Request data) {
final boolean justBounds = data.hasSize();
final boolean justBounds = data.hasSize() || data.cropRect != null;
final boolean hasConfig = data.config != null;
BitmapFactory.Options options = null;
if (justBounds || hasConfig) {
Expand Down