/**
   * Draw an image squeezed along the horizontal and/or vertical dimensions (no preservation of
   * aspect ratio) centered at (x, y).
   *
   * @param g - object to draw onto
   * @param x - horizontal center of where to draw the resulting image
   * @param y - vertical center of where to draw the resulting image
   * @param inputImageARGB
   * @param outputImageARGB - often this is the same as inputImageARGB for speed
   * @param srcW - source image width
   * @param srcH - source image height
   * @param maxW - target width
   * @param maxH - target height
   * @param processAlpha - set false for speed if the image is opaque
   */
  public static void drawFlipshade(
      final Graphics g,
      final int x,
      final int y,
      final int[] inputImageARGB,
      final int[] outputImageARGB,
      int srcW,
      int srcH,
      int maxW,
      int maxH,
      final boolean processAlpha) {
    if (maxW < 1 || maxH < 1 || srcW < 1 || srcH < 1) {
      throw new IllegalArgumentException(
          "drawFlipshade requires maxW, maxH, srcW and srcH be >= 1");
    }
    if (inputImageARGB == null || outputImageARGB == null) {
      throw new NullPointerException(
          "drawFlipshade requires non-null input and output image buffers");
    }

    while (srcW >> 1 > maxW && srcH >> 1 > maxH) {
      JMEImageUtils.half(inputImageARGB, inputImageARGB, srcW, srcH >>= 1);
      srcW >>= 1;
    }
    if (srcW >> 1 == maxW && srcH >> 1 == maxH) {
      JMEImageUtils.half(inputImageARGB, outputImageARGB, maxW, maxH);
    } else {
      maxW = Math.min(srcW, maxW);
      maxH = Math.min(srcH, maxH);
      JMEImageUtils.fivePointSampleDownscale(
          inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH);
    }
    g.drawRGB(outputImageARGB, 0, maxW, x - (maxW >> 1), y - (maxH >> 1), maxW, maxH, processAlpha);
  }
  /**
   * Return an image which is, if needed to fit inside a bounding box, down-scaled and smaller then
   * the original on one or both axes. The image will not be up-scaled if smaller than the bounding
   * box.
   *
   * <p>Using this variant receiving an <source>int[]</source> instead of <source>Image</source>
   * allows you to reduce peak memory usage during scaling. This reduces the likelihood you will see
   * OutOfMemory problems when scaling. A low-memory example usage pattern is below. The
   * <source>Task.LARGE_MEMORY_MUTEX</source> prevents multiple threads from entering memory-spike
   * sections simultaneously. The source and result data arrays can be the same for maximum speed
   * and minimal memory consumption.
   *
   * <p><source>
   *
   * <pre>synchronized (Task.LARGE_MEMORY_MUTEX) {
   *    final int[] data = new int[w * h];
   *    image.getRGB(data, 0, w, 0, 0, w, h);
   *    image = null;
   *    image = JMEImageUtils.scaleImage(data, data, w, h, maxW, maxH, true,
   *       false, false);
   * }</pre>
   *
   * </source>
   *
   * @param inputImageARGB - ARGB data for the original image
   * @param outputImageARGB - ARGB data buffer for the scaled image, can be the same as
   *     inputImageARGB if downscaling (faster)
   * @param srcW - Source image width
   * @param srcH - Source image height
   * @param maxW - maximum (bounding box, will not upscale) width of scaled image
   * @param maxH - maximum (bounding box, will not upscale) height of scaled image
   * @param preserveAspectRatio - set true except for special effects
   * @param scalingAlgorithm - a constant from JMEImageUtils specifying how to scale
   * @return
   */
  public static Image scaleImage(
      final int[] inputImageARGB,
      final int[] outputImageARGB,
      int srcW,
      int srcH,
      int maxW,
      int maxH,
      final boolean preserveAspectRatio,
      final int scalingAlgorithm) {
    if (scalingAlgorithm < 0 || scalingAlgorithm > MAX_SCALING_ALGORITHM) {
      throw new IllegalArgumentException(
          "Unsupported scaling algorithm "
              + scalingAlgorithm
              + ", should be [0-"
              + MAX_SCALING_ALGORITHM
              + "]");
    }
    if (maxW < 1 || maxH < 1 || srcW < 1 || srcH < 1) {
      throw new IllegalArgumentException("scaleImage requires maxW, maxH, srcW and srcH be >= 1");
    }
    if (inputImageARGB == null || outputImageARGB == null) {
      throw new NullPointerException("scaleImage requires non-null input and output image buffers");
    }

    final float byWidth = maxW / (float) srcW;
    final float byHeight = maxH / (float) srcH;
    boolean widthIsMaxed = false;

    if (preserveAspectRatio) {
      if (byWidth <= byHeight) {
        maxW = (int) (srcW * byWidth);
        maxH = (int) (srcH * byWidth);
      } else {
        maxW = (int) (srcW * byHeight);
        maxH = (int) (srcH * byHeight);
      }
    }
    if (maxW >= srcW) {
      maxW = srcW;
      widthIsMaxed = true;
    }
    final boolean processAlpha = scalingAlgorithm != JMEImageUtils.WEIGHTED_AVERAGE_OPAQUE;
    if (maxH >= srcH) {
      if (widthIsMaxed) {
        // No resize needed
        maxH = srcH;
        return Image.createRGBImage(inputImageARGB, maxW, maxH, processAlpha);
      }
      maxH = srcH;
    }
    switch (scalingAlgorithm) {
      default:
      case ONE_POINT_PICK:
        while (srcW >> 1 > maxW && srcH >> 1 > maxH) {
          JMEImageUtils.half(inputImageARGB, inputImageARGB, srcW, srcH);
          srcW /= 2;
          srcH /= 2;
        }
        if (srcW >> 1 == maxW && srcH >> 1 == maxH) {
          JMEImageUtils.half(inputImageARGB, outputImageARGB, srcW, srcH);
          break;
        }
      case BASIC_ONE_POINT_PICK:
        JMEImageUtils.onePointPick(inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH);
        break;
      case FIVE_POINT_BLEND:
        while (srcW / 2 > maxW && srcH / 2 > maxH) {
          JMEImageUtils.half(inputImageARGB, inputImageARGB, srcW, srcH);
          srcH /= 2;
          srcW /= 2;
        }
        if (srcW / 2 == maxW && srcH / 2 == maxH) {
          JMEImageUtils.half(inputImageARGB, outputImageARGB, srcW, srcH);
          break;
        }
        JMEImageUtils.fivePointSampleDownscale(
            inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH);
        break;
      case WEIGHTED_AVERAGE_TRANSLUCENT:
        if (srcW < maxW || srcH < maxH) {
          JMEImageUtils.pureUpscale(
              inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH, preserveAspectRatio);
        } else {
          JMEImageUtils.pureDownscale(
              inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH, preserveAspectRatio);
        }
        break;
      case WEIGHTED_AVERAGE_OPAQUE:
        JMEImageUtils.pureOpaqueDownscale(
            inputImageARGB, outputImageARGB, srcW, srcH, maxW, maxH, preserveAspectRatio);
        break;
    }

    return Image.createRGBImage(outputImageARGB, maxW, maxH, processAlpha);
  }