content: package im.actor.sdk.util.images.ops;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.net.Uri;
  import android.os.Build;
  import im.actor.sdk.util.images.common.*;
  import im.actor.sdk.util.images.sources.FileSource;
  import im.actor.sdk.util.images.sources.ImageSource;
  import im.actor.sdk.util.images.sources.MemorySource;
  import im.actor.sdk.util.images.sources.UriSource;
  import im.actor.sdk.util.images.BitmapUtil;
  
  import java.io.ByteArrayOutputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  
  /**
   * Image loading
   */
  public class ImageLoading {
  
      private static final int MAX_PIXELS = 1200 * 1200;
      private static final int MAX_PIXELS_HQ = 1500 * 1500;
  
      public static final int JPEG_QUALITY = 80;
      public static final int JPEG_QUALITY_HQ = 90;
      public static final int JPEG_QUALITY_LOW = 55;
  
      /**
       * Loading bitmap without any modifications
       *
       * @param fileName Image file name
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmap(String fileName) throws ImageLoadException {
          return loadBitmap(new FileSource(fileName));
      }
  
      /**
       * Loading bitmap without any modifications
       *
       * @param uri     content uri for bitmap
       * @param context Application Context
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmap(Uri uri, Context context) throws ImageLoadException {
          return loadBitmap(new UriSource(uri, context));
      }
  
      /**
       * Loading bitmap without any modifications
       *
       * @param data image file contents
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmap(byte[] data) throws ImageLoadException {
          return loadBitmap(new MemorySource(data));
      }
  
      /**
       * Loading bitmap with scaling
       *
       * @param fileName Image file name
       * @param scale    divider of size, might be factor of two
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmap(String fileName, int scale) throws ImageLoadException {
          return loadBitmap(new FileSource(fileName), scale);
      }
  
      /**
       * Loading bitmap with scaling
       *
       * @param fileName Image file name
       * @param minW     minimal width
       * @param minH     minimal height
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmap(String fileName, int minW, int minH) throws ImageLoadException {
          return loadBitmapOptimized(new FileSource(fileName), minW, minH);
      }
  
      /**
       * Loading bitmap with optimized loaded size less than 1.4 MPX
       *
       * @param uri     content uri for bitmap
       * @param context Application Context
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmapOptimized(Uri uri, Context context) throws ImageLoadException {
          return loadBitmapOptimized(uri, context, MAX_PIXELS);
      }
  
      /**
       * Loading bitmap with optimized loaded size less than 1.4 MPX
       *
       * @param fileName Image file name
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmapOptimized(String fileName) throws ImageLoadException {
          return loadBitmapOptimized(fileName, MAX_PIXELS);
      }
  
      /**
       * Loading bitmap with optimized loaded size less than 2.2 MPX
       *
       * @param fileName Image file name
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmapOptimizedHQ(String fileName) throws ImageLoadException {
          return loadBitmapOptimized(fileName, MAX_PIXELS_HQ);
      }
  
      /**
       * Loading bitmap with optimized loaded size less than specific pixels count
       *
       * @param fileName Image file name
       * @param limit    maximum pixels size
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmapOptimized(String fileName, int limit) throws ImageLoadException {
          return loadBitmapOptimized(new FileSource(fileName), limit);
      }
  
      /**
       * Loading bitmap with optimized loaded size less than specific pixels count
       *
       * @param uri     content uri for bitmap
       * @param context Application Context
       * @param limit   maximum pixels size
       * @return loaded bitmap (always not null)
       * @throws ImageLoadException if it is unable to load file
       */
      public static Bitmap loadBitmapOptimized(Uri uri, Context context, int limit) throws ImageLoadException {
          return loadBitmapOptimized(new UriSource(uri, context) {
          }, limit);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the same size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only in 3.0+
       *
       * @param fileName Image file name
       * @param dest     reuse bitmap
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuseExact(String fileName, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuseExact(new FileSource(fileName), dest);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the same size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only in 3.0+
       *
       * @param data image file contents
       * @param dest reuse bitmap
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuseExact(byte[] data, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuseExact(new MemorySource(data), dest);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the same size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only in 3.0+
       *
       * @param uri     content uri for bitmap
       * @param context Application Context
       * @param dest    reuse bitmap
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuseExact(Uri uri, Context context, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuseExact(new UriSource(uri, context), dest);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the different size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only for Android 4.4+
       *
       * @param fileName Image file name
       * @param dest     reuse bitmap
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuse(String fileName, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuse(new FileSource(fileName), dest);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the different size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only for Android 4.4+
       *
       * @param data image file contents
       * @param dest reuse bitmap
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuse(byte[] data, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuse(new MemorySource(data), dest);
      }
  
      /**
       * Loading bitmap with using reuse bitmap with the different size of source image.
       * If it is unable to load with reuse method tries to load without it.
       * Reuse works only for Android 4.4+
       *
       * @param uri     content uri for bitmap
       * @param dest    destination bitmap
       * @param context Application Context
       * @return result of loading
       * @throws ImageLoadException if it is unable to load file
       */
      public static ReuseResult loadReuse(Uri uri, Context context, Bitmap dest) throws ImageLoadException {
          return loadBitmapReuse(new UriSource(uri, context), dest);
      }
  
  
      /**
       * Saving image in jpeg to byte array with quality 80
       *
       * @param src source image
       * @return saved data
       * @throws ImageSaveException if it is unable to save image
       */
      public static byte[] save(Bitmap src) throws ImageSaveException {
          return save(src, Bitmap.CompressFormat.JPEG, JPEG_QUALITY);
      }
  
      /**
       * Saving image in jpeg to byte array with better quality 90
       *
       * @param src source image
       * @return saved data
       * @throws ImageSaveException if it is unable to save image
       */
      public static byte[] saveHq(Bitmap src) throws ImageSaveException {
          return save(src, Bitmap.CompressFormat.JPEG, JPEG_QUALITY_HQ);
      }
  
      /**
       * Saving image in jpeg to byte array with specific quality
       *
       * @param src     source image
       * @param quality jpeg quality
       * @return saved data
       * @throws ImageSaveException if it is unable to save image
       */
      public static byte[] saveJpeg(Bitmap src, int quality) throws ImageSaveException {
          return save(src, Bitmap.CompressFormat.JPEG, quality);
      }
  
      /**
       * Saving image in png to byte array with specific quality
       *
       * @param src source image
       * @return saved data
       * @throws ImageSaveException if it is unable to save image
       */
      public static byte[] savePng(Bitmap src) throws ImageSaveException {
          return save(src, Bitmap.CompressFormat.PNG, 100);
      }
  
      /**
       * Saving image in jpeg to file with quality 80
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void save(Bitmap src, String fileName) throws ImageSaveException {
          saveJpeg(src, fileName, JPEG_QUALITY);
      }
  
      /**
       * Saving image in jpeg to file with quality 55
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void saveLq(Bitmap src, String fileName) throws ImageSaveException {
          saveJpeg(src, fileName, JPEG_QUALITY_LOW);
      }
  
      /**
       * Saving image in jpeg to file with better quality 90
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void saveHq(Bitmap src, String fileName) throws ImageSaveException {
          saveJpeg(src, fileName, JPEG_QUALITY_HQ);
      }
  
      /**
       * Saving image in jpeg to file with better quality 90
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void saveJpeg(Bitmap src, String fileName, int quality) throws ImageSaveException {
          save(src, fileName, Bitmap.CompressFormat.JPEG, quality);
      }
  
      /**
       * Saving image in png to file
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void savePng(Bitmap src, String fileName) throws ImageSaveException {
          save(src, fileName, Bitmap.CompressFormat.PNG, 100);
      }
  
      /**
       * Saving image in bmp to file
       *
       * @param src      source image
       * @param fileName destination file name
       * @throws ImageSaveException if it is unable to save image
       */
      public static void saveBmp(Bitmap src, String fileName) throws ImageSaveException {
          try {
              BitmapUtil.save(src, fileName);
          } catch (IOException e) {
              throw new ImageSaveException(e);
          }
      }
  
      /**
       * Calculating allocated memory for bitmap
       *
       * @param bitmap source bitmap
       * @return size in bytes
       */
      public static int bitmapSize(Bitmap bitmap) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
              return bitmap.getByteCount();
          } else {
              return bitmap.getRowBytes() * bitmap.getHeight();
          }
      }
  
      /**
       * Loading bitmap from ImageSource
       *
       * @param source image source
       * @return loaded bitmap
       * @throws ImageLoadException if it is unable to load image
       */
      private static Bitmap loadBitmap(ImageSource source) throws ImageLoadException {
          return source.loadBitmap();
      }
  
      /**
       * Loading bitmap from ImageSource with limit of amout of pixels
       *
       * @param source image source
       * @param limit  maximum pixels size
       * @return loaded bitmap
       * @throws ImageLoadException if it is unable to load image
       */
      private static Bitmap loadBitmapOptimized(ImageSource source, int limit) throws ImageLoadException {
          int scale = getScaleFactor(source.getImageMetadata(), limit);
          return loadBitmap(source, scale);
      }
  
  
      /**
       * Loading bitmap from ImageSource with limit of amout of pixels
       *
       * @param source image source
       * @param w      min width
       * @param h      min height
       * @return loaded bitmap
       * @throws ImageLoadException if it is unable to load image
       */
      private static Bitmap loadBitmapOptimized(ImageSource source, int w, int h) throws ImageLoadException {
          int scale = getScaleFactor(source.getImageMetadata(), w, h);
          return loadBitmap(source, scale);
      }
  
      /**
       * Loading bitmap from ImageSource with specific scale
       *
       * @param source image source
       * @param scale  divider of size, might be factor of two
       * @return loaded bitmap
       * @throws ImageLoadException if it is unable to load image
       */
      private static Bitmap loadBitmap(ImageSource source, int scale) throws ImageLoadException {
          return source.loadBitmap(scale);
      }
  
      /**
       * Loading image with reuse bitmap of same size as source
       *
       * @param source image source
       * @param dest   destination bitmap
       * @return loaded bitmap result
       * @throws ImageLoadException if it is unable to load image
       */
      private static ReuseResult loadBitmapReuseExact(ImageSource source, Bitmap dest) throws ImageLoadException {
          ImageMetadata metadata = source.getImageMetadata();
          boolean tryReuse = false;
          if (dest.isMutable()
                  && dest.getWidth() == metadata.getW()
                  && dest.getHeight() == metadata.getH()) {
              if (Build.VERSION.SDK_INT >= 19) {
                  tryReuse = true;
              } else if (Build.VERSION.SDK_INT >= 11) {
                  if (metadata.getFormat() == ImageFormat.JPEG || metadata.getFormat() == ImageFormat.PNG) {
                      tryReuse = true;
                  }
              }
          }
  
          if (tryReuse) {
              return source.loadBitmap(dest);
          } else {
              return new ReuseResult(loadBitmap(source), false);
          }
      }
  
      /**
       * Loading image with reuse bitmap
       *
       * @param source image source
       * @param dest   destination bitmap
       * @return loaded bitmap result
       * @throws ImageLoadException if it is unable to load image
       */
      private static ReuseResult loadBitmapReuse(ImageSource source, Bitmap dest) throws ImageLoadException {
          ImageMetadata metadata = source.getImageMetadata();
          boolean tryReuse = false;
          if (dest.isMutable()) {
              if (Build.VERSION.SDK_INT >= 19) {
                  tryReuse = dest.getAllocationByteCount() >= metadata.getW() * metadata.getH() * 4;
              } else if (Build.VERSION.SDK_INT >= 11) {
                  if (metadata.getFormat() == ImageFormat.JPEG || metadata.getFormat() == ImageFormat.PNG) {
                      tryReuse = dest.getWidth() == metadata.getW()
                              && dest.getHeight() == metadata.getH();
                  }
              }
          }
  
          if (tryReuse) {
              return source.loadBitmap(dest);
          } else {
              return new ReuseResult(loadBitmap(source), false);
          }
      }
  
      /**
       * Saving image to file
       *
       * @param src      source image
       * @param fileName destination file name
       * @param format   image format
       * @param quality  jpeg quality
       * @throws ImageSaveException if it is unable to save image
       */
      private static void save(Bitmap src, String fileName, Bitmap.CompressFormat format, int quality) throws ImageSaveException {
          FileOutputStream outputStream = null;
          try {
              outputStream = new FileOutputStream(fileName);
              src.compress(format, quality, outputStream);
              outputStream.close();
          } catch (IOException e) {
              throw new ImageSaveException(e);
          } finally {
              if (outputStream != null) {
                  try {
                      outputStream.close();
                  } catch (IOException e1) {
                      e1.printStackTrace();
                  }
              }
          }
      }
  
      /**
       * Saving image to byte array
       *
       * @param src     source image
       * @param format  image format
       * @param quality jpeg quality
       * @return saved image
       */
      private static byte[] save(Bitmap src, Bitmap.CompressFormat format, int quality) {
          ByteArrayOutputStream outputStream = null;
          try {
              outputStream = new ByteArrayOutputStream();
              src.compress(format, quality, outputStream);
              return outputStream.toByteArray();
          } finally {
              if (outputStream != null) {
                  try {
                      outputStream.close();
                  } catch (IOException e1) {
                      e1.printStackTrace();
                  }
              }
          }
      }
  
      /**
       * Calculating scale factor with limit of pixel amount
       *
       * @param metadata  image metadata
       * @param maxPixels limit for pixels
       * @return scale factor
       */
      private static int getScaleFactor(ImageMetadata metadata, int maxPixels) {
          int scale = 1;
          int scaledW = metadata.getW();
          int scaledH = metadata.getH();
          while (scaledW * scaledH > maxPixels) {
              scale *= 2;
              scaledH /= 2;
              scaledW /= 2;
          }
          return scale;
      }
  
      /**
       * Calculating scale factor with limit of pixel amount
       *
       * @param metadata image metadata
       * @param minH     minimal height
       * @param minW     minimal width
       * @return scale factor
       */
      private static int getScaleFactor(ImageMetadata metadata, int minW, int minH) {
          int scale = 1;
          int scaledW = metadata.getW();
          int scaledH = metadata.getH();
          while (scaledW / 2 > minW && scaledH / 2 > minH) {
              scale *= 2;
              scaledH /= 2;
              scaledW /= 2;
          }
          return scale;
      }
  
      protected ImageLoading() {
      }
  }
  