6
6
import android .graphics .BitmapFactory ;
7
7
import android .graphics .ColorSpace ;
8
8
import android .os .Build ;
9
+ import android .os .ParcelFileDescriptor ;
9
10
import android .util .DisplayMetrics ;
10
11
import android .util .Log ;
11
12
import androidx .annotation .Nullable ;
13
+ import androidx .annotation .RequiresApi ;
12
14
import com .bumptech .glide .load .DecodeFormat ;
13
15
import com .bumptech .glide .load .ImageHeaderParser ;
14
16
import com .bumptech .glide .load .ImageHeaderParser .ImageType ;
15
- import com .bumptech .glide .load .ImageHeaderParserUtils ;
16
17
import com .bumptech .glide .load .Option ;
17
18
import com .bumptech .glide .load .Options ;
18
19
import com .bumptech .glide .load .PreferredColorSpace ;
20
+ import com .bumptech .glide .load .data .ParcelFileDescriptorRewinder ;
19
21
import com .bumptech .glide .load .engine .Resource ;
20
22
import com .bumptech .glide .load .engine .bitmap_recycle .ArrayPool ;
21
23
import com .bumptech .glide .load .engine .bitmap_recycle .BitmapPool ;
42
44
*/
43
45
public final class Downsampler {
44
46
static final String TAG = "Downsampler" ;
47
+
45
48
/**
46
49
* Indicates the {@link com.bumptech.glide.load.DecodeFormat} that will be used in conjunction
47
50
* with the image format to determine the {@link android.graphics.Bitmap.Config} to provide to
@@ -130,9 +133,6 @@ public void onDecodeComplete(BitmapPool bitmapPool, Bitmap downsampled) {
130
133
ImageHeaderParser .ImageType .PNG_A ,
131
134
ImageHeaderParser .ImageType .PNG ));
132
135
private static final Queue <BitmapFactory .Options > OPTIONS_QUEUE = Util .createQueue (0 );
133
- // 10MB. This is the max image header size we can handle, we preallocate a much smaller buffer
134
- // but will resize up to this amount if necessary.
135
- private static final int MARK_POSITION = 10 * 1024 * 1024 ;
136
136
137
137
private final BitmapPool bitmapPool ;
138
138
private final DisplayMetrics displayMetrics ;
@@ -161,6 +161,10 @@ public boolean handles(@SuppressWarnings("unused") ByteBuffer byteBuffer) {
161
161
return true ;
162
162
}
163
163
164
+ public boolean handles (@ SuppressWarnings ("unused" ) ParcelFileDescriptor source ) {
165
+ return ParcelFileDescriptorRewinder .isSupported ();
166
+ }
167
+
164
168
/**
165
169
* Returns a Bitmap decoded from the given {@link InputStream} that is rotated to match any EXIF
166
170
* data present in the stream and that is downsampled according to the given dimensions and any
@@ -183,10 +187,6 @@ public Resource<Bitmap> decode(InputStream is, int outWidth, int outHeight, Opti
183
187
* of the image for the given InputStream is available, the operation is much less expensive in
184
188
* terms of memory.
185
189
*
186
- * <p>The provided {@link java.io.InputStream} must return <code>true</code> from {@link
187
- * java.io.InputStream#markSupported()} and is expected to support a reasonably large mark limit
188
- * to accommodate reading large image headers (~5MB).
189
- *
190
190
* @param is An {@link InputStream} to the data for the image.
191
191
* @param requestedWidth The width the final image should be close to.
192
192
* @param requestedHeight The height the final image should be close to.
@@ -197,17 +197,41 @@ public Resource<Bitmap> decode(InputStream is, int outWidth, int outHeight, Opti
197
197
* @return A new bitmap containing the image from the given InputStream, or recycle if recycle is
198
198
* not null.
199
199
*/
200
- @ SuppressWarnings ({"resource" , "deprecation" })
201
200
public Resource <Bitmap > decode (
202
201
InputStream is ,
203
202
int requestedWidth ,
204
203
int requestedHeight ,
205
204
Options options ,
206
205
DecodeCallbacks callbacks )
207
206
throws IOException {
208
- Preconditions .checkArgument (
209
- is .markSupported (), "You must provide an InputStream that supports" + " mark()" );
207
+ return decode (
208
+ new ImageReader .InputStreamImageReader (is , parsers , byteArrayPool ),
209
+ requestedWidth ,
210
+ requestedHeight ,
211
+ options ,
212
+ callbacks );
213
+ }
214
+
215
+ @ RequiresApi (Build .VERSION_CODES .LOLLIPOP )
216
+ public Resource <Bitmap > decode (
217
+ ParcelFileDescriptor parcelFileDescriptor , int outWidth , int outHeight , Options options )
218
+ throws IOException {
219
+ return decode (
220
+ new ImageReader .ParcelFileDescriptorImageReader (
221
+ parcelFileDescriptor , parsers , byteArrayPool ),
222
+ outWidth ,
223
+ outHeight ,
224
+ options ,
225
+ EMPTY_CALLBACKS );
226
+ }
210
227
228
+ private Resource <Bitmap > decode (
229
+ ImageReader imageReader ,
230
+ int requestedWidth ,
231
+ int requestedHeight ,
232
+ Options options ,
233
+ DecodeCallbacks callbacks )
234
+ throws IOException {
211
235
byte [] bytesForOptions = byteArrayPool .get (ArrayPool .STANDARD_BUFFER_SIZE_BYTES , byte [].class );
212
236
BitmapFactory .Options bitmapFactoryOptions = getDefaultOptions ();
213
237
bitmapFactoryOptions .inTempStorage = bytesForOptions ;
@@ -222,7 +246,7 @@ public Resource<Bitmap> decode(
222
246
try {
223
247
Bitmap result =
224
248
decodeFromWrappedStreams (
225
- is ,
249
+ imageReader ,
226
250
bitmapFactoryOptions ,
227
251
downsampleStrategy ,
228
252
decodeFormat ,
@@ -240,7 +264,7 @@ public Resource<Bitmap> decode(
240
264
}
241
265
242
266
private Bitmap decodeFromWrappedStreams (
243
- InputStream is ,
267
+ ImageReader imageReader ,
244
268
BitmapFactory .Options options ,
245
269
DownsampleStrategy downsampleStrategy ,
246
270
DecodeFormat decodeFormat ,
@@ -253,7 +277,7 @@ private Bitmap decodeFromWrappedStreams(
253
277
throws IOException {
254
278
long startTime = LogTime .getLogTime ();
255
279
256
- int [] sourceDimensions = getDimensions (is , options , callbacks , bitmapPool );
280
+ int [] sourceDimensions = getDimensions (imageReader , options , callbacks , bitmapPool );
257
281
int sourceWidth = sourceDimensions [0 ];
258
282
int sourceHeight = sourceDimensions [1 ];
259
283
String sourceMimeType = options .outMimeType ;
@@ -266,7 +290,7 @@ private Bitmap decodeFromWrappedStreams(
266
290
isHardwareConfigAllowed = false ;
267
291
}
268
292
269
- int orientation = ImageHeaderParserUtils . getOrientation ( parsers , is , byteArrayPool );
293
+ int orientation = imageReader . getImageOrientation ( );
270
294
int degreesToRotate = TransformationUtils .getExifOrientationDegrees (orientation );
271
295
boolean isExifOrientationRequired = TransformationUtils .isExifOrientationRequired (orientation );
272
296
@@ -279,11 +303,11 @@ private Bitmap decodeFromWrappedStreams(
279
303
? (isRotationRequired (degreesToRotate ) ? sourceWidth : sourceHeight )
280
304
: requestedHeight ;
281
305
282
- ImageType imageType = ImageHeaderParserUtils . getType ( parsers , is , byteArrayPool );
306
+ ImageType imageType = imageReader . getImageType ( );
283
307
284
308
calculateScaling (
285
309
imageType ,
286
- is ,
310
+ imageReader ,
287
311
callbacks ,
288
312
bitmapPool ,
289
313
downsampleStrategy ,
@@ -294,7 +318,7 @@ private Bitmap decodeFromWrappedStreams(
294
318
targetHeight ,
295
319
options );
296
320
calculateConfig (
297
- is ,
321
+ imageReader ,
298
322
decodeFormat ,
299
323
isHardwareConfigAllowed ,
300
324
isExifOrientationRequired ,
@@ -363,7 +387,7 @@ private Bitmap decodeFromWrappedStreams(
363
387
options .inPreferredColorSpace = ColorSpace .get (ColorSpace .Named .SRGB );
364
388
}
365
389
366
- Bitmap downsampled = decodeStream (is , options , callbacks , bitmapPool );
390
+ Bitmap downsampled = decodeStream (imageReader , options , callbacks , bitmapPool );
367
391
callbacks .onDecodeComplete (bitmapPool , downsampled );
368
392
369
393
if (Log .isLoggable (TAG , Log .VERBOSE )) {
@@ -395,7 +419,7 @@ private Bitmap decodeFromWrappedStreams(
395
419
396
420
private static void calculateScaling (
397
421
ImageType imageType ,
398
- InputStream is ,
422
+ ImageReader imageReader ,
399
423
DecodeCallbacks decodeCallbacks ,
400
424
BitmapPool bitmapPool ,
401
425
DownsampleStrategy downsampleStrategy ,
@@ -524,7 +548,7 @@ private static void calculateScaling(
524
548
|| orientedSourceHeight % powerOfTwoSampleSize != 0 ) {
525
549
// If we're not confident the image is in one of our types, fall back to checking the
526
550
// dimensions again. inJustDecodeBounds decodes do obey inSampleSize.
527
- int [] dimensions = getDimensions (is , options , decodeCallbacks , bitmapPool );
551
+ int [] dimensions = getDimensions (imageReader , options , decodeCallbacks , bitmapPool );
528
552
// Power of two downsampling in BitmapFactory uses a variety of random factors to determine
529
553
// rounding that we can't reliably replicate for all image formats. Use ceiling here to make
530
554
// sure that we at least provide a Bitmap that's large enough to fit the content we're going
@@ -626,7 +650,7 @@ private boolean shouldUsePool(ImageType imageType) {
626
650
627
651
@ SuppressWarnings ("deprecation" )
628
652
private void calculateConfig (
629
- InputStream is ,
653
+ ImageReader imageReader ,
630
654
DecodeFormat format ,
631
655
boolean isHardwareConfigAllowed ,
632
656
boolean isExifOrientationRequired ,
@@ -652,7 +676,7 @@ private void calculateConfig(
652
676
653
677
boolean hasAlpha = false ;
654
678
try {
655
- hasAlpha = ImageHeaderParserUtils . getType ( parsers , is , byteArrayPool ).hasAlpha ();
679
+ hasAlpha = imageReader . getImageType ( ).hasAlpha ();
656
680
} catch (IOException e ) {
657
681
if (Log .isLoggable (TAG , Log .DEBUG )) {
658
682
Log .d (
@@ -674,39 +698,39 @@ private void calculateConfig(
674
698
/**
675
699
* A method for getting the dimensions of an image from the given InputStream.
676
700
*
677
- * @param is The InputStream representing the image.
701
+ * @param imageReader The {@link ImageReader} representing the image.
678
702
* @param options The options to pass to {@link BitmapFactory#decodeStream(java.io.InputStream,
679
703
* android.graphics.Rect, android.graphics.BitmapFactory.Options)}.
680
704
* @return an array containing the dimensions of the image in the form {width, height}.
681
705
*/
682
706
private static int [] getDimensions (
683
- InputStream is ,
707
+ ImageReader imageReader ,
684
708
BitmapFactory .Options options ,
685
709
DecodeCallbacks decodeCallbacks ,
686
710
BitmapPool bitmapPool )
687
711
throws IOException {
688
712
options .inJustDecodeBounds = true ;
689
- decodeStream (is , options , decodeCallbacks , bitmapPool );
713
+ decodeStream (imageReader , options , decodeCallbacks , bitmapPool );
690
714
options .inJustDecodeBounds = false ;
691
715
return new int [] {options .outWidth , options .outHeight };
692
716
}
693
717
694
718
private static Bitmap decodeStream (
695
- InputStream is ,
719
+ ImageReader imageReader ,
696
720
BitmapFactory .Options options ,
697
721
DecodeCallbacks callbacks ,
698
722
BitmapPool bitmapPool )
699
723
throws IOException {
700
- if (options .inJustDecodeBounds ) {
701
- is .mark (MARK_POSITION );
702
- } else {
724
+ if (!options .inJustDecodeBounds ) {
703
725
// Once we've read the image header, we no longer need to allow the buffer to expand in
704
726
// size. To avoid unnecessary allocations reading image data, we fix the mark limit so that it
705
727
// is no larger than our current buffer size here. We need to do so immediately before
706
728
// decoding the full image to avoid having our mark limit overridden by other calls to
707
729
// mark and reset. See issue #225.
708
730
callbacks .onObtainBounds ();
731
+ imageReader .stopGrowingBuffers ();
709
732
}
733
+
710
734
// BitmapFactory.Options out* variables are reset by most calls to decodeStream, successful or
711
735
// otherwise, so capture here in case we log below.
712
736
int sourceWidth = options .outWidth ;
@@ -715,7 +739,7 @@ private static Bitmap decodeStream(
715
739
final Bitmap result ;
716
740
TransformationUtils .getBitmapDrawableLock ().lock ();
717
741
try {
718
- result = BitmapFactory . decodeStream ( is , null , options );
742
+ result = imageReader . decodeBitmap ( options );
719
743
} catch (IllegalArgumentException e ) {
720
744
IOException bitmapAssertionException =
721
745
newIoExceptionForInBitmapAssertion (e , sourceWidth , sourceHeight , outMimeType , options );
@@ -727,10 +751,9 @@ private static Bitmap decodeStream(
727
751
}
728
752
if (options .inBitmap != null ) {
729
753
try {
730
- is .reset ();
731
754
bitmapPool .put (options .inBitmap );
732
755
options .inBitmap = null ;
733
- return decodeStream (is , options , callbacks , bitmapPool );
756
+ return decodeStream (imageReader , options , callbacks , bitmapPool );
734
757
} catch (IOException resetException ) {
735
758
throw bitmapAssertionException ;
736
759
}
@@ -740,9 +763,6 @@ private static Bitmap decodeStream(
740
763
TransformationUtils .getBitmapDrawableLock ().unlock ();
741
764
}
742
765
743
- if (options .inJustDecodeBounds ) {
744
- is .reset ();
745
- }
746
766
return result ;
747
767
}
748
768
0 commit comments