/* * Copyright (C) 2006 The Android Open Source Project * * 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 android.graphics; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.io.PrintWriter; /** * The Matrix class holds a 3x3 matrix for transforming coordinates. */ 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 /** @hide */ public final static Matrix IDENTITY_MATRIX = new Matrix() { void oops() { throw new IllegalStateException("Matrix can not be modified"); } @Override public void set(Matrix src) { oops(); } @Override public void reset() { oops(); } @Override public void setTranslate(float dx, float dy) { oops(); } @Override public void setScale(float sx, float sy, float px, float py) { oops(); } @Override public void setScale(float sx, float sy) { oops(); } @Override public void setRotate(float degrees, float px, float py) { oops(); } @Override public void setRotate(float degrees) { oops(); } @Override public void setSinCos(float sinValue, float cosValue, float px, float py) { oops(); } @Override public void setSinCos(float sinValue, float cosValue) { oops(); } @Override public void setSkew(float kx, float ky, float px, float py) { oops(); } @Override public void setSkew(float kx, float ky) { oops(); } @Override public boolean setConcat(Matrix a, Matrix b) { oops(); return false; } @Override public boolean preTranslate(float dx, float dy) { oops(); return false; } @Override public boolean preScale(float sx, float sy, float px, float py) { oops(); return false; } @Override public boolean preScale(float sx, float sy) { oops(); return false; } @Override public boolean preRotate(float degrees, float px, float py) { oops(); return false; } @Override public boolean preRotate(float degrees) { oops(); return false; } @Override public boolean preSkew(float kx, float ky, float px, float py) { oops(); return false; } @Override public boolean preSkew(float kx, float ky) { oops(); return false; } @Override public boolean preConcat(Matrix other) { oops(); return false; } @Override public boolean postTranslate(float dx, float dy) { oops(); return false; } @Override public boolean postScale(float sx, float sy, float px, float py) { oops(); return false; } @Override public boolean postScale(float sx, float sy) { oops(); return false; } @Override public boolean postRotate(float degrees, float px, float py) { oops(); return false; } @Override public boolean postRotate(float degrees) { oops(); return false; } @Override public boolean postSkew(float kx, float ky, float px, float py) { oops(); return false; } @Override public boolean postSkew(float kx, float ky) { oops(); return false; } @Override public boolean postConcat(Matrix other) { oops(); return false; } @Override public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { oops(); return false; } @Override public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) { oops(); return false; } @Override public void setValues(float[] values) { oops(); } }; // sizeof(SkMatrix) is 9 * sizeof(float) + uint32_t private static final long NATIVE_ALLOCATION_SIZE = 40; private static class NoImagePreloadHolder { public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( Matrix.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE); } /** * @hide */ public final long native_instance; /** * Create an identity matrix */ public Matrix() { native_instance = nCreate(0); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance); } /** * Create a matrix that is a (deep) copy of src * * @param src The matrix to copy into this matrix */ public Matrix(Matrix src) { native_instance = nCreate(src != null ? src.native_instance : 0); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance); } /** * Returns true if the matrix is identity. This maybe faster than testing if (getType() == 0) */ public boolean isIdentity() { return nIsIdentity(native_instance); } /** * Gets whether this matrix is affine. An affine matrix preserves straight lines and has no * perspective. * * @return Whether the matrix is affine. */ public boolean isAffine() { return nIsAffine(native_instance); } /** * 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 nRectStaysRect(native_instance); } /** * (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 { nSet(native_instance, src.native_instance); } } /** * Returns true iff obj is a Matrix and its values equal our values. */ @Override public boolean equals(Object obj) { // if (obj == this) return true; -- NaN value would mean matrix != itself if (!(obj instanceof Matrix)) { return false; } return nEquals(native_instance, ((Matrix) obj).native_instance); } @Override public int hashCode() { // This should generate the hash code by performing some arithmetic operation on all // the matrix elements -- our equals() does an element-by-element comparison, and we // need to ensure that the hash code for two equal objects is the same. We're not // really using this at the moment, so we take the easy way out. return 44; } /** Set the matrix to identity */ public void reset() { nReset(native_instance); } /** Set the matrix to translate by (dx, dy). */ public void setTranslate(float dx, float dy) { nSetTranslate(native_instance, 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) { nSetScale(native_instance, sx, sy, px, py); } /** Set the matrix to scale by sx and sy. */ public void setScale(float sx, float sy) { nSetScale(native_instance, 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) { nSetRotate(native_instance, degrees, px, py); } /** * Set the matrix to rotate about (0,0) by the specified number of degrees. */ public void setRotate(float degrees) { nSetRotate(native_instance, 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) { nSetSinCos(native_instance, sinValue, cosValue, px, py); } /** Set the matrix to rotate by the specified sine and cosine values. */ public void setSinCos(float sinValue, float cosValue) { nSetSinCos(native_instance, 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) { nSetSkew(native_instance, kx, ky, px, py); } /** Set the matrix to skew by sx and sy. */ public void setSkew(float kx, float ky) { nSetSkew(native_instance, kx, ky); } /** * Set the matrix to the concatenation of the two specified matrices and return true. *

* Either of the two matrices may also be the target matrix, that is * matrixA.setConcat(matrixA, matrixB); is valid. *

*

* In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this function returns * true only if the result can be represented. In * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true. *

*/ public boolean setConcat(Matrix a, Matrix b) { nSetConcat(native_instance, a.native_instance, b.native_instance); return true; } /** * Preconcats the matrix with the specified translation. M' = M * T(dx, dy) */ public boolean preTranslate(float dx, float dy) { nPreTranslate(native_instance, dx, dy); return true; } /** * 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) { nPreScale(native_instance, sx, sy, px, py); return true; } /** * Preconcats the matrix with the specified scale. M' = M * S(sx, sy) */ public boolean preScale(float sx, float sy) { nPreScale(native_instance, sx, sy); return true; } /** * Preconcats the matrix with the specified rotation. M' = M * R(degrees, px, py) */ public boolean preRotate(float degrees, float px, float py) { nPreRotate(native_instance, degrees, px, py); return true; } /** * Preconcats the matrix with the specified rotation. M' = M * R(degrees) */ public boolean preRotate(float degrees) { nPreRotate(native_instance, degrees); return true; } /** * 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) { nPreSkew(native_instance, kx, ky, px, py); return true; } /** * Preconcats the matrix with the specified skew. M' = M * K(kx, ky) */ public boolean preSkew(float kx, float ky) { nPreSkew(native_instance, kx, ky); return true; } /** * Preconcats the matrix with the specified matrix. M' = M * other */ public boolean preConcat(Matrix other) { nPreConcat(native_instance, other.native_instance); return true; } /** * Postconcats the matrix with the specified translation. M' = T(dx, dy) * M */ public boolean postTranslate(float dx, float dy) { nPostTranslate(native_instance, dx, dy); return true; } /** * 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) { nPostScale(native_instance, sx, sy, px, py); return true; } /** * Postconcats the matrix with the specified scale. M' = S(sx, sy) * M */ public boolean postScale(float sx, float sy) { nPostScale(native_instance, sx, sy); return true; } /** * Postconcats the matrix with the specified rotation. M' = R(degrees, px, py) * M */ public boolean postRotate(float degrees, float px, float py) { nPostRotate(native_instance, degrees, px, py); return true; } /** * Postconcats the matrix with the specified rotation. M' = R(degrees) * M */ public boolean postRotate(float degrees) { nPostRotate(native_instance, degrees); return true; } /** * 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) { nPostSkew(native_instance, kx, ky, px, py); return true; } /** * Postconcats the matrix with the specified skew. M' = K(kx, ky) * M */ public boolean postSkew(float kx, float ky) { nPostSkew(native_instance, kx, ky); return true; } /** * Postconcats the matrix with the specified matrix. M' = other * M */ public boolean postConcat(Matrix other) { nPostConcat(native_instance, other.native_instance); return true; } /** * 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 nSetRectToRect(native_instance, 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); return nSetPolyToPoly(native_instance, src, srcIndex, dst, dstIndex, pointCount); } /** * 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 nInvert(native_instance, inverse.native_instance); } /** * 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); nMapPoints(native_instance, dst, dstIndex, src, srcIndex, pointCount, true); } /** * 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]. Note: this method does not apply the translation associated with * the matrix. Use {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the * translation to be applied. * * @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); nMapPoints(native_instance, dst, dstIndex, src, srcIndex, vectorCount, false); } /** * 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]. Note: this method does not apply the translation associated with * the matrix. Use {@link Matrix#mapPoints(float[], float[])} if you want the translation to be * applied. * * @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. Note: this method does not apply the translation associated with the matrix. Use * {@link Matrix#mapPoints(float[])} if you want the translation to be applied. * * @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 nMapRect(native_instance, 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 nMapRadius(native_instance, radius); } /** * Copy 9 values from the matrix into the array. */ public void getValues(float[] values) { if (values.length < 9) { throw new ArrayIndexOutOfBoundsException(); } nGetValues(native_instance, 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(); } nSetValues(native_instance, values); } @Override 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(']'); } /** @hide */ public final long ni() { return native_instance; } // ------------------ Regular JNI ------------------------ private static native long nCreate(long nSrc_or_zero); private static native long nGetNativeFinalizer(); // ------------------ Fast JNI ------------------------ @FastNative private static native boolean nSetRectToRect(long nObject, RectF src, RectF dst, int stf); @FastNative private static native boolean nSetPolyToPoly(long nObject, float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount); @FastNative private static native void nMapPoints(long nObject, float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount, boolean isPts); @FastNative private static native boolean nMapRect(long nObject, RectF dst, RectF src); @FastNative private static native void nGetValues(long nObject, float[] values); @FastNative private static native void nSetValues(long nObject, float[] values); // ------------------ Critical JNI ------------------------ @CriticalNative private static native boolean nIsIdentity(long nObject); @CriticalNative private static native boolean nIsAffine(long nObject); @CriticalNative private static native boolean nRectStaysRect(long nObject); @CriticalNative private static native void nReset(long nObject); @CriticalNative private static native void nSet(long nObject, long nOther); @CriticalNative private static native void nSetTranslate(long nObject, float dx, float dy); @CriticalNative private static native void nSetScale(long nObject, float sx, float sy, float px, float py); @CriticalNative private static native void nSetScale(long nObject, float sx, float sy); @CriticalNative private static native void nSetRotate(long nObject, float degrees, float px, float py); @CriticalNative private static native void nSetRotate(long nObject, float degrees); @CriticalNative private static native void nSetSinCos(long nObject, float sinValue, float cosValue, float px, float py); @CriticalNative private static native void nSetSinCos(long nObject, float sinValue, float cosValue); @CriticalNative private static native void nSetSkew(long nObject, float kx, float ky, float px, float py); @CriticalNative private static native void nSetSkew(long nObject, float kx, float ky); @CriticalNative private static native void nSetConcat(long nObject, long nA, long nB); @CriticalNative private static native void nPreTranslate(long nObject, float dx, float dy); @CriticalNative private static native void nPreScale(long nObject, float sx, float sy, float px, float py); @CriticalNative private static native void nPreScale(long nObject, float sx, float sy); @CriticalNative private static native void nPreRotate(long nObject, float degrees, float px, float py); @CriticalNative private static native void nPreRotate(long nObject, float degrees); @CriticalNative private static native void nPreSkew(long nObject, float kx, float ky, float px, float py); @CriticalNative private static native void nPreSkew(long nObject, float kx, float ky); @CriticalNative private static native void nPreConcat(long nObject, long nOther_matrix); @CriticalNative private static native void nPostTranslate(long nObject, float dx, float dy); @CriticalNative private static native void nPostScale(long nObject, float sx, float sy, float px, float py); @CriticalNative private static native void nPostScale(long nObject, float sx, float sy); @CriticalNative private static native void nPostRotate(long nObject, float degrees, float px, float py); @CriticalNative private static native void nPostRotate(long nObject, float degrees); @CriticalNative private static native void nPostSkew(long nObject, float kx, float ky, float px, float py); @CriticalNative private static native void nPostSkew(long nObject, float kx, float ky); @CriticalNative private static native void nPostConcat(long nObject, long nOther_matrix); @CriticalNative private static native boolean nInvert(long nObject, long nInverse); @CriticalNative private static native float nMapRadius(long nObject, float radius); @CriticalNative private static native boolean nEquals(long nA, long nB); }