diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java index 065e9796a9..894eef7085 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java @@ -196,12 +196,12 @@ private Bitmap decodeFromWrappedStreams(InputStream is, } // Visible for testing. - static void calculateScaling(DownsampleStrategy downsampleStrategy, int degreesToRotate, + static float calculateScaling(DownsampleStrategy downsampleStrategy, int degreesToRotate, int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight, BitmapFactory.Options options) { // We can't downsample source content if we can't determine its dimensions. if (sourceWidth <= 0 || sourceHeight <= 0) { - return; + return 1; } int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight; @@ -248,17 +248,6 @@ static void calculateScaling(DownsampleStrategy downsampleStrategy, int degreesT float adjustedScaleFactor = powerOfTwoSampleSize * exactScaleFactor; options.inSampleSize = powerOfTwoSampleSize; - // Density scaling is only supported if inBitmap is null prior to KitKat. Avoid setting - // densities here so we calculate the final Bitmap size correctly. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - options.inTargetDensity = (int) (1000 * adjustedScaleFactor + 0.5f); - options.inDensity = 1000; - } - if (isScaling(options)) { - options.inScaled = true; - } else { - options.inDensity = options.inTargetDensity = 0; - } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Calculate scaling" @@ -270,6 +259,7 @@ static void calculateScaling(DownsampleStrategy downsampleStrategy, int degreesT + ", target density: " + options.inTargetDensity + ", density: " + options.inDensity); } + return adjustedScaleFactor; } private int getOrientation(InputStream is) throws IOException { diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java index 8f5b9248bb..1d45aae00e 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/TransformationUtils.java @@ -30,6 +30,8 @@ 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 Object LOCK = new Object(); + static { CIRCLE_CROP_BITMAP_PAINT = new Paint(CIRCLE_CROP_PAINT_FLAGS); CIRCLE_CROP_BITMAP_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); @@ -45,7 +47,7 @@ private TransformationUtils() { * Bitmap with the given dimensions is passed in as well. * * @param pool The BitmapPool to obtain a bitmap from. - * @param inBitmap The Bitmap to resize. + * @param inBitmap The Bitmap to resize. * @param width The width in pixels of the final Bitmap. * @param height The height in pixels of the final Bitmap. * @return The resized Bitmap (will be recycled if recycled is not null). @@ -74,9 +76,11 @@ 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); + synchronized (LOCK) { + Canvas canvas = new Canvas(result); + canvas.drawBitmap(inBitmap, m, DEFAULT_PAINT); + clear(canvas); + } return result; } @@ -84,10 +88,10 @@ public static Bitmap centerCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitm * An expensive operation to resize the given Bitmap down so that it fits within the given * dimensions maintain the original proportions. * - * @param pool The BitmapPool obtain a bitmap from. - * @param inBitmap The Bitmap to shrink. - * @param width The width in pixels the final image will fit within. - * @param height The height in pixels the final image will fit within. + * @param pool The BitmapPool obtain a bitmap from. + * @param inBitmap The Bitmap to shrink. + * @param width The width in pixels the final image will fit within. + * @param height The height in pixels the final image will fit within. * @return A new Bitmap shrunk to fit within the given dimensions, or toFit if toFit's width or * height matches the given dimensions and toFit fits within the given dimensions */ @@ -129,11 +133,13 @@ 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); + synchronized (LOCK) { + Canvas canvas = new Canvas(toReuse); + Matrix matrix = new Matrix(); + matrix.setScale(minPercentage, minPercentage); + canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT); + clear(canvas); + } return toReuse; } @@ -143,9 +149,9 @@ public static Bitmap fitCenter(@NonNull BitmapPool pool, @NonNull Bitmap inBitma * transform. This keeps {@link android.graphics.Bitmap#hasAlpha()}} consistent before and after * the transformation for transformations that don't add or remove transparent pixels. * - * @param inBitmap The {@link android.graphics.Bitmap} that will be transformed. - * @param outBitmap The {@link android.graphics.Bitmap} that will be returned from the - * transformation. + * @param inBitmap The {@link android.graphics.Bitmap} that will be transformed. + * @param outBitmap The {@link android.graphics.Bitmap} that will be returned from the + * transformation. */ public static void setAlpha(Bitmap inBitmap, Bitmap outBitmap) { setAlphaIfAvailable(outBitmap, inBitmap.hasAlpha()); @@ -240,21 +246,22 @@ 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); - + synchronized (LOCK) { + final Canvas canvas = new Canvas(result); + canvas.drawBitmap(inBitmap, matrix, DEFAULT_PAINT); + clear(canvas); + } return result; } /** - * Crop the image to a circle and resize to the specified width/height. The circle crop will - * have the same width and height equal to the min-edge of the result image. + * Crop the image to a circle and resize to the specified width/height. The circle crop will have + * the same width and height equal to the min-edge of the result image. * - * @param pool The BitmapPool obtain a bitmap from. + * @param pool The BitmapPool obtain a bitmap from. * @param inBitmap The Bitmap to resize. - * @param destWidth The width in pixels of the final Bitmap. - * @param destHeight The height in pixels of the final Bitmap. + * @param destWidth The width in pixels of the final Bitmap. + * @param destHeight The height in pixels of the final Bitmap. * @return The resized Bitmap (will be recycled if recycled is not null). */ public static Bitmap circleCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitmap, @@ -275,15 +282,18 @@ 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); + synchronized (LOCK) { + 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); + // Draw the bitmap in the circle + canvas.drawBitmap(toTransform, srcRect, destRect, CIRCLE_CROP_BITMAP_PAINT); + clear(canvas); + } if (!toTransform.equals(inBitmap)) { pool.put(toTransform); @@ -310,9 +320,9 @@ private static Bitmap getAlphaSafeBitmap(@NonNull BitmapPool pool, /** * Creates a bitmap from a source bitmap and rounds the corners. * - * @param inBitmap the source bitmap to use as a basis for the created bitmap. - * @param width the width of the generated bitmap. - * @param height the height of the generated bitmap. + * @param inBitmap the source bitmap to use as a basis for the created bitmap. + * @param width the width of the generated bitmap. + * @param height the height of the generated bitmap. * @param roundingRadius the corner radius to be applied (in device-specific pixels). * @return a {@link Bitmap} similar to inBitmap but with rounded corners. * @throws IllegalArgumentException if roundingRadius, width or height is 0 or less. @@ -335,10 +345,13 @@ 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); + + synchronized (LOCK) { + Canvas canvas = new Canvas(result); + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + canvas.drawRoundRect(rect, roundingRadius, roundingRadius, paint); + clear(canvas); + } if (!toTransform.equals(inBitmap)) { pool.put(toTransform); diff --git a/library/src/test/java/com/bumptech/glide/load/resource/bitmap/DownsamplerTest.java b/library/src/test/java/com/bumptech/glide/load/resource/bitmap/DownsamplerTest.java index c88e3081a9..1845e1b6c9 100644 --- a/library/src/test/java/com/bumptech/glide/load/resource/bitmap/DownsamplerTest.java +++ b/library/src/test/java/com/bumptech/glide/load/resource/bitmap/DownsamplerTest.java @@ -155,19 +155,19 @@ public void testCalculateScaling_withNone() { private static void runScaleTest(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight, DownsampleStrategy strategy, int expectedWidth, int expectedHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); - Downsampler.calculateScaling(strategy, 0, sourceWidth, sourceHeight, targetWidth, targetHeight, - options); - assertSize(sourceWidth, sourceHeight, expectedWidth, expectedHeight, options); + float adjustedScaleFactor = Downsampler.calculateScaling(strategy, 0, sourceWidth, sourceHeight, + targetWidth, targetHeight, options); + assertSize(sourceWidth, sourceHeight, expectedWidth, expectedHeight, options, + adjustedScaleFactor); } private static void assertSize(int sourceWidth, int sourceHeight, int expectedWidth, - int expectedHeight, BitmapFactory.Options options) { + int expectedHeight, BitmapFactory.Options options, float adjustedScaleFactor) { float sampleSize = Math.max(1, options.inSampleSize); int downsampledWidth = (int) ((sourceWidth / sampleSize) + 0.5f); int downsampledHeight = (int) ((sourceHeight / sampleSize) + 0.5f); - float scaleFactor = options.inScaled && options.inTargetDensity > 0 && options.inDensity > 0 - ? options.inTargetDensity / (float) options.inDensity : 1f; + float scaleFactor = adjustedScaleFactor; int scaledWidth = (int) Math.ceil(downsampledWidth * scaleFactor); int scaledHeight = (int) Math.ceil(downsampledHeight * scaleFactor);