protected Bitmap considerExactScaleAndOrientatiton(
      Bitmap subsampledBitmap,
      ImageDecodingInfo decodingInfo,
      int rotation,
      boolean flipHorizontal) {
    Matrix m = new Matrix();
    // Scale to exact size if need
    ImageScaleType scaleType = decodingInfo.getImageScaleType();
    if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) {
      ImageSize srcSize =
          new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);
      float scale =
          ImageSizeUtils.computeImageScale(
              srcSize,
              decodingInfo.getTargetSize(),
              decodingInfo.getViewScaleType(),
              scaleType == ImageScaleType.EXACTLY_STRETCHED);
      if (Float.compare(scale, 1f) != 0) {
        m.setScale(scale, scale);

        if (loggingEnabled) {
          L.d(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey());
        }
      }
    }
    // Flip bitmap if need
    if (flipHorizontal) {
      m.postScale(-1, 1);

      if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey());
    }
    // Rotate bitmap if need
    if (rotation != 0) {
      m.postRotate(rotation);

      if (loggingEnabled) L.d(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey());
    }

    Bitmap finalBitmap =
        Bitmap.createBitmap(
            subsampledBitmap,
            0,
            0,
            subsampledBitmap.getWidth(),
            subsampledBitmap.getHeight(),
            m,
            true);
    if (finalBitmap != subsampledBitmap) {
      subsampledBitmap.recycle();
    }
    return finalBitmap;
  }
  protected Options prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo) {
    ImageScaleType scaleType = decodingInfo.getImageScaleType();
    int scale;
    if (scaleType == ImageScaleType.NONE) {
      scale = 1;
    } else if (scaleType == ImageScaleType.NONE_SAFE) {
      scale = ImageSizeUtils.computeMinImageSampleSize(imageSize);
    } else {
      ImageSize targetSize = decodingInfo.getTargetSize();
      boolean powerOf2 = scaleType == ImageScaleType.IN_SAMPLE_POWER_OF_2;
      scale =
          ImageSizeUtils.computeImageSampleSize(
              imageSize, targetSize, decodingInfo.getViewScaleType(), powerOf2);
    }
    if (scale > 1 && loggingEnabled) {
      L.d(
          LOG_SUBSAMPLE_IMAGE,
          imageSize,
          imageSize.scaleDown(scale),
          scale,
          decodingInfo.getImageKey());
    }

    Options decodingOptions = decodingInfo.getDecodingOptions();
    decodingOptions.inSampleSize = scale;
    return decodingOptions;
  }
 protected ExifInfo defineExifOrientation(String imageUri) {
   int rotation = 0;
   boolean flip = false;
   try {
     ExifInterface exif = new ExifInterface(Scheme.FILE.crop(imageUri));
     int exifOrientation =
         exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
     switch (exifOrientation) {
       case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
         flip = true;
       case ExifInterface.ORIENTATION_NORMAL:
         rotation = 0;
         break;
       case ExifInterface.ORIENTATION_TRANSVERSE:
         flip = true;
       case ExifInterface.ORIENTATION_ROTATE_90:
         rotation = 90;
         break;
       case ExifInterface.ORIENTATION_FLIP_VERTICAL:
         flip = true;
       case ExifInterface.ORIENTATION_ROTATE_180:
         rotation = 180;
         break;
       case ExifInterface.ORIENTATION_TRANSPOSE:
         flip = true;
       case ExifInterface.ORIENTATION_ROTATE_270:
         rotation = 270;
         break;
     }
   } catch (IOException e) {
     L.w("Can't read EXIF tags from file [%s]", imageUri);
   }
   return new ExifInfo(rotation, flip);
 }
  /**
   * Decodes image from URI into {@link Bitmap}. Image is scaled close to incoming {@linkplain
   * ImageSize target size} during decoding (depend on incoming parameters).
   *
   * @param decodingInfo Needed data for decoding image
   * @return Decoded bitmap
   * @throws IOException if some I/O exception occurs during image reading
   * @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
   */
  @Override
  public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
    Bitmap decodedBitmap;
    ImageFileInfo imageInfo;

    InputStream imageStream = getImageStream(decodingInfo);
    try {
      imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);
      imageStream = resetStream(imageStream, decodingInfo);
      Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
      decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
    } finally {
      IoUtils.closeSilently(imageStream);
    }

    if (decodedBitmap == null) {
      L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());
    } else {
      decodedBitmap =
          considerExactScaleAndOrientatiton(
              decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal);
    }
    return decodedBitmap;
  }