Skip to content

Commit

Permalink
fixes bumptech#738: Add optional threading lock for bitmap manipulati…
Browse files Browse the repository at this point in the history
…on (affects only Moto X 2nd gen on api 22)

this addresses a threading bug on this specific device, the bug manifests itself by displaying
black images instead of resized images.
  • Loading branch information
Matthijs Mullender committed Nov 20, 2015
1 parent 4f159aa commit 2fa00cf
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 50 deletions.
28 changes: 0 additions & 28 deletions .idea/codeStyleSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -400,17 +400,19 @@ private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options
int sourceHeight = options.outHeight;
String outMimeType = options.outMimeType;
final Bitmap result;
TransformationUtils.getBitmapDrawableLock().lock();
try {
result = BitmapFactory.decodeStream(is, null, options);
} catch (IllegalArgumentException e) {
throw newIoExceptionForInBitmapAssertion(e, sourceWidth, sourceHeight, outMimeType, options);
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}

if (options.inJustDecodeBounds) {
is.reset();

}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.util.Preconditions;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* A class with methods to efficiently resize Bitmaps.
*/
Expand All @@ -30,6 +35,11 @@ public final class TransformationUtils {
private static final int CIRCLE_CROP_PAINT_FLAGS = PAINT_FLAGS | Paint.ANTI_ALIAS_FLAG;
private static final Paint CIRCLE_CROP_SHAPE_PAINT = new Paint(CIRCLE_CROP_PAINT_FLAGS);
private static final Paint CIRCLE_CROP_BITMAP_PAINT;
private static final Lock BITMAP_DRAWABLE_LOCK = "XT1097".equals(Build.MODEL)
&& Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1
? new ReentrantLock()
: new NoLock();

static {
CIRCLE_CROP_BITMAP_PAINT = new Paint(CIRCLE_CROP_PAINT_FLAGS);
CIRCLE_CROP_BITMAP_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Expand All @@ -39,6 +49,11 @@ private TransformationUtils() {
// Utility class.
}


public static Lock getBitmapDrawableLock() {
return BITMAP_DRAWABLE_LOCK;
}

/**
* A potentially expensive operation to crop the given Bitmap so that it fills the given
* dimensions. This operation is significantly less expensive in terms of memory if a mutable
Expand Down Expand Up @@ -74,9 +89,7 @@ public static Bitmap centerCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitm
// We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
TransformationUtils.setAlpha(inBitmap, result);

Canvas canvas = new Canvas(result);
canvas.drawBitmap(inBitmap, m, DEFAULT_PAINT);
clear(canvas);
applyMatrix(inBitmap, result, m);
return result;
}

Expand Down Expand Up @@ -129,11 +142,9 @@ public static Bitmap fitCenter(@NonNull BitmapPool pool, @NonNull Bitmap inBitma
Log.v(TAG, "minPct: " + minPercentage);
}

Canvas canvas = new Canvas(toReuse);
Matrix matrix = new Matrix();
matrix.setScale(minPercentage, minPercentage);
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
clear(canvas);
applyMatrix(inBitmap, toReuse, matrix);

return toReuse;
}
Expand Down Expand Up @@ -240,10 +251,7 @@ public static Bitmap rotateImageExif(@NonNull BitmapPool pool, @NonNull Bitmap i

matrix.postTranslate(-newRect.left, -newRect.top);

final Canvas canvas = new Canvas(result);
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
clear(canvas);

applyMatrix(inBitmap, result, matrix);
return result;
}

Expand Down Expand Up @@ -275,15 +283,20 @@ public static Bitmap circleCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitm

Bitmap result = pool.get(destWidth, destHeight, getSafeConfig(toTransform));
setAlphaIfAvailable(result, true /*hasAlpha*/);
Canvas canvas = new Canvas(result);

// Draw a circle
canvas.drawCircle(destRect.left + radius, destRect.top + radius, radius,
CIRCLE_CROP_SHAPE_PAINT);

// Draw the bitmap in the circle
canvas.drawBitmap(toTransform, srcRect, destRect, CIRCLE_CROP_BITMAP_PAINT);
clear(canvas);
TransformationUtils.getBitmapDrawableLock().lock();
try {
Canvas canvas = new Canvas(result);
// Draw a circle
canvas.drawCircle(destRect.left + radius, destRect.top + radius, radius,
CIRCLE_CROP_SHAPE_PAINT);
// Draw the bitmap in the circle
canvas.drawBitmap(toTransform, srcRect, destRect, CIRCLE_CROP_BITMAP_PAINT);
clear(canvas);
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}

if (!toTransform.equals(inBitmap)) {
pool.put(toTransform);
Expand Down Expand Up @@ -335,10 +348,15 @@ public static Bitmap roundedCorners(@NonNull BitmapPool pool, @NonNull Bitmap in
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0, 0, result.getWidth(), result.getHeight());
Canvas canvas = new Canvas(result);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawRoundRect(rect, roundingRadius, roundingRadius, paint);
clear(canvas);
TransformationUtils.getBitmapDrawableLock().lock();
try {
Canvas canvas = new Canvas(result);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawRoundRect(rect, roundingRadius, roundingRadius, paint);
clear(canvas);
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}

if (!toTransform.equals(inBitmap)) {
pool.put(toTransform);
Expand All @@ -356,6 +374,18 @@ private static Bitmap.Config getSafeConfig(Bitmap bitmap) {
return bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888;
}

private static void applyMatrix(@NonNull Bitmap inBitmap, @NonNull Bitmap targetBitmap,
Matrix matrix) {
BITMAP_DRAWABLE_LOCK.lock();
try {
Canvas canvas = new Canvas(targetBitmap);
canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT);
clear(canvas);
} finally {
BITMAP_DRAWABLE_LOCK.unlock();
}
}

// Visible for testing.
static void initializeMatrixForRotation(int exifOrientation, Matrix matrix) {
switch (exifOrientation) {
Expand Down Expand Up @@ -387,4 +417,37 @@ static void initializeMatrixForRotation(int exifOrientation, Matrix matrix) {
// Do nothing.
}
}

private static final class NoLock implements Lock {
@Override
public void lock() {
// do nothing
}

@Override
public void lockInterruptibly() throws InterruptedException {
// do nothing
}

@Override
public boolean tryLock() {
return true;
}

@Override
public boolean tryLock(long time, @NonNull TimeUnit unit) throws InterruptedException {
return true;
}

@Override
public void unlock() {
// do nothing
}

@NonNull
@Override
public Condition newCondition() {
throw new UnsupportedOperationException("Should not be called");
}
}
}

0 comments on commit 2fa00cf

Please sign in to comment.