Skip to content

Commit ebdf8be

Browse files
committed
Always use orientation corrected sizes in Downsampler#calculateScaling.
Previously we were sometimes using the rotated size and sometimes using the original size. As a result, some combinations of width, height and exif orientation were decoded with unexpected sizes. Fixes #3673
1 parent 41877c6 commit ebdf8be

File tree

2 files changed

+58
-27
lines changed

2 files changed

+58
-27
lines changed

instrumentation/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/DownsamplerEmulatorTest.java

+26
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class DownsamplerEmulatorTest {
5757
@Test
5858
public void calculateScaling_withAtMost() throws IOException {
5959
new Tester(DownsampleStrategy.AT_MOST)
60+
// See #3673
61+
.setTargetDimensions(1977, 2636)
62+
.givenImageWithDimensionsOf(3024, 4032, onAllApisAndAllFormatsExpect(1512, 2016))
6063
.setTargetDimensions(100, 100)
6164
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
6265
.givenSquareImageWithDimensionOf(200, onAllApisAndAllFormatsExpect(100, 100))
@@ -113,6 +116,9 @@ public void calculateScaling_withAtMost() throws IOException {
113116
@Test
114117
public void calculateScaling_withAtLeast() throws IOException {
115118
new Tester(DownsampleStrategy.AT_LEAST)
119+
// See #3673
120+
.setTargetDimensions(1977, 2636)
121+
.givenImageWithDimensionsOf(3024, 4032, onAllApisAndAllFormatsExpect(3024, 4032))
116122
.setTargetDimensions(100, 100)
117123
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
118124
.givenSquareImageWithDimensionOf(200, onAllApisAndAllFormatsExpect(100, 100))
@@ -133,6 +139,12 @@ public void calculateScaling_withAtLeast() throws IOException {
133139
@Test
134140
public void calculateScaling_withCenterInside() throws IOException {
135141
new Tester(DownsampleStrategy.CENTER_INSIDE)
142+
// See #3673
143+
.setTargetDimensions(1977, 2636)
144+
.givenImageWithDimensionsOf(3024, 4032,
145+
atAndAbove(KITKAT).with(allFormats().expect(1977, 2636)),
146+
// TODO(b/134182995): This shouldn't be preserving quality.
147+
below(KITKAT).with(allFormats().expect(3024, 4032)))
136148
.setTargetDimensions(100, 100)
137149
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
138150
.givenSquareImageWithDimensionOf(400, onAllApisAndAllFormatsExpect(100, 100))
@@ -189,6 +201,11 @@ public void calculateScaling_withCenterInside() throws IOException {
189201
@Test
190202
public void calculateScaling_withCenterOutside() throws IOException {
191203
new Tester(DownsampleStrategy.CENTER_OUTSIDE)
204+
// See #3673
205+
.setTargetDimensions(1977, 2636)
206+
.givenImageWithDimensionsOf(3024, 4032,
207+
atAndAbove(KITKAT).with(allFormats().expect(1977, 2636)),
208+
below(KITKAT).with(allFormats().expect(3024, 4032)))
192209
.setTargetDimensions(100, 100)
193210
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
194211
.givenSquareImageWithDimensionOf(200, onAllApisAndAllFormatsExpect(100, 100))
@@ -228,6 +245,9 @@ public void calculateScaling_withCenterOutside() throws IOException {
228245
@Test
229246
public void calculateScaling_withNone() throws IOException {
230247
new Tester(DownsampleStrategy.NONE)
248+
// See #3673
249+
.setTargetDimensions(1977, 2636)
250+
.givenImageWithDimensionsOf(3024, 4032, onAllApisAndAllFormatsExpect(3024, 4032))
231251
.setTargetDimensions(100, 100)
232252
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
233253
.givenSquareImageWithDimensionOf(200, onAllApisAndAllFormatsExpect(200, 200))
@@ -248,6 +268,12 @@ public void calculateScaling_withNone() throws IOException {
248268
@Test
249269
public void calculateScaling_withFitCenter() throws IOException {
250270
new Tester(DownsampleStrategy.FIT_CENTER)
271+
// See #3673
272+
.setTargetDimensions(1977, 2636)
273+
.givenImageWithDimensionsOf(3024, 4032,
274+
atAndAbove(KITKAT).with(allFormats().expect(1977, 2636)),
275+
// TODO(b/134182995): This shouldn't be preserving quality.
276+
below(KITKAT).with(allFormats().expect(3024, 4032)))
251277
.setTargetDimensions(100, 100)
252278
.givenSquareImageWithDimensionOf(100, onAllApisAndAllFormatsExpect(100, 100))
253279
.givenSquareImageWithDimensionOf(200, onAllApisAndAllFormatsExpect(100, 100))

library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java

+32-27
Original file line numberDiff line numberDiff line change
@@ -384,19 +384,21 @@ private static void calculateScaling(
384384
return;
385385
}
386386

387-
final float exactScaleFactor;
387+
int orientedSourceWidth = sourceWidth;
388+
int orientedSourceHeight = sourceHeight;
389+
// If we're rotating the image +-90 degrees, we need to downsample accordingly so the image
390+
// width is decreased to near our target's height and the image height is decreased to near
391+
// our target width.
392+
//noinspection SuspiciousNameCombination
388393
if (degreesToRotate == 90 || degreesToRotate == 270) {
389-
// If we're rotating the image +-90 degrees, we need to downsample accordingly so the image
390-
// width is decreased to near our target's height and the image height is decreased to near
391-
// our target width.
392-
//noinspection SuspiciousNameCombination
393-
exactScaleFactor =
394-
downsampleStrategy.getScaleFactor(sourceHeight, sourceWidth, targetWidth, targetHeight);
395-
} else {
396-
exactScaleFactor =
397-
downsampleStrategy.getScaleFactor(sourceWidth, sourceHeight, targetWidth, targetHeight);
394+
orientedSourceWidth = sourceHeight;
395+
orientedSourceHeight = sourceWidth;
398396
}
399397

398+
final float exactScaleFactor =
399+
downsampleStrategy.getScaleFactor(
400+
orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);
401+
400402
if (exactScaleFactor <= 0f) {
401403
throw new IllegalArgumentException(
402404
"Cannot scale with factor: "
@@ -414,18 +416,19 @@ private static void calculateScaling(
414416
+ targetHeight
415417
+ "]");
416418
}
419+
417420
SampleSizeRounding rounding =
418421
downsampleStrategy.getSampleSizeRounding(
419-
sourceWidth, sourceHeight, targetWidth, targetHeight);
422+
orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);
420423
if (rounding == null) {
421424
throw new IllegalArgumentException("Cannot round with null rounding");
422425
}
423426

424-
int outWidth = round(exactScaleFactor * sourceWidth);
425-
int outHeight = round(exactScaleFactor * sourceHeight);
427+
int outWidth = round(exactScaleFactor * orientedSourceWidth);
428+
int outHeight = round(exactScaleFactor * orientedSourceHeight);
426429

427-
int widthScaleFactor = sourceWidth / outWidth;
428-
int heightScaleFactor = sourceHeight / outHeight;
430+
int widthScaleFactor = orientedSourceWidth / outWidth;
431+
int heightScaleFactor = orientedSourceHeight / outHeight;
429432

430433
int scaleFactor =
431434
rounding == SampleSizeRounding.MEMORY
@@ -458,26 +461,26 @@ private static void calculateScaling(
458461
// After libjpegturbo's native rounding, skia does a secondary scale using floor
459462
// (integer division). Here we replicate that logic.
460463
int nativeScaling = Math.min(powerOfTwoSampleSize, 8);
461-
powerOfTwoWidth = (int) Math.ceil(sourceWidth / (float) nativeScaling);
462-
powerOfTwoHeight = (int) Math.ceil(sourceHeight / (float) nativeScaling);
464+
powerOfTwoWidth = (int) Math.ceil(orientedSourceWidth / (float) nativeScaling);
465+
powerOfTwoHeight = (int) Math.ceil(orientedSourceHeight / (float) nativeScaling);
463466
int secondaryScaling = powerOfTwoSampleSize / 8;
464467
if (secondaryScaling > 0) {
465468
powerOfTwoWidth = powerOfTwoWidth / secondaryScaling;
466469
powerOfTwoHeight = powerOfTwoHeight / secondaryScaling;
467470
}
468471
} else if (imageType == ImageType.PNG || imageType == ImageType.PNG_A) {
469-
powerOfTwoWidth = (int) Math.floor(sourceWidth / (float) powerOfTwoSampleSize);
470-
powerOfTwoHeight = (int) Math.floor(sourceHeight / (float) powerOfTwoSampleSize);
472+
powerOfTwoWidth = (int) Math.floor(orientedSourceWidth / (float) powerOfTwoSampleSize);
473+
powerOfTwoHeight = (int) Math.floor(orientedSourceHeight / (float) powerOfTwoSampleSize);
471474
} else if (imageType == ImageType.WEBP || imageType == ImageType.WEBP_A) {
472475
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
473-
powerOfTwoWidth = Math.round(sourceWidth / (float) powerOfTwoSampleSize);
474-
powerOfTwoHeight = Math.round(sourceHeight / (float) powerOfTwoSampleSize);
476+
powerOfTwoWidth = Math.round(orientedSourceWidth / (float) powerOfTwoSampleSize);
477+
powerOfTwoHeight = Math.round(orientedSourceHeight / (float) powerOfTwoSampleSize);
475478
} else {
476-
powerOfTwoWidth = (int) Math.floor(sourceWidth / (float) powerOfTwoSampleSize);
477-
powerOfTwoHeight = (int) Math.floor(sourceHeight / (float) powerOfTwoSampleSize);
479+
powerOfTwoWidth = (int) Math.floor(orientedSourceWidth / (float) powerOfTwoSampleSize);
480+
powerOfTwoHeight = (int) Math.floor(orientedSourceHeight / (float) powerOfTwoSampleSize);
478481
}
479-
} else if (sourceWidth % powerOfTwoSampleSize != 0
480-
|| sourceHeight % powerOfTwoSampleSize != 0) {
482+
} else if (orientedSourceWidth % powerOfTwoSampleSize != 0
483+
|| orientedSourceHeight % powerOfTwoSampleSize != 0) {
481484
// If we're not confident the image is in one of our types, fall back to checking the
482485
// dimensions again. inJustDecodeBounds decodes do obey inSampleSize.
483486
int[] dimensions = getDimensions(is, options, decodeCallbacks, bitmapPool);
@@ -488,8 +491,8 @@ private static void calculateScaling(
488491
powerOfTwoWidth = dimensions[0];
489492
powerOfTwoHeight = dimensions[1];
490493
} else {
491-
powerOfTwoWidth = sourceWidth / powerOfTwoSampleSize;
492-
powerOfTwoHeight = sourceHeight / powerOfTwoSampleSize;
494+
powerOfTwoWidth = orientedSourceWidth / powerOfTwoSampleSize;
495+
powerOfTwoHeight = orientedSourceHeight / powerOfTwoSampleSize;
493496
}
494497

495498
double adjustedScaleFactor =
@@ -517,6 +520,8 @@ private static void calculateScaling(
517520
+ "x"
518521
+ sourceHeight
519522
+ "]"
523+
+ ", degreesToRotate: "
524+
+ degreesToRotate
520525
+ ", target: ["
521526
+ targetWidth
522527
+ "x"

0 commit comments

Comments
 (0)