package android.graphics; import java.io.PrintWriter; import java.util.Arrays; import android.util.MathUtils; /** * The Matrix class holds a 3x3 matrix for transforming coordinates. * Matrix does not have a constructor, so it must be explicitly initialized * using either reset() - to construct an identity matrix, or one of the set..() * functions (e.g. setTranslate, setRotate, etc.). */ public class Matrix { public static final int MSCALE_X = 0; //!< use with getValues/setValues public static final int MSKEW_X = 1; //!< use with getValues/setValues public static final int MTRANS_X = 2; //!< use with getValues/setValues public static final int MSKEW_Y = 3; //!< use with getValues/setValues public static final int MSCALE_Y = 4; //!< use with getValues/setValues public static final int MTRANS_Y = 5; //!< use with getValues/setValues public static final int MPERSP_0 = 6; //!< use with getValues/setValues public static final int MPERSP_1 = 7; //!< use with getValues/setValues public static final int MPERSP_2 = 8; //!< use with getValues/setValues private SkMatrix mSkMatrix; /** * Create an identity matrix */ public Matrix() { mSkMatrix = native_create(null); } /** * Create a matrix that is a (deep) copy of src * @param src The matrix to copy into this matrix */ public Matrix(Matrix src) { mSkMatrix = native_create(src.mSkMatrix); } /** * Returns true if the matrix is identity. * This maybe faster than testing if (getType() == 0) */ public boolean isIdentity() { return mSkMatrix.isIdentity(); } /** * Returns true if will map a rectangle to another rectangle. This can be * true if the matrix is identity, scale-only, or rotates a multiple of 90 * degrees. */ public boolean rectStaysRect() { return mSkMatrix.rectStaysRect(); } /** * (deep) copy the src matrix into this matrix. If src is null, reset this * matrix to the identity matrix. */ public void set(Matrix src) { if (src == null) { reset(); } else { mSkMatrix.fTypeMask = src.mSkMatrix.fTypeMask; System.arraycopy(src.mSkMatrix.fMat, 0, mSkMatrix.fMat, 0, src.mSkMatrix.fMat.length); } } /** Returns true iff obj is a Matrix and its values equal our values. */ public boolean equals(Object obj) { return obj != null && obj instanceof Matrix && Arrays.equals(mSkMatrix.fMat, ((Matrix)obj).mSkMatrix.fMat); } /** Set the matrix to identity */ public void reset() { mSkMatrix.reset(); } /** Set the matrix to translate by (dx, dy). */ public void setTranslate(float dx, float dy) { mSkMatrix.setTranslate(dx, dy); } /** * Set the matrix to scale by sx and sy, with a pivot point at (px, py). * The pivot point is the coordinate that should remain unchanged by the * specified transformation. */ public void setScale(float sx, float sy, float px, float py) { mSkMatrix.setScale(sx, sy, px, py); } /** Set the matrix to scale by sx and sy. */ public void setScale(float sx, float sy) { mSkMatrix.setScale(sx, sy); } /** * Set the matrix to rotate by the specified number of degrees, with a pivot * point at (px, py). The pivot point is the coordinate that should remain * unchanged by the specified transformation. */ public void setRotate(float degrees, float px, float py) { mSkMatrix.setRotate(degrees, px, py); } /** * Set the matrix to rotate about (0,0) by the specified number of degrees. */ public void setRotate(float degrees) { mSkMatrix.setRotate(degrees); } /** * Set the matrix to rotate by the specified sine and cosine values, with a * pivot point at (px, py). The pivot point is the coordinate that should * remain unchanged by the specified transformation. */ public void setSinCos(float sinValue, float cosValue, float px, float py) { mSkMatrix.setSinCos(sinValue, cosValue, px, py); } /** Set the matrix to rotate by the specified sine and cosine values. */ public void setSinCos(float sinValue, float cosValue) { mSkMatrix.setSinCos(sinValue, cosValue); } /** * Set the matrix to skew by sx and sy, with a pivot point at (px, py). * The pivot point is the coordinate that should remain unchanged by the * specified transformation. */ public void setSkew(float kx, float ky, float px, float py) { mSkMatrix.setSkew(kx, ky, px, py); } /** Set the matrix to skew by sx and sy. */ public void setSkew(float kx, float ky) { mSkMatrix.setSkew(kx, ky); } /** * Set the matrix to the concatenation of the two specified matrices, * returning true if the the result can be represented. Either of the two * matrices may also be the target matrix. this = a * b */ public boolean setConcat(Matrix a, Matrix b) { return mSkMatrix.setConcat(a.mSkMatrix, b.mSkMatrix); } /** * Preconcats the matrix with the specified translation. * M' = M * T(dx, dy) */ public boolean preTranslate(float dx, float dy) { return mSkMatrix.preTranslate(dx, dy); } /** * Preconcats the matrix with the specified scale. * M' = M * S(sx, sy, px, py) */ public boolean preScale(float sx, float sy, float px, float py) { return mSkMatrix.preScale(sx, sy, px, py); } /** * Preconcats the matrix with the specified scale. * M' = M * S(sx, sy) */ public boolean preScale(float sx, float sy) { return mSkMatrix.preScale(sx, sy); } /** * Preconcats the matrix with the specified rotation. * M' = M * R(degrees, px, py) */ public boolean preRotate(float degrees, float px, float py) { return mSkMatrix.preRotate(degrees, px, py); } /** * Preconcats the matrix with the specified rotation. * M' = M * R(degrees) */ public boolean preRotate(float degrees) { return mSkMatrix.preRotate(degrees); } /** * Preconcats the matrix with the specified skew. * M' = M * K(kx, ky, px, py) */ public boolean preSkew(float kx, float ky, float px, float py) { return mSkMatrix.preSkew(kx, ky, px, py); } /** * Preconcats the matrix with the specified skew. * M' = M * K(kx, ky) */ public boolean preSkew(float kx, float ky) { return mSkMatrix.preSkew(kx, ky); } /** * Preconcats the matrix with the specified matrix. * M' = M * other */ public boolean preConcat(Matrix other) { return mSkMatrix.preConcat(other.mSkMatrix); } /** * Postconcats the matrix with the specified translation. * M' = T(dx, dy) * M */ public boolean postTranslate(float dx, float dy) { return mSkMatrix.postTranslate(dx, dy); } /** * Postconcats the matrix with the specified scale. * M' = S(sx, sy, px, py) * M */ public boolean postScale(float sx, float sy, float px, float py) { return mSkMatrix.postScale(sx, sy, px, py); } /** * Postconcats the matrix with the specified scale. * M' = S(sx, sy) * M */ public boolean postScale(float sx, float sy) { return mSkMatrix.postScale(sx, sy); } /** * Postconcats the matrix with the specified rotation. * M' = R(degrees, px, py) * M */ public boolean postRotate(float degrees, float px, float py) { return mSkMatrix.postRotate(degrees, px, py); } /** * Postconcats the matrix with the specified rotation. * M' = R(degrees) * M */ public boolean postRotate(float degrees) { return mSkMatrix.postRotate(degrees); } /** * Postconcats the matrix with the specified skew. * M' = K(kx, ky, px, py) * M */ public boolean postSkew(float kx, float ky, float px, float py) { return mSkMatrix.postSkew(kx, ky, px, py); } /** * Postconcats the matrix with the specified skew. * M' = K(kx, ky) * M */ public boolean postSkew(float kx, float ky) { return mSkMatrix.postSkew(kx, ky); } /** * Postconcats the matrix with the specified matrix. * M' = other * M */ public boolean postConcat(Matrix other) { return mSkMatrix.postConcat(other.mSkMatrix); } /** * Controlls how the src rect should align into the dst rect for * setRectToRect(). */ public enum ScaleToFit { /** * Scale in X and Y independently, so that src matches dst exactly. This * may change the aspect ratio of the src. */ FILL(0), /** * Compute a scale that will maintain the original src aspect ratio, but * will also ensure that src fits entirely inside dst. At least one axis * (X or Y) will fit exactly. START aligns the result to the left and * top edges of dst. */ START(1), /** * Compute a scale that will maintain the original src aspect ratio, but * will also ensure that src fits entirely inside dst. At least one axis * (X or Y) will fit exactly. The result is centered inside dst. */ CENTER(2), /** * Compute a scale that will maintain the original src aspect ratio, but * will also ensure that src fits entirely inside dst. At least one axis * (X or Y) will fit exactly. END aligns the result to the right and * bottom edges of dst. */ END(3); // the native values must match those in SkMatrix.h ScaleToFit(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; } /** * Set the matrix to the scale and translate values that map the source * rectangle to the destination rectangle, returning true if the the result * can be represented. * * @param src the source rectangle to map from. * @param dst the destination rectangle to map to. * @param stf the ScaleToFit option * @return true if the matrix can be represented by the rectangle mapping. */ public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { if (dst == null || src == null) { throw new NullPointerException(); } return mSkMatrix.setRectToRect(src, dst, stf.nativeInt); } // private helper to perform range checks on arrays of "points" private static void checkPointArrays(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) { // check for too-small and too-big indices int srcStop = srcIndex + (pointCount << 1); int dstStop = dstIndex + (pointCount << 1); if ((pointCount | srcIndex | dstIndex | srcStop | dstStop) < 0 || srcStop > src.length || dstStop > dst.length) { throw new ArrayIndexOutOfBoundsException(); } } /** * Set the matrix such that the specified src points would map to the * specified dst points. The "points" are represented as an array of floats, * order [x0, y0, x1, y1, ...], where each "point" is 2 float values. * * @param src The array of src [x,y] pairs (points) * @param srcIndex Index of the first pair of src values * @param dst The array of dst [x,y] pairs (points) * @param dstIndex Index of the first pair of dst values * @param pointCount The number of pairs/points to be used. Must be [0..4] * @return true if the matrix was set to the specified transformation */ public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) { if (pointCount > 4) { throw new IllegalArgumentException(); } checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); float[] tmpSrc = Arrays.copyOfRange(src, srcIndex, src.length); float[] tmpDst = Arrays.copyOfRange(dst, dstIndex, dst.length); boolean result = mSkMatrix.setPolyToPoly(tmpSrc, tmpDst, pointCount); System.arraycopy(tmpSrc, 0, src, srcIndex, tmpSrc.length); System.arraycopy(tmpDst, 0, dst, dstIndex, tmpDst.length); return result; } /** * If this matrix can be inverted, return true and if inverse is not null, * set inverse to be the inverse of this matrix. If this matrix cannot be * inverted, ignore inverse and return false. */ public boolean invert(Matrix inverse) { return mSkMatrix.invert(inverse.mSkMatrix); } /** * Apply this matrix to the array of 2D points specified by src, and write * the transformed points into the array of points specified by dst. The * two arrays represent their "points" as pairs of floats [x, y]. * * @param dst The array of dst points (x,y pairs) * @param dstIndex The index of the first [x,y] pair of dst floats * @param src The array of src points (x,y pairs) * @param srcIndex The index of the first [x,y] pair of src floats * @param pointCount The number of points (x,y pairs) to transform */ public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount) { checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); float[] tmpSrc = Arrays.copyOfRange(src, srcIndex, src.length - 1); float[] tmpDst = Arrays.copyOfRange(dst, dstIndex, dst.length - 1); mSkMatrix.mapPoints(tmpDst, tmpSrc, pointCount); System.arraycopy(tmpSrc, 0, src, srcIndex, tmpSrc.length); System.arraycopy(tmpDst, 0, dst, dstIndex, tmpDst.length); } /** * Apply this matrix to the array of 2D vectors specified by src, and write * the transformed vectors into the array of vectors specified by dst. The * two arrays represent their "vectors" as pairs of floats [x, y]. * * @param dst The array of dst vectors (x,y pairs) * @param dstIndex The index of the first [x,y] pair of dst floats * @param src The array of src vectors (x,y pairs) * @param srcIndex The index of the first [x,y] pair of src floats * @param vectorCount The number of vectors (x,y pairs) to transform */ public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount) { checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount); float[] tmpSrc = Arrays.copyOfRange(src, srcIndex, src.length - 1); float[] tmpDst = Arrays.copyOfRange(dst, dstIndex, dst.length - 1); mSkMatrix.mapVectors(dst, src, vectorCount); System.arraycopy(tmpSrc, 0, src, srcIndex, tmpSrc.length); System.arraycopy(tmpDst, 0, dst, dstIndex, tmpDst.length); } /** * Apply this matrix to the array of 2D points specified by src, and write * the transformed points into the array of points specified by dst. The * two arrays represent their "points" as pairs of floats [x, y]. * * @param dst The array of dst points (x,y pairs) * @param src The array of src points (x,y pairs) */ public void mapPoints(float[] dst, float[] src) { if (dst.length != src.length) { throw new ArrayIndexOutOfBoundsException(); } mapPoints(dst, 0, src, 0, dst.length >> 1); } /** * Apply this matrix to the array of 2D vectors specified by src, and write * the transformed vectors into the array of vectors specified by dst. The * two arrays represent their "vectors" as pairs of floats [x, y]. * * @param dst The array of dst vectors (x,y pairs) * @param src The array of src vectors (x,y pairs) */ public void mapVectors(float[] dst, float[] src) { if (dst.length != src.length) { throw new ArrayIndexOutOfBoundsException(); } mapVectors(dst, 0, src, 0, dst.length >> 1); } /** * Apply this matrix to the array of 2D points, and write the transformed * points back into the array * * @param pts The array [x0, y0, x1, y1, ...] of points to transform. */ public void mapPoints(float[] pts) { mapPoints(pts, 0, pts, 0, pts.length >> 1); } /** * Apply this matrix to the array of 2D vectors, and write the transformed * vectors back into the array. * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform. */ public void mapVectors(float[] vecs) { mapVectors(vecs, 0, vecs, 0, vecs.length >> 1); } /** * Apply this matrix to the src rectangle, and write the transformed * rectangle into dst. This is accomplished by transforming the 4 corners of * src, and then setting dst to the bounds of those points. * * @param dst Where the transformed rectangle is written. * @param src The original rectangle to be transformed. * @return the result of calling rectStaysRect() */ public boolean mapRect(RectF dst, RectF src) { if (dst == null || src == null) { throw new NullPointerException(); } return mSkMatrix.mapRect(dst, src); } /** * Apply this matrix to the rectangle, and write the transformed rectangle * back into it. This is accomplished by transforming the 4 corners of rect, * and then setting it to the bounds of those points * * @param rect The rectangle to transform. * @return the result of calling rectStaysRect() */ public boolean mapRect(RectF rect) { return mapRect(rect, rect); } /** * Return the mean radius of a circle after it has been mapped by * this matrix. NOTE: in perspective this value assumes the circle * has its center at the origin. */ public float mapRadius(float radius) { return mSkMatrix.mapRadius(radius); } /** Copy 9 values from the matrix into the array. */ public void getValues(float[] values) { if (values.length < 9) { throw new ArrayIndexOutOfBoundsException(); } mSkMatrix.getValues(values); } /** Copy 9 values from the array into the matrix. Depending on the implementation of Matrix, these may be transformed into 16.16 integers in the Matrix, such that a subsequent call to getValues() will not yield exactly the same values. */ public void setValues(float[] values) { if (values.length < 9) { throw new ArrayIndexOutOfBoundsException(); } mSkMatrix.setValues(values); } public int getType() { return mSkMatrix.getType(); } public String toString() { StringBuilder sb = new StringBuilder(64); sb.append("Matrix{"); toShortString(sb); sb.append('}'); return sb.toString(); } public String toShortString() { StringBuilder sb = new StringBuilder(64); toShortString(sb); return sb.toString(); } /** * @hide */ public void toShortString(StringBuilder sb) { float[] values = new float[9]; getValues(values); sb.append('['); sb.append(values[0]); sb.append(", "); sb.append(values[1]); sb.append(", "); sb.append(values[2]); sb.append("]["); sb.append(values[3]); sb.append(", "); sb.append(values[4]); sb.append(", "); sb.append(values[5]); sb.append("]["); sb.append(values[6]); sb.append(", "); sb.append(values[7]); sb.append(", "); sb.append(values[8]); sb.append(']'); } /** * Print short string, to optimize dumping. * @hide */ public void printShortString(PrintWriter pw) { float[] values = new float[9]; getValues(values); pw.print('['); pw.print(values[0]); pw.print(", "); pw.print(values[1]); pw.print(", "); pw.print(values[2]); pw.print("]["); pw.print(values[3]); pw.print(", "); pw.print(values[4]); pw.print(", "); pw.print(values[5]); pw.print("]["); pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", "); pw.print(values[8]); pw.print(']'); } protected void finalize() throws Throwable { finalizer(mSkMatrix); } /*package*/ final SkMatrix ni() { return mSkMatrix; } private static SkMatrix native_create(final SkMatrix matrix) { return new SkMatrix(matrix); } private static void finalizer(final SkMatrix matrix) {} private static class SkMatrix { static final int kIdentity_Mask = 0; static final int kTranslate_Mask = 0x01; //!< set if the matrix has translation static final int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale static final int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates static final int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective static final int kRectStaysRect_Mask = 0x10; static final int kUnknown_Mask = 0x80; static final int kAllMasks = kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask | kRectStaysRect_Mask; static final int kMScaleX = 0; static final int kMSkewX = 1; static final int kMTransX = 2; static final int kMSkewY = 3; static final int kMScaleY = 4; static final int kMTransY = 5; static final int kMPersp0 = 6; static final int kMPersp1 = 7; static final int kMPersp2 = 8; float[] fMat = new float[9]; static final float SK_Scalar1 = 1.0f; static final float kMatrix22Elem = SK_Scalar1; static final float tolerance = SK_Scalar1 / (1<<12); static final float kPersp1Int = 1.0f; static final float kScalar1Int = 1.0f; // this guy aligns with the masks, so we can compute a mask from a varaible 0/1 static final int kTranslate_Shift = 0; static final int kScale_Shift = 1; static final int kAffine_Shift = 2; static final int kPerspective_Shift = 3; static final int kRectStaysRect_Shift = 4; /** * Scale in X and Y independently, so that src matches dst exactly. * This may change the aspect ratio of the src. */ static final int kFill_ScaleToFit = 0; /** * Compute a scale that will maintain the original src aspect ratio, * but will also ensure that src fits entirely inside dst. At least one * axis (X or Y) will fit exactly. kStart aligns the result to the * left and top edges of dst. */ static final int kStart_ScaleToFit = 1; /** * Compute a scale that will maintain the original src aspect ratio, * but will also ensure that src fits entirely inside dst. At least one * axis (X or Y) will fit exactly. The result is centered inside dst. */ static final int kCenter_ScaleToFit = 2; /** * Compute a scale that will maintain the original src aspect ratio, * but will also ensure that src fits entirely inside dst. At least one * axis (X or Y) will fit exactly. kEnd aligns the result to the * right and bottom edges of dst. */ static final int kEnd_ScaleToFit = 3; int fTypeMask; public int getType() { if ((fTypeMask & kUnknown_Mask) != 0) { fTypeMask = computeTypeMask(); } return (fTypeMask & 0xF); } public int computeTypeMask() { int mask = 0; if (fMat[kMPersp0] != 0 | fMat[kMPersp1] != 0 | (fMat[kMPersp2] - kPersp1Int) != 0) { mask |= kPerspective_Mask; } if (fMat[kMTransX] != 0 | fMat[kMTransY] != 0) { mask |= kTranslate_Mask; } if (fMat[kMSkewX] != 0 | fMat[kMSkewY] != 0) { mask |= kAffine_Mask; } if ((fMat[kMScaleX] - kScalar1Int) != 0 | (fMat[kMScaleY] - kScalar1Int) != 0) { mask |= kScale_Mask; } if ((mask & kPerspective_Mask) == 0) { // map non-zero to 1 int m00 = fMat[kMScaleX] != 0 ? 1 : 0; int m01 = fMat[kMSkewX] != 0 ? 1 : 0; int m10 = fMat[kMSkewY] != 0 ? 1 : 0; int m11 = fMat[kMScaleY] != 0 ? 1 : 0; // record if the (p)rimary and (s)econdary diagonals are all 0 or // all non-zero (answer is 0 or 1) int dp0 = (m00 | m11) ^ 1; // true if both are 0 int dp1 = m00 & m11; // true if both are 1 int ds0 = (m01 | m10) ^ 1; // true if both are 0 int ds1 = m01 & m10; // true if both are 1 // return 1 if primary is 1 and secondary is 0 or // primary is 0 and secondary is 1 mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; } return mask; } public SkMatrix(final SkMatrix src) { if (src != null) { this.fTypeMask = src.fTypeMask; System.arraycopy(src.fMat, 0, this.fMat, 0, this.fMat.length); } else { this.reset(); } } public boolean isIdentity() { return this.getType() == 0; } public boolean rectStaysRect() { if ((fTypeMask & kUnknown_Mask) != 0) { fTypeMask = computeTypeMask(); } return (fTypeMask & kRectStaysRect_Mask) != 0; } public void reset() { fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1; fMat[kMSkewX] = fMat[kMSkewY] = fMat[kMTransX] = fMat[kMTransY] = fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kIdentity_Mask | kRectStaysRect_Mask); } public boolean hasPerspective() { return (getType() & kPerspective_Mask) != 0; } public float getScaleX() { return fMat[kMScaleX]; } public float getScaleY() { return fMat[kMScaleY]; } public float getSkewY() { return fMat[kMSkewY]; } public float getSkewX() { return fMat[kMSkewX]; } public float getTranslateX() { return fMat[kMTransX]; } public float getTranslateY() { return fMat[kMTransY]; } public float getPerspX() { return fMat[kMPersp0]; } public float getPerspY() { return fMat[kMPersp1]; } public void set(int index, float value) { fMat[index] = value; setTypeMask(kUnknown_Mask); } public float get(int index) { return fMat[index]; } public void setScaleX(float v) { set(kMScaleX, v); } public void setScaleY(float v) { set(kMScaleY, v); } public void setSkewY(float v) { set(kMSkewY, v); } public void setSkewX(float v) { set(kMSkewX, v); } public void setTranslateX(float v) { set(kMTransX, v); } public void setTranslateY(float v) { set(kMTransY, v); } public void setPerspX(float v) { set(kMPersp0, v); } public void setPerspY(float v) { set(kMPersp1, v); } public void setTranslate(float dx, float dy) { if (dx != 0 || dy != 0) { fMat[kMTransX] = dx; fMat[kMTransY] = dy; fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1; fMat[kMSkewX] = fMat[kMSkewY] = fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kTranslate_Mask | kRectStaysRect_Mask); } else { reset(); } } public void setScale(float sx, float sy, float px, float py) { fMat[kMScaleX] = sx; fMat[kMScaleY] = sy; fMat[kMTransX] = px - sx * px; fMat[kMTransY] = py - sy * py; fMat[kMPersp2] = kMatrix22Elem; fMat[kMSkewX] = fMat[kMSkewY] = fMat[kMPersp0] = fMat[kMPersp1] = 0; setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask); } public void setScale(float sx, float sy) { fMat[kMScaleX] = sx; fMat[kMScaleY] = sy; fMat[kMPersp2] = kMatrix22Elem; fMat[kMTransX] = fMat[kMTransY] = fMat[kMSkewX] = fMat[kMSkewY] = fMat[kMPersp0] = fMat[kMPersp1] = 0; setTypeMask(kScale_Mask | kRectStaysRect_Mask); } public void setRotate(float degrees, float px, float py) { float sinV, cosV; float radians = MathUtils.radians(degrees); sinV = (float) Math.sin(radians); cosV = (float) Math.cos(radians); setSinCos(sinV, cosV, px, py); } public void setRotate(float degrees) { float sinV, cosV; float radians = MathUtils.radians(degrees); sinV = (float) Math.sin(radians); cosV = (float) Math.cos(radians); setSinCos(sinV, cosV); } private static float rowcol3(float row[], int r, float col[], int c) { return row[r + 0] * col[c + 0] + row[r + 1] * col[c + 3] + row[r + 2] * col[c + 6]; } private static void normalize_perspective(float mat[]) { if (Math.abs(mat[kMPersp2]) > kMatrix22Elem) { for (int i = 0; i < 9; i++) { mat[i] = 0.5f * mat[i]; } } } public boolean setConcat(SkMatrix a, SkMatrix b) { int aType = a.getType(); int bType = b.getType(); if (0 == aType) { this.fTypeMask = b.fTypeMask; System.arraycopy(b.fMat, 0, this.fMat, 0, this.fMat.length); } else if (0 == bType) { this.fTypeMask = a.fTypeMask; System.arraycopy(a.fMat, 0, this.fMat, 0, this.fMat.length); } else { SkMatrix tmp = new SkMatrix(null); if (((aType | bType) & kPerspective_Mask) != 0) { tmp.fMat[kMScaleX] = rowcol3(a.fMat, 0, b.fMat, 0); tmp.fMat[kMSkewX] = rowcol3(a.fMat, 0, b.fMat, 1); tmp.fMat[kMTransX] = rowcol3(a.fMat, 0, b.fMat, 2); tmp.fMat[kMSkewY] = rowcol3(a.fMat, 3, b.fMat, 0); tmp.fMat[kMScaleY] = rowcol3(a.fMat, 3, b.fMat, 1); tmp.fMat[kMTransY] = rowcol3(a.fMat, 3, b.fMat, 2); tmp.fMat[kMPersp0] = rowcol3(a.fMat, 6, b.fMat, 0); tmp.fMat[kMPersp1] = rowcol3(a.fMat, 6, b.fMat, 1); tmp.fMat[kMPersp2] = rowcol3(a.fMat, 6, b.fMat, 2); normalize_perspective(tmp.fMat); } else { // not perspective tmp.fMat[kMScaleX] = a.fMat[kMScaleX] * b.fMat[kMScaleX] + a.fMat[kMSkewX] * b.fMat[kMSkewY]; tmp.fMat[kMSkewX] = a.fMat[kMScaleX] * b.fMat[kMSkewX] + a.fMat[kMSkewX] * b.fMat[kMScaleY]; tmp.fMat[kMTransX] = a.fMat[kMScaleX] * b.fMat[kMTransX] + a.fMat[kMSkewX] * b.fMat[kMTransY]; tmp.fMat[kMTransX] = tmp.fMat[kMTransX] + a.fMat[kMTransX]; tmp.fMat[kMSkewY] = a.fMat[kMSkewY] * b.fMat[kMScaleX] + a.fMat[kMScaleY] * b.fMat[kMSkewY]; tmp.fMat[kMScaleY] = a.fMat[kMSkewY] * b.fMat[kMSkewX] + a.fMat[kMScaleY] * b.fMat[kMScaleY]; tmp.fMat[kMTransY] = a.fMat[kMSkewY] * b.fMat[kMTransX] + a.fMat[kMScaleY] * b.fMat[kMTransY]; tmp.fMat[kMTransY] = tmp.fMat[kMTransY] + a.fMat[kMTransY]; tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0; tmp.fMat[kMPersp2] = kMatrix22Elem; } this.fTypeMask = tmp.fTypeMask; System.arraycopy(tmp.fMat, 0, this.fMat, 0, this.fMat.length); } this.setTypeMask(kUnknown_Mask); return true; } void setTypeMask(int mask) { fTypeMask = mask; } void clearTypeMask(int mask) { fTypeMask &= ~mask; } void setSinCos(float sinV, float cosV, float px, float py) { // Workaround: We can't get accurate float in JavaScript, for // example, cos(90), we will get 6.123031769111886e-17, very close to 0, but accutually // the result is 0. Here we will call toFixed(15) to get the near value so we can get 0. /** * @j2sNative * sinV = sinV.toFixed(15); * cosV = cosV.toFixed(15); */{} float oneMinusCosV = SK_Scalar1 - cosV; fMat[kMScaleX] = cosV; fMat[kMSkewX] = -sinV; fMat[kMTransX] = sinV * py + oneMinusCosV * px; fMat[kMSkewY] = sinV; fMat[kMScaleY] = cosV; fMat[kMTransY] = -sinV * px + oneMinusCosV * py; fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kUnknown_Mask); } void setSinCos(float sinV, float cosV) { // Workaround: We can't get accurate float in JavaScript, for // example, cos(90), we will get 6.123031769111886e-17, very close to 0, but accutually // the result is 0. Here we will call toFixed(15) to get the near value so we can get 0. /** * @j2sNative * sinV = sinV.toFixed(15); * cosV = cosV.toFixed(15); */{} fMat[kMScaleX] = cosV; fMat[kMSkewX] = -sinV; fMat[kMTransX] = 0; fMat[kMSkewY] = sinV; fMat[kMScaleY] = cosV; fMat[kMTransY] = 0; fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kUnknown_Mask); } boolean invert(SkMatrix inv) { boolean isPersp = this.hasPerspective(); double scale = sk_inv_determinant(fMat, isPersp); if (scale == 0) { // underflow return false; } if (inv != null) { SkMatrix tmp = new SkMatrix(null); if (inv == this) { inv.fTypeMask = this.fTypeMask; System.arraycopy(this.fMat, 0, inv.fMat, 0, this.fMat.length); } if (isPersp) { tmp.fMat[kMScaleX] = (float) ((fMat[kMScaleY] * fMat[kMPersp2] - fMat[kMTransY] * fMat[kMPersp1]) * scale); tmp.fMat[kMSkewX] = (float) ((fMat[kMTransX] * fMat[kMPersp1] - fMat[kMSkewX] * fMat[kMPersp2]) * scale); tmp.fMat[kMTransX] = (float) ((fMat[kMSkewX] * fMat[kMTransY] - fMat[kMTransX] * fMat[kMScaleY]) * scale); tmp.fMat[kMSkewY] = (float) ((fMat[kMTransY] * fMat[kMPersp0] - fMat[kMSkewY] * fMat[kMPersp2]) * scale); tmp.fMat[kMScaleY] = (float) ((fMat[kMScaleX] * fMat[kMPersp2] - fMat[kMTransX] * fMat[kMPersp0]) * scale); tmp.fMat[kMTransY] = (float) ((fMat[kMTransX] * fMat[kMSkewY] - fMat[kMScaleX] * fMat[kMTransY]) * scale); tmp.fMat[kMPersp0] = (float) ((fMat[kMSkewY] * fMat[kMPersp1] - fMat[kMScaleY] * fMat[kMPersp0]) * scale); tmp.fMat[kMPersp1] = (float) ((fMat[kMSkewX] * fMat[kMPersp0] - fMat[kMScaleX] * fMat[kMPersp1]) * scale); tmp.fMat[kMPersp2] = (float) ((fMat[kMScaleX] * fMat[kMScaleY] - fMat[kMSkewX] * fMat[kMSkewY]) * scale); } else { tmp.fMat[kMScaleX] = (float)(fMat[kMScaleY] * scale); tmp.fMat[kMSkewX] = (float)(-fMat[kMSkewX] * scale); tmp.fMat[kMTransX] = mul_diff_scale(fMat[kMSkewX], fMat[kMTransY], fMat[kMScaleY], fMat[kMTransX], scale); tmp.fMat[kMSkewY] = (float)(-fMat[kMSkewY] * scale); tmp.fMat[kMScaleY] = (float)(fMat[kMScaleX] * scale); tmp.fMat[kMTransY] = mul_diff_scale(fMat[kMSkewY], fMat[kMTransX], fMat[kMScaleX], fMat[kMTransY], scale); tmp.fMat[kMPersp0] = 0; tmp.fMat[kMPersp1] = 0; tmp.fMat[kMPersp2] = kMatrix22Elem; } System.arraycopy(tmp.fMat, 0, inv.fMat, 0, tmp.fMat.length); inv.setTypeMask(kUnknown_Mask); } return true; } private float mul_diff_scale(double a, double b, double c, double d, double scale) { return (float)((a * b - c * d) * scale); } boolean setRectToRect(RectF src, RectF dst, int stf) { if (src.isEmpty()) { reset(); return false; } if (dst.isEmpty()) { Arrays.fill(fMat, 0); setTypeMask(kScale_Mask | kRectStaysRect_Mask); } else { float tx, sx = dst.width() / src.width(); float ty, sy = dst.height() / src.height(); boolean xLarger = false; if (stf != kFill_ScaleToFit) { if (sx > sy) { xLarger = true; sx = sy; } else { sy = sx; } } tx = dst.left - src.left * sx; ty = dst.top - src.top * sy; if (stf == kCenter_ScaleToFit || stf == kEnd_ScaleToFit) { float diff; if (xLarger) { diff = dst.width() - src.width() * sy; } else { diff = dst.height() - src.height() * sy; } if (stf == kCenter_ScaleToFit) { diff = diff * 0.5f; } if (xLarger) { tx += diff; } else { ty += diff; } } fMat[kMScaleX] = sx; fMat[kMScaleY] = sy; fMat[kMTransX] = tx; fMat[kMTransY] = ty; fMat[kMSkewX] = fMat[kMSkewY] = fMat[kMPersp0] = fMat[kMPersp1] = 0; setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask); } // shared cleanup fMat[kMPersp2] = kMatrix22Elem; return true; } void setSkew(float sx, float sy, float px, float py) { fMat[kMScaleX] = SK_Scalar1; fMat[kMSkewX] = sx; fMat[kMTransX] = -sx * py; fMat[kMSkewY] = sy; fMat[kMScaleY] = SK_Scalar1; fMat[kMTransY] = -sy * px; fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kUnknown_Mask); } void setSkew(float sx, float sy) { fMat[kMScaleX] = SK_Scalar1; fMat[kMSkewX] = sx; fMat[kMTransX] = 0; fMat[kMSkewY] = sy; fMat[kMScaleY] = SK_Scalar1; fMat[kMTransY] = 0; fMat[kMPersp0] = fMat[kMPersp1] = 0; fMat[kMPersp2] = kMatrix22Elem; setTypeMask(kUnknown_Mask); } boolean preTranslate(float dx, float dy) { if (hasPerspective()) { SkMatrix m = new SkMatrix(null); m.setTranslate(dx, dy); return this.preConcat(m); } if (dx != 0 || dy != 0) { fMat[kMTransX] += fMat[kMScaleX] * dx + fMat[kMSkewX] * dy; fMat[kMTransY] += fMat[kMSkewY] * dx + fMat[kMScaleY] * dy; setTypeMask(kUnknown_Mask); } return true; } boolean preScale(float sx, float sy, float px, float py) { SkMatrix m = new SkMatrix(null); m.setScale(sx, sy, px, py); return this.preConcat(m); } boolean preScale(float sx, float sy) { SkMatrix m = new SkMatrix(null); m.setScale(sx, sy); return this.preConcat(m); } boolean preRotate(float degrees, float px, float py) { SkMatrix m = new SkMatrix(null); m.setRotate(degrees, px, py); return this.preConcat(m); } boolean preRotate(float degrees) { SkMatrix m = new SkMatrix(null); m.setRotate(degrees); return this.preConcat(m); } boolean preSkew(float sx, float sy, float px, float py) { SkMatrix m = new SkMatrix(null); m.setSkew(sx, sy, px, py); return this.preConcat(m); } boolean preSkew(float sx, float sy) { SkMatrix m = new SkMatrix(null); m.setSkew(sx, sy); return this.preConcat(m); } boolean preConcat(SkMatrix mat) { // check for identity first, so we don't do a needless copy of ourselves // to ourselves inside setConcat() return mat.isIdentity() || this.setConcat(this, mat); } boolean postConcat(SkMatrix mat) { // check for identity first, so we don't do a needless copy of ourselves // to ourselves inside setConcat() return mat.isIdentity() || this.setConcat(mat, this); } boolean postTranslate(float dx, float dy) { if (hasPerspective()) { SkMatrix m = new SkMatrix(null); m.setTranslate(dx, dy); return this.postConcat(m); } if (dx != 0 || dy != 0) { fMat[kMTransX] += dx; fMat[kMTransY] += dy; setTypeMask(kUnknown_Mask); } return true; } boolean postScale(float sx, float sy, float px, float py) { SkMatrix m = new SkMatrix(null); m.setScale(sx, sy, px, py); return this.postConcat(m); } boolean postScale(float sx, float sy) { SkMatrix m = new SkMatrix(null); m.setScale(sx, sy); return this.postConcat(m); } boolean postRotate(float degrees, float px, float py) { SkMatrix m = new SkMatrix(null); m.setRotate(degrees, px, py); return this.postConcat(m); } boolean postRotate(float degrees) { SkMatrix m = new SkMatrix(null); m.setRotate(degrees); return this.postConcat(m); } boolean postSkew(float sx, float sy, float px, float py) { SkMatrix m = new SkMatrix(null); m.setSkew(sx, sy, px, py); return this.postConcat(m); } boolean postSkew(float sx, float sy) { SkMatrix m = new SkMatrix(null); m.setSkew(sx, sy); return this.postConcat(m); } static double sk_inv_determinant(float mat[], boolean isPerspective) { double det; if (isPerspective) { det = mat[SkMatrix.kMScaleX] * ((double) mat[SkMatrix.kMScaleY] * mat[SkMatrix.kMPersp2] - (double) mat[SkMatrix.kMTransY] * mat[SkMatrix.kMPersp1]) + mat[SkMatrix.kMSkewX] * ((double) mat[SkMatrix.kMTransY] * mat[SkMatrix.kMPersp0] - (double) mat[SkMatrix.kMSkewY] * mat[SkMatrix.kMPersp2]) + mat[SkMatrix.kMTransX] * ((double) mat[SkMatrix.kMSkewY] * mat[SkMatrix.kMPersp1] - (double) mat[SkMatrix.kMScaleY] * mat[SkMatrix.kMPersp0]); } else { det = (double) mat[SkMatrix.kMScaleX] * mat[SkMatrix.kMScaleY] - (double) mat[SkMatrix.kMSkewX] * mat[SkMatrix.kMSkewY]; } // Since the determinant is on the order of the cube of the matrix // members, // compare to the cube of the default nearly-zero constant (although // an // estimate of the condition number would be better if it wasn't so // expensive). if (SkScalarNearlyZero((float) det, tolerance * tolerance * tolerance)) { return 0; } return 1.0 / det; } static boolean polyToPoint(float pt[], float poly[], int count) { float x = 1, y = 1; float[] pt1 = new float[2]; float[] pt2 = new float[2]; if (count > 1) { pt1[0] = poly[2] - poly[0]; pt1[1] = poly[3] - poly[1]; y = (float) Math.sqrt(pt1[0] * pt1[0] + pt1[1] * pt1[1]); if (y * y == 0) { return false; } switch (count) { case 2: break; case 3: pt2[0] = poly[1] - poly[5]; pt2[1] = poly[4] - poly[0]; x = (pt1[0] * pt2[0] + pt1[1] * pt2[1]) / y; break; default: pt2[0] = poly[1] - poly[7]; pt2[1] = poly[6] - poly[0]; x = (pt1[0] * pt2[0] + pt1[1] * pt2[1]) / y; break; } } pt[0] = x; pt[1] = y; return true; } static boolean SkScalarNearlyZero(float x, float f) { return Math.abs(x) < f; } static boolean Poly2Proc(float srcPt[], SkMatrix dst, float scale[]) { float invScale = 1 / scale[1]; dst.fMat[kMScaleX] = (srcPt[3] - srcPt[1]) * invScale; dst.fMat[kMSkewY] = (srcPt[0] - srcPt[2]) * invScale; dst.fMat[kMPersp0] = 0; dst.fMat[kMSkewX] = (srcPt[2] - srcPt[0]) * invScale; dst.fMat[kMScaleY] = (srcPt[3] - srcPt[1]) * invScale; dst.fMat[kMPersp1] = 0; dst.fMat[kMTransX] = srcPt[0]; dst.fMat[kMTransY] = srcPt[1]; dst.fMat[kMPersp2] = 1; dst.setTypeMask(kUnknown_Mask); return true; } static boolean Poly3Proc(float srcPt[], SkMatrix dst, float scale[]) { float invScale = 1 / scale[0]; dst.fMat[kMScaleX] = (srcPt[4] - srcPt[0]) * invScale; dst.fMat[kMSkewY] = (srcPt[5] - srcPt[1]) * invScale; dst.fMat[kMPersp0] = 0; invScale = 1 / scale[1]; dst.fMat[kMSkewX] = (srcPt[2] - srcPt[0]) * invScale; dst.fMat[kMScaleY] = (srcPt[3] - srcPt[1]) * invScale; dst.fMat[kMPersp1] = 0; dst.fMat[kMTransX] = srcPt[0]; dst.fMat[kMTransY] = srcPt[1]; dst.fMat[kMPersp2] = 1; dst.setTypeMask(kUnknown_Mask); return true; } static boolean Poly4Proc(float srcPt[], SkMatrix dst, float scale[]) { float a1, a2; float x0, y0, x1, y1, x2, y2; x0 = srcPt[4] - srcPt[0]; y0 = srcPt[5] - srcPt[1]; x1 = srcPt[4] - srcPt[2]; y1 = srcPt[5] - srcPt[3]; x2 = srcPt[4] - srcPt[6]; y2 = srcPt[5] - srcPt[7]; /* check if abs(x2) > abs(y2) */ if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) { float denom = x1 * y2 / x2 - y1; if (denom * denom == 0) { return false; } a1 = ((x0 - x1) * y2 / x2 - y0 + y1) / denom; } else { float denom = x1 - y1 * x2 / y2; if (denom * denom == 0) { return false; } a1 = (x0 - x1 - ((y0 - y1) * x2 / y2)) / denom; } /* check if abs(x1) > abs(y1) */ if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) { float denom = y2 - (x2 * y1 / x1); if (denom * denom == 0) { return false; } a2 = (y0 - y2 - (x0 - x2) * y1 / x1) / denom; } else { float denom = y2 * x1 / y1 - x2; if (denom * denom == 0) { return false; } a2 = ((y0 - y2) * x1 / y1 - x0 + x2) / denom; } float invScale = 1 / scale[0]; dst.fMat[kMScaleX] = (a2 * srcPt[6] + srcPt[6] - srcPt[0]) * invScale; dst.fMat[kMSkewY] = (a2 * srcPt[7] + srcPt[7] - srcPt[1]) * invScale; dst.fMat[kMPersp0] = a2 * invScale; invScale = 1 / scale[1]; dst.fMat[kMSkewX] = (a1 * srcPt[2] + srcPt[2] - srcPt[0]) * invScale; dst.fMat[kMScaleY] = (a1 * srcPt[3] + srcPt[3] - srcPt[1]) * invScale; dst.fMat[kMPersp1] = a1 * invScale; dst.fMat[kMTransX] = srcPt[0]; dst.fMat[kMTransY] = srcPt[1]; dst.fMat[kMPersp2] = 1; dst.setTypeMask(kUnknown_Mask); return true; } /* Taken from Rob Johnson's original sample code in QuickDraw GX */ boolean setPolyToPoly(float src[], float dst[], int count) { if (count > 4) { return false; } if (0 == count) { this.reset(); return true; } if (1 == count) { this.setTranslate(dst[0] - src[0], dst[1] - src[1]); return true; } float[] scale = new float[2]; if (!polyToPoint(scale, src, count) || SkScalarNearlyZero(scale[0], tolerance) || SkScalarNearlyZero(scale[1], tolerance)) { return false; } SkMatrix tempMap = new SkMatrix(null); SkMatrix result = new SkMatrix(null); tempMap.setTypeMask(kUnknown_Mask); boolean flag = true; switch (count - 2) { case 0: flag = Poly2Proc(src, tempMap, scale); break; case 1: flag = Poly3Proc(src, tempMap, scale); break; case 2: flag = Poly4Proc(src, tempMap, scale); break; } if (!flag) return false; if (!tempMap.invert(result)) { return false; } switch (count - 2) { case 0: flag = Poly2Proc(dst, tempMap, scale); break; case 1: flag = Poly3Proc(dst, tempMap, scale); break; case 2: flag = Poly4Proc(dst, tempMap, scale); break; } if (!result.setConcat(tempMap, result)) { return false; } this.fTypeMask = result.fTypeMask; System.arraycopy(result.fMat, 0, this.fMat, 0, this.fMat.length); return true; } //////////////////////////////////////////////////////////////////////////////// void Identity_pts(SkMatrix m, float dst[], float src[], int count) { if (dst != src && count > 0) { System.arraycopy(src, 0, dst, 0, count * 2); } } void Trans_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { float tx = m.fMat[kMTransX]; float ty = m.fMat[kMTransY]; int i = 0; do { dst[i + 1] = src[i + 1] + ty; dst[i + 0] = src[i + 0] + tx; i += 2; } while (--count != 0); } } void Scale_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { float mx = m.fMat[kMScaleX]; float my = m.fMat[kMScaleY]; int i = 0; do { dst[i + 1] = src[i + 1] * my; dst[i + 0] = src[i + 0] * mx; i += 2; } while (--count != 0); } } void ScaleTrans_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { float mx = m.fMat[kMScaleX]; float my = m.fMat[kMScaleY]; float tx = m.fMat[kMTransX]; float ty = m.fMat[kMTransY]; int i = 0; do { dst[i + 1] = src[i + 1] * my + ty; dst[i + 0] = src[i + 0] * mx + tx; i += 2; } while (--count != 0); } } void Rot_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { float mx = m.fMat[kMScaleX]; float my = m.fMat[kMScaleY]; float kx = m.fMat[kMSkewX]; float ky = m.fMat[kMSkewY]; int i = 0; do { float sy = src[i + 1]; float sx = src[i + 0]; dst[i + 1] = sx * ky + sy * my; dst[i + 0] = sx * mx + sy * kx; i += 2; } while (--count != 0); } } void RotTrans_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { float mx = m.fMat[kMScaleX]; float my = m.fMat[kMScaleY]; float kx = m.fMat[kMSkewX]; float ky = m.fMat[kMSkewY]; float tx = m.fMat[kMTransX]; float ty = m.fMat[kMTransY]; int i = 0; do { float sy = src[i + 1]; float sx = src[i + 0]; dst[i + 1] = sx * ky + sy * my + ty; dst[i + 0] = sx * mx + sy * kx + tx; i += 2; } while (--count != 0); } } void Persp_pts(SkMatrix m, float dst[], float src[], int count) { if (count > 0) { int i = 0; do { float sy = src[i + 1]; float sx = src[i + 0]; float x = sx * m.fMat[kMScaleX] + sy * m.fMat[kMSkewX] + m.fMat[kMTransX]; float y = sx * m.fMat[kMSkewY] + sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; float z = sx * m.fMat[kMPersp0] + sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]; if (z != 0) { z = SK_Scalar1 / z; } dst[i + 1] = y * z; dst[i + 0] = x * z; i += 2; } while (--count != 0); } } void mapPoints(float dst[], float src[], int count) { switch (this.getType() & kAllMasks) { case 0: Identity_pts(this, dst, src, count); break; case 1: Trans_pts(this, dst, src, count); break; case 2: Scale_pts(this, dst, src, count); break; case 3: ScaleTrans_pts(this, dst, src, count); break; case 4: Rot_pts(this, dst, src, count); break; case 5: RotTrans_pts(this, dst, src, count); break; case 6: RotTrans_pts(this, dst, src, count); break; case 7: RotTrans_pts(this, dst, src, count); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: Persp_pts(this, dst, src, count); break; } } void Identity_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx; pt[1] = sy; } void Trans_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx + m.fMat[kMTransX]; pt[1] = sy + m.fMat[kMTransY]; } void Scale_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx * m.fMat[kMScaleX]; pt[1] = sy * m.fMat[kMScaleY]; } void ScaleTrans_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx * m.fMat[kMScaleX] + m.fMat[kMTransX]; pt[1] = sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; } void Rot_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx * m.fMat[kMScaleX] + sy * m.fMat[kMSkewX] + m.fMat[kMTransX]; pt[1] = sx * m.fMat[kMSkewY] + sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; } void RotTrans_xy(SkMatrix m, float sx, float sy, float pt[]) { pt[0] = sx * m.fMat[kMScaleX] + sy * m.fMat[kMSkewX] + m.fMat[kMTransX]; pt[1] = sx * m.fMat[kMSkewY] + sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; } void Persp_xy(SkMatrix m, float sx, float sy, float pt[]) { float x = sx * m.fMat[kMScaleX] + sy * m.fMat[kMSkewX] + m.fMat[kMTransX]; float y = sx * m.fMat[kMSkewY] + sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; float z = sx * m.fMat[kMPersp0] + sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]; if (z != 0) { z = SK_Scalar1 / z; } pt[0] = x * z; pt[1] = y * z; } void mapXY(int masks, SkMatrix m, float x, float y, float result[]) { switch (masks & kAllMasks) { case 0: Identity_xy(m, x, y, result); break; case 1: Trans_xy(m, x, y, result); break; case 2: Scale_xy(m, x, y, result); break; case 3: ScaleTrans_xy(m, x, y, result); break; case 4: Rot_xy(m, x, y, result); break; case 5: RotTrans_xy(m, x, y, result); break; case 6: Rot_xy(m, x, y, result); break; case 7: RotTrans_xy(m, x, y, result); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: Persp_xy(m, x, y, result); break; } } void mapVectors(float dst[], float src[], int count) { if ((this.fTypeMask & kPerspective_Mask) != 0) { float[] origin = new float[2]; mapXY(this.fTypeMask, this, 0, 0, origin); int index = 0; for (int i = 0; i < count; i++) { float[] tmp = new float[2]; mapXY(this.fTypeMask, this, src[index+0], src[index+1], tmp); dst[index+0] = tmp[0] - origin[0]; dst[index+1] = tmp[1] - origin[1]; index += 2; } } else { SkMatrix tmp = new SkMatrix(null); tmp.fTypeMask = this.fTypeMask; System.arraycopy(this.fMat, 0, tmp.fMat, 0, this.fMat.length); tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0; tmp.clearTypeMask(kTranslate_Mask); tmp.mapPoints(dst, src, count); } } boolean mapRect(RectF dst, RectF src) { if (this.rectStaysRect()) { float[] tmpSrc = new float[4]; float[] tmpDst = new float[4]; tmpSrc[0] = src.left; tmpSrc[1] = src.top; tmpSrc[2] = src.right; tmpSrc[3] = src.bottom; this.mapPoints(tmpDst, tmpSrc, 2); dst.left = (int) tmpDst[0]; dst.top = (int) tmpDst[1]; dst.right = (int) tmpDst[2]; dst.bottom = (int) tmpDst[3]; dst.sort(); return true; } else { float[] quad = new float[8]; quad[0] = src.left; quad[1] = src.top; quad[2] = src.right; quad[3] = src.top; quad[4] = src.right; quad[5] = src.bottom; quad[6] = src.left; quad[7] = src.bottom; this.mapPoints(quad, quad, 4); float l, t, r, b; l = r = quad[0]; t = b = quad[1]; for (int i = 2; i < 8; i += 2) { float x = quad[i + 0]; float y = quad[i + 1]; if (x < l) l = x; else if (x > r) r = x; if (y < t) t = y; else if (y > b) b = y; } dst.set((int) l, (int) t, (int) r, (int) b); return false; } } float mapRadius(float radius) { float[] points = new float[4]; points[0] = radius; points[1] = 0; points[2] = 0; points[3] = radius; this.mapVectors(points, points, 2); float d0 = (float) Math.sqrt(points[0] * points[0] + points[1] * points[1]); float d1 = (float) Math.sqrt(points[2] * points[2] + points[3] * points[3]); if (radius == Float.MAX_VALUE) { return Float.POSITIVE_INFINITY; } else if(radius == Float.MIN_VALUE) { return 0f; } return (float) Math.sqrt(d0 * d1); } void setValues(float values[]) { for (int i = 0; i < 9; i++) { this.set(i, values[i]); } } void getValues(float values[]) { for (int i = 0; i < 9; i++) { values[i] = this.get(i); } } } }