Package com.blackberry.toolkit.ui.images

Source Code of com.blackberry.toolkit.ui.images.ImageManipulator

/*
* Copyright (c) 2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.blackberry.toolkit.ui.images;

import javax.microedition.lcdui.Image;

import net.rim.device.api.math.Fixed32;
import net.rim.device.api.math.VecMath;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.XYDimension;

/**
* Class for Rotating, Mirroring, and Scaling Bitmaps
*
* Supports:
* <ul>
* <li>Transformations by angle around 0,0 and mirroring in X and/or Y axis.</li>
* <li>Transformation by arbitrary transformation matrix.</li>
* <li>Scaling applied independently of any transformation.</li>
* <li>Scaling separately in X and Y axis</li>
* <li>Results can be painted to a new Bitmap, or directly on any Graphics
* context</li>
* </ul>
*
* @version 1.6 (December 2009)
*/
public class ImageManipulator {

  /**
   * This bitmap scaling option marks that scaling should proceed without
   * preserving aspect ratio. The source bitmap is fit to the dimensions of
   * the destination bitmap. Copied from 5.0 Bitmap API.
   *
   * <pre>
   * http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html
   * </pre>
   *
   * @since 1.1
   */
  public static final int SCALE_STRETCH = 0;
  /**
   * This bitmap scaling option marks that scaling is done with preserving the
   * aspect ratio. The source bitmap fills the destination bitmap completely
   * and outstanding parts of the source bitmap are not copied to the
   * destination bitmap. Copied from 5.0 Bitmap API.
   *
   * <pre>
   * http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html
   * </pre>
   *
   * @since 1.1
   */
  public static final int SCALE_TO_FILL = 1;
  /**
   * This bitmap scaling option marks that scaling is done with preserving the
   * aspect ratio. The source bitmap is fit to the dimensions of the
   * destination bitmap and a part of destination bitmap remains unchanged.
   * Copied from 5.0 Bitmap API.
   *
   * <pre>
   * http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html
   * </pre>
   *
   * @since 1.1
   */
  public static final int SCALE_TO_FIT = 2;
  /**
   * Used for compatibility with 5.0. Has no effect, but the values are
   * checked.
   *
   * @since 1.1
   */
  public static final int FILTER_LANCZOS = 0;
  /**
   * Used for compatibility with 5.0. Has no effect, but the values are
   * checked.
   *
   * @since 1.1
   */
  public static final int FILTER_BOX = 1;
  /**
   * Used for compatibility with 5.0. Has no effect, but the values are
   * checked.
   *
   * @since 1.1
   */
  public static final int FILTER_BILINEAR = 2;

  // The image to alter
  private Bitmap bitmap;

  // The graphics context to use
  // if one is not provided, a bitmap will be

  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int UX = 0;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int UY = 3;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int UZ = 6;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int VX = 1;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int VY = 4;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int VZ = 7;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int WX = 2;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int WY = 5;
  /**
   * Constants for Matrix locations within a single dimensional array
   *
   * <pre>
   * | UX  UY  UZ |
   * | VX  VY  VZ |
   * | WX  WY  WZ |
   * </pre>
   *
   * @since 1.3
   */
  public static final int WZ = 8;

  /*
   * the transform matrix, initialized to identity
   */
  private int[] transformMatrix = VecMath.IDENTITY_3X3;
  // set scale in X axis
  private int scaleX = Fixed32.ONE;
  // set scale in Y axis
  private int scaleY = Fixed32.ONE;

  // track separate texture origin for non-orthogonal rotations
  private int textureX;
  private int textureY;

  // track whether the rotation a multiple of 90 degrees
  private boolean orthogonal;

  // actual scale factor when applied to pixels
  private int resultantScaleX = Fixed32.ONE;
  private int resultantScaleY = Fixed32.ONE;

  // Initialize the X and Y point arrays
  // These define the resultant image points
  private int[] bitmapXPts = { 0, 0, 0, 0 };
  private int[] bitmapYPts = { 0, 0, 0, 0 };

  // Store the resulting dimensions of the transformation
  private XYDimension transformedRegion = new XYDimension();

  // Flag whether the transformation has been applied
  private boolean transformationApplied;

  // Constant for drawing in "15.16 fixed point format"
  private static final int SHIFT16 = 1 << 16;

  // Set the background color default white
  private int backgroundColor = Color.WHITE;

  // Set default alpha to transparent
  private int backgroundAlpha = 0;

  // static object for use in the static methods
  private static ImageManipulator imageManipulator;

  /**
   * Scale the provided bitmap
   *
   * @param bitmap
   *            the bitmap to scale
   * @param scale
   *            the new scale factor in Fixed32
   * @return the scaled bitmap
   * @since 1.1
   */
  public static Bitmap scale(Bitmap bitmap, int scale) {
    if (imageManipulator == null) {
      imageManipulator = new ImageManipulator(bitmap);
    } else {
      imageManipulator.setBitmap(bitmap);
    }
    imageManipulator.resetTransform();
    imageManipulator.setScale(scale);
    return imageManipulator.transformAndPaintBitmap();
  }

  /**
   * Rotate the provided Bitmap
   *
   * @param bitmap
   *            the bitmap to rotate
   * @param angle
   *            the angle in degrees to rotate, counterclockwise around the
   *            top left corner.
   * @return the rotated bitmap
   * @since 1.1
   */
  public static Bitmap rotate(Bitmap bitmap, int angle) {
    if (imageManipulator == null) {
      imageManipulator = new ImageManipulator(bitmap);
    } else {
      imageManipulator.setBitmap(bitmap);
    }
    imageManipulator.resetTransform();
    imageManipulator.transformByAngle(angle, false, false);
    return imageManipulator.transformAndPaintBitmap();
  }

  public ImageManipulator(Bitmap image) {
    bitmap = image;
  }

  /**
   * Reset the transformation to the identity: No rotation and Scale is 1.0
   *
   * @since 1.1
   */
  public void resetTransform() {
    transformByMatrix(Fixed32.ONE, 0, 0, Fixed32.ONE);
    orthogonal = true;
    setScale(Fixed32.ONE);
    transformationApplied = false;
  }

  /**
   * Setup the given transformation matrix, given the cosine and sine values,
   * and mirroring options
   *
   * @param matrix
   *            the transform matrix, must be of size 4
   * @param cos
   *            cosine of the angle in radians
   * @param sin
   *            sine of the angle in radians
   * @param mirrorX
   *            to mirror in the X axis
   * @param mirrorY
   *            to mirror in the Y axis
   */
  private void setTransform(int[] matrix, int cos, int sin, boolean mirrorX, boolean mirrorY) {
    // default, non mirrored transformation
    matrix[UX] = cos;
    matrix[UY] = -sin;
    matrix[VX] = sin;
    matrix[VY] = cos;
    if (mirrorY) {
      matrix[UX] = -cos;
      matrix[UY] = sin;
    }
    if (mirrorX) {
      matrix[VX] = -sin;
      matrix[VY] = -cos;
    }
  }

  /**
   * Rotate the image about an angle and/or mirror about an axis
   *
   * @param angle
   *            the angle in degrees, postive rotates counterclockwise
   * @param mirrorX
   *            true to mirror in the X axis
   * @param mirrorY
   *            true to mirror in the Y axis
   */
  public void transformByAngle(int angle, boolean mirrorX, boolean mirrorY) {
    if (angle % 90 == 0) {
      orthogonal = true;
    } else {
      orthogonal = false;
    }
    angle = Fixed32.toFP(angle);
    int cos = Fixed32.cosd(angle);
    int sin = Fixed32.sind(angle);
    setTransform(transformMatrix, cos, sin, mirrorX, mirrorY);
    transformationApplied = false;
  }

  /**
   * Rotate the image using a custom transformation matrix. The matrix takes
   * the form:
   *
   * <pre>
   * | ux  uy 0 |
   * | vx  vy 0 |
   * | 0   0  1 |
   * </pre>
   *
   * Where, the identity (matrix that does no rotation) is:
   *
   * <pre>
   * | 1  0  0 |
   * | 0  1  0 |
   * | 0  0  1 |
   * </pre>
   *
   * @param ux
   *            in Fixed32 format (15.16)
   * @param uy
   *            in Fixed32 format (15.16)
   * @param vx
   *            in Fixed32 format (15.16)
   * @param vy
   *            in Fixed32 format (15.16)
   */
  public void transformByMatrix(int ux, int uy, int vx, int vy) {
    transformMatrix[UX] = ux;
    transformMatrix[UY] = uy;
    transformMatrix[VX] = vx;
    transformMatrix[VY] = vy;
    orthogonal = false; // not completely accurate
    transformationApplied = false;
  }

  /**
   * Applies the transformation matrix and scaling to the region defined by
   * the bitmap. Uses the parameters given in the setter methods and applies
   * to the internal region.
   *
   * Called by the transformAndPaintBitmap methods before painting.
   */
  public void applyTransformation() {

    // Check if this is necessary
    if (transformationApplied) {
      return;
    }

    int bitmapRight = Fixed32.toFP(bitmap.getWidth());
    int bitmapBottom = Fixed32.toFP(bitmap.getHeight());

    // scale the image size as requested
    bitmapRight = Fixed32.mul(bitmapRight, scaleX);
    bitmapBottom = Fixed32.mul(bitmapBottom, scaleY);

    // init matrix
    bitmapXPts[0] = 0;
    bitmapYPts[0] = 0;

    bitmapXPts[1] = 0;
    bitmapYPts[1] = bitmapBottom;

    bitmapXPts[2] = bitmapRight;
    bitmapYPts[2] = bitmapBottom;

    bitmapXPts[3] = bitmapRight;
    bitmapYPts[3] = 0;

    long rotation;
    for (int i = 0; i < 4; i++) {
      rotation = VecMath.multiplyPoint(transformMatrix, 0, bitmapXPts[i], bitmapYPts[i]);
      bitmapXPts[i] = (int) rotation;
      bitmapYPts[i] = (int) (rotation >>> 32);
    }

    // calculate the new boundaries (as rotation can require a
    // larger bitmap to hold the image)
    int minX = 0;
    int minY = 0;
    int maxX = 0;
    int maxY = 0;

    for (int i = 1; i < 4; i++) {
      if (bitmapXPts[i] < minX) {
        minX = bitmapXPts[i];
      } else if (bitmapXPts[i] > maxX) {
        maxX = bitmapXPts[i];
      }

      if (bitmapYPts[i] < minY) {
        minY = bitmapYPts[i];
      } else if (bitmapYPts[i] > maxY) {
        maxY = bitmapYPts[i];
      }
    }

    // shift the new boundaries to be anchored in the top left at
    // 0,0 and restore to int coordinates.
    for (int i = 0; i < 4; i++) {
      bitmapXPts[i] = Fixed32.toInt(bitmapXPts[i] - minX);
      bitmapYPts[i] = Fixed32.toInt(bitmapYPts[i] - minY);
    }

    /**
     * When creating a non-orthogonal rotation, the resultant path may not
     * be properly rectangular after rotation. This avoids misalignment of
     * the texture and path by slightly spreading the texture over the
     * space, making it a bit larger.
     */
    if (!orthogonal) {
      // New texture origin outside the original region
      textureX = Fixed32.toFP(-2);
      textureY = Fixed32.toFP(-2);

      // rotate the texture origin just as the region was rotated
      rotation = VecMath.multiplyPoint(transformMatrix, 0, textureX, textureY);
      textureX = (int) rotation;
      textureY = (int) (rotation >>> 32);

      // adjust the texture origin to the same point space as the region
      textureX = Fixed32.toInt(textureX - minX);
      textureY = Fixed32.toInt(textureY - minY);

      // scale the texture so that it is 2 pixels larger than the region
      // on all sides
      resultantScaleX = Fixed32.div((bitmapRight + Fixed32.toFP(4)), Fixed32.toFP(bitmap.getWidth()));
      resultantScaleY = Fixed32.div((bitmapBottom + Fixed32.toFP(4)), Fixed32.toFP(bitmap.getHeight()));

    } else {
      // orthogonal roations can use exact coordinates since the region is
      // perfectly formed
      textureX = bitmapXPts[0];
      textureY = bitmapYPts[0];
      resultantScaleX = scaleX;
      resultantScaleY = scaleY;
    }
    // set the resulting dimensions of a bitmap containing this rotation
    transformedRegion.set(Fixed32.toRoundedInt(maxX - minX) + 1, Fixed32.toRoundedInt(maxY - minY) + 1);
    transformationApplied = true;
  }

  /**
   * Get the background color.
   *
   * @return integer in the format 0xAARRGGBB.
   * @since 1.5
   */
  public int getBackgroundColor() {
    return backgroundColor;
  }

  /**
   * Set the background color. Takes an integer in the format 0xAARRGGBB,
   * where AA is the alpha value.
   *
   * @param backgroundColor
   * @since 1.5
   */
  public void setBackgroundColor(int backgroundColor) {
    this.backgroundColor = backgroundColor;
  }

  /**
   * Set the background alpha value.
   *
   * @param backgroundAlpha
   *            0-255, 0 is transparent.
   */
  public void setBackgroundAlpha(int backgroundAlpha) {
    this.backgroundAlpha = backgroundAlpha;
  }

  /**
   * Get the background alpha
   *
   * @return alpha setting of the background (0-255).
   */
  public int getBackgroundAlpha() {
    return backgroundAlpha;
  }

  /**
   * Paint the transformed Bitmap using the given Graphics context. Paints the
   * area transparent before painting the bitmap.
   *
   * @param g
   *            the {@link Graphics} context to use - from a screen or bitmap,
   *            etc.
   */
  private void paintTransformedBitmap(Graphics g) {
    paintTransformedBitmap(g, textureX, textureY);
  }

  /**
   * Paint the transformed Bitmap using the given Graphics context. Paints the
   * area transparent before painting the bitmap.
   *
   * @param g
   *            the {@link Graphics} context to use - from a screen or bitmap,
   *            etc.
   * @param textureOriginX
   *            x value in the original bitmap to start drawing from
   * @param textureOriginY
   *            y value in the original bitmap to start drawing from
   * @since 1.1
   */
  private void paintTransformedBitmap(Graphics g, int textureOriginX, int textureOriginY) {
    // Make the drawing space transparent first before painting
    g.setGlobalAlpha(getBackgroundAlpha());
    g.setBackgroundColor(getBackgroundColor());
    g.clear();
    g.setGlobalAlpha(255);
    /**
     * Keep the precision of our transformation and Scale the drawing as
     * well. Scale is applied as though a matrix of the form
     *
     * <pre>
     * | ScaleX  0     0|
     * | 0    ScaleY   0|
     * | 0       0     1|
     * </pre>
     *
     * is multiplied by the Transformation matrix
     **/

    int dux = Fixed32.div(transformMatrix[UX], resultantScaleX);
    int dvx = Fixed32.div(transformMatrix[VX], resultantScaleY);
    int duy = Fixed32.div(transformMatrix[UY], resultantScaleX);
    int dvy = Fixed32.div(transformMatrix[VY], resultantScaleY);

    g.drawTexturedPath(bitmapXPts, bitmapYPts, null, null, textureOriginX, textureOriginY, dux, dvx, duy, dvy, bitmap);
  }

  /**
   * Apply the transformation and paint on the given Graphics context
   *
   * @param g
   *            the {@link Graphics} context to use - from a screen or bitmap,
   *            etc.
   */
  public void transformAndPaintBitmap(Graphics g) {
    applyTransformation();
    paintTransformedBitmap(g);
  }

  /**
   * Apply the transformation, paint to a new {@link Bitmap} and return it
   *
   * @return The transformed {@link Bitmap} of the required size to display
   *         the entire transformed (rotated, scaled) image, with an alpha
   *         channel so that the space around the bitmap is transparent.
   */
  public Bitmap transformAndPaintBitmap() {

    applyTransformation();

    // create the new bitmap
    Bitmap transformedBitmap = new Bitmap(bitmap.getType(), transformedRegion.width, transformedRegion.height);
    transformedBitmap.createAlpha(Bitmap.ALPHA_BITDEPTH_8BPP);
    Graphics graphics = new Graphics(transformedBitmap);
    paintTransformedBitmap(graphics);
    return transformedBitmap;
  }

  /**
   * Scale the internal bitmap into the provided Bitmap. Alternate
   * implementation of method from 5.0 Bitmap API.
   *
   * <pre>
   * {@link http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html}
   * </pre>
   *
   * @param dst
   *            the {@link Bitmap} to paint on, scale comes from this.
   * @param filterType
   *            has no real effect, but must be one of:
   *            <ul>
   *            <li>{@link #FILTER_LANCZOS}</li>
   *            <li>{@link #FILTER_BOX}</li>
   *            <li>{@link #FILTER_BILINEAR}</li>
   *            </ul>
   *
   * @since 1.1
   */
  public void scaleInto(Bitmap dst, int filterType) {
    scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, 0, 0, dst.getWidth(), dst.getHeight(), filterType);
  }

  /**
   * Scale the internal bitmap into the provided Bitmap, using the provided
   * Aspect Ratio rules. Alternate implementation of method from 5.0 Bitmap
   * API.
   *
   * <pre>
   * {@link http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html}
   * </pre>
   *
   * @param dst
   *            the {@link Bitmap} to paint on, scale comes from this.
   * @param filterType
   *            has no real effect, but must be one of:
   *            <ul>
   *            <li>{@link #FILTER_LANCZOS}</li>
   *            <li>{@link #FILTER_BOX}</li>
   *            <li>{@link #FILTER_BILINEAR}</li>
   *            </ul>
   * @param iAspectRatioOption
   *            one of:
   *            <ul>
   *            <li>{@link #SCALE_STRETCH}</li>
   *            <li>{@link #SCALE_TO_FILL}</li>
   *            <li>{@link #SCALE_TO_FIT}</li>
   *            </ul>
   * @since 1.1
   */
  public void scaleInto(Bitmap dst, int filterType, int iAspectRatioOption) {

    // Maintain same interface as 5.0 API
    if (iAspectRatioOption < 0 || iAspectRatioOption > 2) {
      throw new IllegalArgumentException("Invalid aspect ratio parameter");
    }

    int dstWidth = Fixed32.toFP(dst.getWidth());
    int srcWidth = Fixed32.toFP(bitmap.getWidth());
    int dstHeight = Fixed32.toFP(dst.getHeight());
    int srcHeight = Fixed32.toFP(bitmap.getHeight());

    // Find the scale values in each axis to compare
    int scaleHoriz = Fixed32.div(dstWidth, srcWidth);
    int scaleVert = Fixed32.div(dstHeight, srcHeight);

    switch (iAspectRatioOption) {
    case SCALE_STRETCH:
      scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, 0, 0, dst.getWidth(), dst.getHeight(), filterType);
      break;
    case SCALE_TO_FILL:
      /*
       * Destination should be completely filled by source, with some
       * source not being painted if necessary. Also centers the painting.
       */
      int srcShiftX;
      int srcShiftY;

      if (scaleVert > scaleHoriz) {
        // source height fills the destination
        int srcRegionWidth = Fixed32.div(dstWidth, scaleVert);
        srcShiftX = Fixed32.toRoundedInt(Fixed32.mul((srcWidth - srcRegionWidth) >> 1, scaleVert));
        scaleInto(srcShiftX, 0, Fixed32.toRoundedInt(srcRegionWidth), bitmap.getHeight(), dst, 0, 0, dst.getWidth(), dst.getHeight(),
            filterType);
      } else if (scaleHoriz > scaleVert) {
        // source width fills destination
        int srcRegionHeight = Fixed32.div(dstHeight, scaleHoriz);
        srcShiftY = Fixed32.toRoundedInt(Fixed32.mul((srcHeight - srcRegionHeight) >> 1, scaleVert));
        scaleInto(0, srcShiftY, bitmap.getWidth(), Fixed32.toRoundedInt(srcRegionHeight), dst, 0, 0, dst.getWidth(), dst.getHeight(),
            filterType);
      } else {
        // Both bitmaps have the same aspect ratio
        scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, 0, 0, dst.getWidth(), dst.getHeight(), filterType);
      }
      break;
    case SCALE_TO_FIT:
      /*
       * Source should be completely contained by destination, with some
       * destination not being painted if necessary. Also centers the
       * painting.
       */
      int dstShiftX;
      int dstShiftY;

      if (scaleVert < scaleHoriz) {
        // source is fitted vertically with blank sides
        int scaledWidth = Fixed32.mul(scaleVert, srcWidth);
        dstShiftX = Fixed32.toInt((dstWidth - scaledWidth) >> 1);
        scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, dstShiftX, 0, Fixed32.toRoundedInt(scaledWidth), dst.getHeight(),
            filterType);
      } else if (scaleHoriz < scaleVert) {
        // source fits horizontally with blank top and bottom
        int scaledHeight = Fixed32.mul(scaleHoriz, srcHeight);
        dstShiftY = Fixed32.toInt((dstHeight - scaledHeight) >> 1);
        scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, 0, dstShiftY, dst.getWidth(), Fixed32.toRoundedInt(scaledHeight),
            filterType);
      } else {
        // Both bitmaps have the same aspect ratio
        scaleInto(0, 0, bitmap.getWidth(), bitmap.getHeight(), dst, 0, 0, dst.getWidth(), dst.getHeight(), filterType);
      }
      break;

    }

  }

  /**
   * Scale the internal bitmap into the provided Bitmap. Alternate
   * implementation of method from 5.0 Bitmap API.
   *
   * <pre>
   * {@link http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/system/Bitmap.html}
   * </pre>
   *
   *
   * @param srcLeft
   *            X coordinate of the top left corner of the area to be copied
   *            from the source bitmap.
   * @param srcTop
   *            Y coordinate of the top left corner of the area to be copied
   *            from the source bitmap.
   * @param srcWidth
   *            Width of the area to be copied from the source bitmap.
   * @param srcHeight
   *            Height of the area to be copied from the source bitmap.
   * @param dst
   *            the {@link Bitmap} to paint on, scale comes from this.
   * @param dstLeft
   *            X coordinate of the top left corner of the area to be copied
   *            to the destination bitmap.
   * @param dstTop
   *            Y coordinate of the top left corner of the area to be copied
   *            to the destination bitmap.
   * @param dstWidth
   *            Width of the area to be copied from the source bitmap.
   * @param dstHeight
   *            Height of the area to be copied to the destination bitmap.
   * @param filterType
   *            has no real effect, but must be one of:
   *            <ul>
   *            <li>{@link #FILTER_LANCZOS}</li>
   *            <li>{@link #FILTER_BOX}</li>
   *            <li>{@link #FILTER_BILINEAR}</li>
   *            </ul>
   * @throws NullPointerException
   *             Thrown if 'dst' is null.
   * @throws IllegalArgumentException
   *             Thrown if the destination bitmap is read-only.
   * @throws IllegalArgumentException
   *             Thrown if illegal filter type is specified.
   * @since 1.1
   */
  public void scaleInto(int srcLeft, int srcTop, int srcWidth, int srcHeight, Bitmap dst, int dstLeft, int dstTop, int dstWidth, int dstHeight,
      int filterType) {

    // Maintain same interface as 5.0 API
    if (dst == null) {
      throw new NullPointerException("Destination bitmap can not be set to NULL");
    }
    if (!dst.isWritable()) {
      throw new IllegalArgumentException("Destination bitmap should not be read-only");
    }
    if (filterType < 0 || filterType > 2) {
      throw new IllegalArgumentException("Invalid filter type");
    }

    // make sure we're starting with no rotation.
    resetTransform();

    // set the region to be drawn on
    bitmapXPts = new int[] { dstLeft, dstLeft, dstLeft + dstWidth, dstLeft + dstWidth };
    bitmapYPts = new int[] { dstTop, dstTop + dstHeight, dstTop + dstHeight, dstTop };

    // Calculate the new scale based on the region sizes
    resultantScaleX = Fixed32.div(Fixed32.toFP(dstWidth), Fixed32.toFP(srcWidth));
    resultantScaleY = Fixed32.div(Fixed32.toFP(dstHeight), Fixed32.toFP(srcHeight));

    Graphics graphics = new Graphics(dst);
    paintTransformedBitmap(graphics, dstLeft - srcLeft, dstTop - srcTop);
  }

  /**
   * Round the value to the nearest int
   *
   * @param value
   * @return rounded value
   * @since 1.1
   */
  public static int round(double value) {
    if (value < 0) {
      int roundedDown = (int) value;
      if (value != (roundedDown - 0.5f)) {
        return (int) (value - 0.5f);
      } // Special case to match java.lang.Math round functionality. Same
      // rounding as positive float when value is negative exact half
      // (eg. -x.5)

    }
    return (int) (value + 0.5f);
  }

  /**
   * Convert a double value to a Fixed32 integer
   *
   * @param value
   * @return the value as an integer in Fixed32 format (16.16);
   * @since 1.3
   */
  public static int toFP(double value) {
    return round(value * SHIFT16);
  }

  /**
   * Utility to convert a RIM API Bitmap into a J2ME LCDUI Image. This assists
   * in using this library within a MIDP application, where the
   * drawTexturedPath method is not available.
   *
   * @param bitmap
   *            the source Bitmap
   * @return the Image object based on the Bitmap data
   * @since 1.2
   */
  public static Image convert(Bitmap bitmap) {
    int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
    bitmap.getARGB(data, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
    return Image.createRGBImage(data, bitmap.getWidth(), bitmap.getHeight(), true);
  }

  /**
   * Return the unmodified bitmap
   *
   * @return the original, unmodified bitmap provided in the constructor or
   *         through setBitmap(Bitmap)
   */
  public Bitmap getOriginalBitmap() {
    return bitmap;
  }

  /**
   * Set a new bitmap to be transformed
   *
   * @param bitmap
   */
  public void setBitmap(Bitmap bitmap) {
    this.bitmap = bitmap;
    transformationApplied = false;
  }

  /**
   * The Scale factor in the X dimension
   *
   * @return the scale as a Fixed32 integer (16.16)
   */
  public double getScaleX() {
    return scaleX;
  }

  /**
   * Set the X dimension scale independently
   *
   * @param scaleX
   *            as a Fixed32 integer (16.16)
   */
  public void setScaleX(int scaleX) {
    this.scaleX = scaleX;
    transformationApplied = false;
  }

  /**
   * The Scale factor in the Y dimension
   *
   * @return the scale as a Fixed32 integer (16.16)
   */
  public int getScaleY() {
    return scaleY;
  }

  /**
   * Set the Y dimension scale independently
   *
   * @param scaleY
   *            as a Fixed32 integer (16.16)
   */
  public void setScaleY(int scaleY) {
    this.scaleY = scaleY;
    transformationApplied = false;
  }

  /**
   * Set the X and Y scales together
   *
   * @param scale
   *            as a Fixed32 integer (16.16)
   */
  public void setScale(int scale) {
    this.setScaleX(scale);
    this.setScaleY(scale);
  }
}
TOP

Related Classes of com.blackberry.toolkit.ui.images.ImageManipulator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.