private CloseableReference decodeFromStream()

in imagepipeline/src/main/java/com/facebook/imagepipeline/platform/DefaultDecoder.java [189:296]


  private CloseableReference<Bitmap> decodeFromStream(
      InputStream inputStream,
      BitmapFactory.Options options,
      @Nullable Rect regionToDecode,
      @Nullable final ColorSpace colorSpace) {
    Preconditions.checkNotNull(inputStream);
    int targetWidth = options.outWidth;
    int targetHeight = options.outHeight;
    if (regionToDecode != null) {
      targetWidth = regionToDecode.width() / options.inSampleSize;
      targetHeight = regionToDecode.height() / options.inSampleSize;
    }
    @Nullable Bitmap bitmapToReuse = null;
    boolean shouldUseHardwareBitmapConfig = false;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      shouldUseHardwareBitmapConfig =
          mPreverificationHelper != null
              && mPreverificationHelper.shouldUseHardwareBitmapConfig(options.inPreferredConfig);
    }
    if (regionToDecode == null && shouldUseHardwareBitmapConfig) {
      // Cannot reuse bitmaps with Bitmap.Config.HARDWARE
      options.inMutable = false;
    } else {
      if (regionToDecode != null && shouldUseHardwareBitmapConfig) {
        // If region decoding was requested we need to fallback to default config
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
      }
      final int sizeInBytes = getBitmapSize(targetWidth, targetHeight, options);
      bitmapToReuse = mBitmapPool.get(sizeInBytes);
      if (bitmapToReuse == null) {
        throw new NullPointerException("BitmapPool.get returned null");
      }
    }
    // inBitmap can be nullable
    //noinspection ConstantConditions
    options.inBitmap = bitmapToReuse;

    // Performs transformation at load time to target color space.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      options.inPreferredColorSpace =
          colorSpace == null ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
    }

    Bitmap decodedBitmap = null;
    ByteBuffer byteBuffer = mDecodeBuffers.acquire();
    if (byteBuffer == null) {
      byteBuffer = ByteBuffer.allocate(DecodeBufferHelper.getRecommendedDecodeBufferSize());
    }
    try {
      options.inTempStorage = byteBuffer.array();
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
          && regionToDecode != null
          && bitmapToReuse != null) {
        BitmapRegionDecoder bitmapRegionDecoder = null;
        try {
          bitmapToReuse.reconfigure(targetWidth, targetHeight, options.inPreferredConfig);
          bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, true);
          decodedBitmap = bitmapRegionDecoder.decodeRegion(regionToDecode, options);
        } catch (IOException e) {
          FLog.e(TAG, "Could not decode region %s, decoding full bitmap instead.", regionToDecode);
        } finally {
          if (bitmapRegionDecoder != null) {
            bitmapRegionDecoder.recycle();
          }
        }
      }
      if (decodedBitmap == null) {
        decodedBitmap = BitmapFactory.decodeStream(inputStream, null, options);
      }
    } catch (IllegalArgumentException e) {
      if (bitmapToReuse != null) {
        mBitmapPool.release(bitmapToReuse);
      }
      // This is thrown if the Bitmap options are invalid, so let's just try to decode the bitmap
      // as-is, which might be inefficient - but it works.
      try {
        // We need to reset the stream first
        inputStream.reset();

        Bitmap naiveDecodedBitmap = BitmapFactory.decodeStream(inputStream);
        if (naiveDecodedBitmap == null) {
          throw e;
        }
        return CloseableReference.of(naiveDecodedBitmap, SimpleBitmapReleaser.getInstance());
      } catch (IOException re) {
        // We throw the original exception instead since it's the one causing this workaround in the
        // first place.
        throw e;
      }
    } catch (RuntimeException re) {
      if (bitmapToReuse != null) {
        mBitmapPool.release(bitmapToReuse);
      }
      throw re;
    } finally {
      mDecodeBuffers.release(byteBuffer);
    }

    // If bitmap with Bitmap.Config.HARDWARE was used, `bitmapToReuse` will be null and it's
    // expected
    if (bitmapToReuse != null && bitmapToReuse != decodedBitmap) {
      mBitmapPool.release(bitmapToReuse);
      decodedBitmap.recycle();
      throw new IllegalStateException();
    }

    return CloseableReference.of(decodedBitmap, mBitmapPool);
  }