| /* |
| * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog; |
| import com.android.layoutlib.bridge.Bridge; |
| import com.android.layoutlib.bridge.impl.DelegateManager; |
| import com.android.tools.layoutlib.annotations.LayoutlibDelegate; |
| |
| import android.graphics.Matrix.ScaleToFit; |
| |
| import java.awt.geom.AffineTransform; |
| import java.awt.geom.NoninvertibleTransformException; |
| |
| /** |
| * Delegate implementing the native methods of android.graphics.Matrix |
| * |
| * Through the layoutlib_create tool, the original native methods of Matrix have been replaced |
| * by calls to methods of the same name in this delegate class. |
| * |
| * This class behaves like the original native implementation, but in Java, keeping previously |
| * native data into its own objects and mapping them to int that are sent back and forth between |
| * it and the original Matrix class. |
| * |
| * @see DelegateManager |
| * |
| */ |
| public final class Matrix_Delegate { |
| |
| private final static int MATRIX_SIZE = 9; |
| |
| // ---- delegate manager ---- |
| private static final DelegateManager<Matrix_Delegate> sManager = |
| new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class); |
| |
| // ---- delegate data ---- |
| private float mValues[] = new float[MATRIX_SIZE]; |
| |
| // ---- Public Helper methods ---- |
| |
| public static Matrix_Delegate getDelegate(long native_instance) { |
| return sManager.getDelegate(native_instance); |
| } |
| |
| /** |
| * Returns an {@link AffineTransform} matching the given Matrix. |
| */ |
| public static AffineTransform getAffineTransform(Matrix m) { |
| Matrix_Delegate delegate = sManager.getDelegate(m.native_instance); |
| if (delegate == null) { |
| return null; |
| } |
| |
| return delegate.getAffineTransform(); |
| } |
| |
| public static boolean hasPerspective(Matrix m) { |
| Matrix_Delegate delegate = sManager.getDelegate(m.native_instance); |
| if (delegate == null) { |
| return false; |
| } |
| |
| return delegate.hasPerspective(); |
| } |
| |
| /** |
| * Sets the content of the matrix with the content of another matrix. |
| */ |
| public void set(Matrix_Delegate matrix) { |
| System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE); |
| } |
| |
| /** |
| * Sets the content of the matrix with the content of another matrix represented as an array |
| * of values. |
| */ |
| public void set(float[] values) { |
| System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); |
| } |
| |
| /** |
| * Resets the matrix to be the identity matrix. |
| */ |
| public void reset() { |
| reset(mValues); |
| } |
| |
| /** |
| * Returns whether or not the matrix is identity. |
| */ |
| public boolean isIdentity() { |
| for (int i = 0, k = 0; i < 3; i++) { |
| for (int j = 0; j < 3; j++, k++) { |
| if (mValues[k] != ((i==j) ? 1 : 0)) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public static float[] makeValues(AffineTransform matrix) { |
| float[] values = new float[MATRIX_SIZE]; |
| values[0] = (float) matrix.getScaleX(); |
| values[1] = (float) matrix.getShearX(); |
| values[2] = (float) matrix.getTranslateX(); |
| values[3] = (float) matrix.getShearY(); |
| values[4] = (float) matrix.getScaleY(); |
| values[5] = (float) matrix.getTranslateY(); |
| values[6] = 0.f; |
| values[7] = 0.f; |
| values[8] = 1.f; |
| |
| return values; |
| } |
| |
| public static Matrix_Delegate make(AffineTransform matrix) { |
| return new Matrix_Delegate(makeValues(matrix)); |
| } |
| |
| public boolean mapRect(RectF dst, RectF src) { |
| // array with 4 corners |
| float[] corners = new float[] { |
| src.left, src.top, |
| src.right, src.top, |
| src.right, src.bottom, |
| src.left, src.bottom, |
| }; |
| |
| // apply the transform to them. |
| mapPoints(corners); |
| |
| // now put the result in the rect. We take the min/max of Xs and min/max of Ys |
| dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); |
| dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); |
| |
| dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); |
| dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); |
| |
| |
| return (computeTypeMask() & kRectStaysRect_Mask) != 0; |
| } |
| |
| |
| /** |
| * Returns an {@link AffineTransform} matching the matrix. |
| */ |
| public AffineTransform getAffineTransform() { |
| return getAffineTransform(mValues); |
| } |
| |
| public boolean hasPerspective() { |
| return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1); |
| } |
| |
| |
| |
| // ---- native methods ---- |
| |
| @LayoutlibDelegate |
| /*package*/ static long native_create(long native_src_or_zero) { |
| // create the delegate |
| Matrix_Delegate newDelegate = new Matrix_Delegate(); |
| |
| // copy from values if needed. |
| if (native_src_or_zero > 0) { |
| Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero); |
| if (oldDelegate != null) { |
| System.arraycopy( |
| oldDelegate.mValues, 0, |
| newDelegate.mValues, 0, |
| MATRIX_SIZE); |
| } |
| } |
| |
| return sManager.addNewDelegate(newDelegate); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_isIdentity(long native_object) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return false; |
| } |
| |
| return d.isIdentity(); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_isAffine(long native_object) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return true; |
| } |
| |
| return (d.computeTypeMask() & kPerspective_Mask) == 0; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_rectStaysRect(long native_object) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return true; |
| } |
| |
| return (d.computeTypeMask() & kRectStaysRect_Mask) != 0; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_reset(long native_object) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| reset(d.mValues); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_set(long native_object, long other) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| Matrix_Delegate src = sManager.getDelegate(other); |
| if (src == null) { |
| return; |
| } |
| |
| System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setTranslate(long native_object, float dx, float dy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| setTranslate(d.mValues, dx, dy); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setScale(long native_object, float sx, float sy, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| d.mValues = getScale(sx, sy, px, py); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setScale(long native_object, float sx, float sy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| d.mValues[0] = sx; |
| d.mValues[1] = 0; |
| d.mValues[2] = 0; |
| d.mValues[3] = 0; |
| d.mValues[4] = sy; |
| d.mValues[5] = 0; |
| d.mValues[6] = 0; |
| d.mValues[7] = 0; |
| d.mValues[8] = 1; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setRotate(long native_object, float degrees, float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| d.mValues = getRotate(degrees, px, py); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setRotate(long native_object, float degrees) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| setRotate(d.mValues, degrees); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| // TODO: do it in one pass |
| |
| // translate so that the pivot is in 0,0 |
| setTranslate(d.mValues, -px, -py); |
| |
| // scale |
| d.postTransform(getRotate(sinValue, cosValue)); |
| // translate back the pivot |
| d.postTransform(getTranslate(px, py)); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| setRotate(d.mValues, sinValue, cosValue); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setSkew(long native_object, float kx, float ky, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| d.mValues = getSkew(kx, ky, px, py); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setSkew(long native_object, float kx, float ky) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| d.mValues[0] = 1; |
| d.mValues[1] = kx; |
| d.mValues[2] = -0; |
| d.mValues[3] = ky; |
| d.mValues[4] = 1; |
| d.mValues[5] = 0; |
| d.mValues[6] = 0; |
| d.mValues[7] = 0; |
| d.mValues[8] = 1; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setConcat(long native_object, long a, long b) { |
| if (a == native_object) { |
| native_preConcat(native_object, b); |
| return; |
| } else if (b == native_object) { |
| native_postConcat(native_object, a); |
| return; |
| } |
| |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| Matrix_Delegate a_mtx = sManager.getDelegate(a); |
| Matrix_Delegate b_mtx = sManager.getDelegate(b); |
| if (d != null && a_mtx != null && b_mtx != null) { |
| multiply(d.mValues, a_mtx.mValues, b_mtx.mValues); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preTranslate(long native_object, float dx, float dy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getTranslate(dx, dy)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preScale(long native_object, float sx, float sy, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getScale(sx, sy, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preScale(long native_object, float sx, float sy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getScale(sx, sy)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preRotate(long native_object, float degrees, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getRotate(degrees, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preRotate(long native_object, float degrees) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| |
| double rad = Math.toRadians(degrees); |
| float sin = (float) Math.sin(rad); |
| float cos = (float) Math.cos(rad); |
| |
| d.preTransform(getRotate(sin, cos)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preSkew(long native_object, float kx, float ky, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getSkew(kx, ky, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preSkew(long native_object, float kx, float ky) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.preTransform(getSkew(kx, ky)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_preConcat(long native_object, long other_matrix) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| Matrix_Delegate other = sManager.getDelegate(other_matrix); |
| if (d != null && other != null) { |
| d.preTransform(other.mValues); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postTranslate(long native_object, float dx, float dy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getTranslate(dx, dy)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postScale(long native_object, float sx, float sy, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getScale(sx, sy, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postScale(long native_object, float sx, float sy) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getScale(sx, sy)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postRotate(long native_object, float degrees, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getRotate(degrees, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postRotate(long native_object, float degrees) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getRotate(degrees)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postSkew(long native_object, float kx, float ky, |
| float px, float py) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getSkew(kx, ky, px, py)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postSkew(long native_object, float kx, float ky) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d != null) { |
| d.postTransform(getSkew(kx, ky)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_postConcat(long native_object, long other_matrix) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| Matrix_Delegate other = sManager.getDelegate(other_matrix); |
| if (d != null && other != null) { |
| d.postTransform(other.mValues); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_setRectToRect(long native_object, RectF src, |
| RectF dst, int stf) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return false; |
| } |
| |
| if (src.isEmpty()) { |
| reset(d.mValues); |
| return false; |
| } |
| |
| if (dst.isEmpty()) { |
| d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5] |
| = d.mValues[6] = d.mValues[7] = 0; |
| d.mValues[8] = 1; |
| } else { |
| float tx, sx = dst.width() / src.width(); |
| float ty, sy = dst.height() / src.height(); |
| boolean xLarger = false; |
| |
| if (stf != ScaleToFit.FILL.nativeInt) { |
| if (sx > sy) { |
| xLarger = true; |
| sx = sy; |
| } else { |
| sy = sx; |
| } |
| } |
| |
| tx = dst.left - src.left * sx; |
| ty = dst.top - src.top * sy; |
| if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) { |
| float diff; |
| |
| if (xLarger) { |
| diff = dst.width() - src.width() * sy; |
| } else { |
| diff = dst.height() - src.height() * sy; |
| } |
| |
| if (stf == ScaleToFit.CENTER.nativeInt) { |
| diff = diff / 2; |
| } |
| |
| if (xLarger) { |
| tx += diff; |
| } else { |
| ty += diff; |
| } |
| } |
| |
| d.mValues[0] = sx; |
| d.mValues[4] = sy; |
| d.mValues[2] = tx; |
| d.mValues[5] = ty; |
| d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0; |
| |
| } |
| // shared cleanup |
| d.mValues[8] = 1; |
| return true; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_setPolyToPoly(long native_object, float[] src, int srcIndex, |
| float[] dst, int dstIndex, int pointCount) { |
| // FIXME |
| Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, |
| "Matrix.setPolyToPoly is not supported.", |
| null, null /*data*/); |
| return false; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_invert(long native_object, long inverse) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return false; |
| } |
| |
| Matrix_Delegate inv_mtx = sManager.getDelegate(inverse); |
| if (inv_mtx == null) { |
| return false; |
| } |
| |
| try { |
| AffineTransform affineTransform = d.getAffineTransform(); |
| AffineTransform inverseTransform = affineTransform.createInverse(); |
| inv_mtx.mValues[0] = (float)inverseTransform.getScaleX(); |
| inv_mtx.mValues[1] = (float)inverseTransform.getShearX(); |
| inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX(); |
| inv_mtx.mValues[3] = (float)inverseTransform.getScaleX(); |
| inv_mtx.mValues[4] = (float)inverseTransform.getShearY(); |
| inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY(); |
| |
| return true; |
| } catch (NoninvertibleTransformException e) { |
| return false; |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_mapPoints(long native_object, float[] dst, int dstIndex, |
| float[] src, int srcIndex, int ptCount, boolean isPts) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| if (isPts) { |
| d.mapPoints(dst, dstIndex, src, srcIndex, ptCount); |
| } else { |
| d.mapVectors(dst, dstIndex, src, srcIndex, ptCount); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_mapRect(long native_object, RectF dst, RectF src) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return false; |
| } |
| |
| return d.mapRect(dst, src); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static float native_mapRadius(long native_object, float radius) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return 0.f; |
| } |
| |
| float[] src = new float[] { radius, 0.f, 0.f, radius }; |
| d.mapVectors(src, 0, src, 0, 2); |
| |
| float l1 = (float) Math.hypot(src[0], src[1]); |
| float l2 = (float) Math.hypot(src[2], src[3]); |
| return (float) Math.sqrt(l1 * l2); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_getValues(long native_object, float[] values) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setValues(long native_object, float[] values) { |
| Matrix_Delegate d = sManager.getDelegate(native_object); |
| if (d == null) { |
| return; |
| } |
| |
| System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_equals(long native_a, long native_b) { |
| Matrix_Delegate a = sManager.getDelegate(native_a); |
| if (a == null) { |
| return false; |
| } |
| |
| Matrix_Delegate b = sManager.getDelegate(native_b); |
| if (b == null) { |
| return false; |
| } |
| |
| for (int i = 0 ; i < MATRIX_SIZE ; i++) { |
| if (a.mValues[i] != b.mValues[i]) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void finalizer(long native_instance) { |
| sManager.removeJavaReferenceFor(native_instance); |
| } |
| |
| // ---- Private helper methods ---- |
| |
| /*package*/ static AffineTransform getAffineTransform(float[] matrix) { |
| // the AffineTransform constructor takes the value in a different order |
| // for a matrix [ 0 1 2 ] |
| // [ 3 4 5 ] |
| // the order is 0, 3, 1, 4, 2, 5... |
| return new AffineTransform( |
| matrix[0], matrix[3], matrix[1], |
| matrix[4], matrix[2], matrix[5]); |
| } |
| |
| /** |
| * Reset a matrix to the identity |
| */ |
| private static void reset(float[] mtx) { |
| for (int i = 0, k = 0; i < 3; i++) { |
| for (int j = 0; j < 3; j++, k++) { |
| mtx[k] = ((i==j) ? 1 : 0); |
| } |
| } |
| } |
| |
| @SuppressWarnings("unused") |
| private final static int kIdentity_Mask = 0; |
| private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation |
| private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale |
| private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates |
| private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective |
| private final static int kRectStaysRect_Mask = 0x10; |
| @SuppressWarnings("unused") |
| private final static int kUnknown_Mask = 0x80; |
| |
| @SuppressWarnings("unused") |
| private final static int kAllMasks = kTranslate_Mask | |
| kScale_Mask | |
| kAffine_Mask | |
| kPerspective_Mask | |
| kRectStaysRect_Mask; |
| |
| // these guys align with the masks, so we can compute a mask from a variable 0/1 |
| @SuppressWarnings("unused") |
| private final static int kTranslate_Shift = 0; |
| @SuppressWarnings("unused") |
| private final static int kScale_Shift = 1; |
| @SuppressWarnings("unused") |
| private final static int kAffine_Shift = 2; |
| @SuppressWarnings("unused") |
| private final static int kPerspective_Shift = 3; |
| private final static int kRectStaysRect_Shift = 4; |
| |
| private int computeTypeMask() { |
| int mask = 0; |
| |
| if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) { |
| mask |= kPerspective_Mask; |
| } |
| |
| if (mValues[2] != 0. || mValues[5] != 0.) { |
| mask |= kTranslate_Mask; |
| } |
| |
| float m00 = mValues[0]; |
| float m01 = mValues[1]; |
| float m10 = mValues[3]; |
| float m11 = mValues[4]; |
| |
| if (m01 != 0. || m10 != 0.) { |
| mask |= kAffine_Mask; |
| } |
| |
| if (m00 != 1. || m11 != 1.) { |
| mask |= kScale_Mask; |
| } |
| |
| if ((mask & kPerspective_Mask) == 0) { |
| // map non-zero to 1 |
| int im00 = m00 != 0 ? 1 : 0; |
| int im01 = m01 != 0 ? 1 : 0; |
| int im10 = m10 != 0 ? 1 : 0; |
| int im11 = m11 != 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 = (im00 | im11) ^ 1; // true if both are 0 |
| int dp1 = im00 & im11; // true if both are 1 |
| int ds0 = (im01 | im10) ^ 1; // true if both are 0 |
| int ds1 = im01 & im10; // 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; |
| } |
| |
| private Matrix_Delegate() { |
| reset(); |
| } |
| |
| private Matrix_Delegate(float[] values) { |
| System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); |
| } |
| |
| /** |
| * Adds the given transformation to the current Matrix |
| * <p/>This in effect does this = this*matrix |
| * @param matrix |
| */ |
| private void postTransform(float[] matrix) { |
| float[] tmp = new float[9]; |
| multiply(tmp, mValues, matrix); |
| mValues = tmp; |
| } |
| |
| /** |
| * Adds the given transformation to the current Matrix |
| * <p/>This in effect does this = matrix*this |
| * @param matrix |
| */ |
| private void preTransform(float[] matrix) { |
| float[] tmp = new float[9]; |
| multiply(tmp, matrix, mValues); |
| mValues = tmp; |
| } |
| |
| /** |
| * 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 |
| */ |
| |
| private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, |
| int pointCount) { |
| final int count = pointCount * 2; |
| |
| float[] tmpDest = dst; |
| boolean inPlace = dst == src; |
| if (inPlace) { |
| tmpDest = new float[dstIndex + count]; |
| } |
| |
| for (int i = 0 ; i < count ; i += 2) { |
| // just in case we are doing in place, we better put this in temp vars |
| float x = mValues[0] * src[i + srcIndex] + |
| mValues[1] * src[i + srcIndex + 1] + |
| mValues[2]; |
| float y = mValues[3] * src[i + srcIndex] + |
| mValues[4] * src[i + srcIndex + 1] + |
| mValues[5]; |
| |
| tmpDest[i + dstIndex] = x; |
| tmpDest[i + dstIndex + 1] = y; |
| } |
| |
| if (inPlace) { |
| System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| |
| private void mapPoints(float[] pts) { |
| mapPoints(pts, 0, pts, 0, pts.length >> 1); |
| } |
| |
| private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) { |
| if (hasPerspective()) { |
| // transform the (0,0) point |
| float[] origin = new float[] { 0.f, 0.f}; |
| mapPoints(origin); |
| |
| // translate the vector data as points |
| mapPoints(dst, dstIndex, src, srcIndex, ptCount); |
| |
| // then substract the transformed origin. |
| final int count = ptCount * 2; |
| for (int i = 0 ; i < count ; i += 2) { |
| dst[dstIndex + i] = dst[dstIndex + i] - origin[0]; |
| dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1]; |
| } |
| } else { |
| // make a copy of the matrix |
| Matrix_Delegate copy = new Matrix_Delegate(mValues); |
| |
| // remove the translation |
| setTranslate(copy.mValues, 0, 0); |
| |
| // map the content as points. |
| copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount); |
| } |
| } |
| |
| /** |
| * multiply two matrices and store them in a 3rd. |
| * <p/>This in effect does dest = a*b |
| * dest cannot be the same as a or b. |
| */ |
| /*package*/ static void multiply(float dest[], float[] a, float[] b) { |
| // first row |
| dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6]; |
| dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7]; |
| dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8]; |
| |
| // 2nd row |
| dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6]; |
| dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7]; |
| dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8]; |
| |
| // 3rd row |
| dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6]; |
| dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7]; |
| dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8]; |
| } |
| |
| /** |
| * Returns a matrix that represents a given translate |
| * @param dx |
| * @param dy |
| * @return |
| */ |
| /*package*/ static float[] getTranslate(float dx, float dy) { |
| return setTranslate(new float[9], dx, dy); |
| } |
| |
| /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) { |
| dest[0] = 1; |
| dest[1] = 0; |
| dest[2] = dx; |
| dest[3] = 0; |
| dest[4] = 1; |
| dest[5] = dy; |
| dest[6] = 0; |
| dest[7] = 0; |
| dest[8] = 1; |
| return dest; |
| } |
| |
| /*package*/ static float[] getScale(float sx, float sy) { |
| return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; |
| } |
| |
| /** |
| * Returns a matrix that represents the given scale info. |
| * @param sx |
| * @param sy |
| * @param px |
| * @param py |
| */ |
| /*package*/ static float[] getScale(float sx, float sy, float px, float py) { |
| float[] tmp = new float[9]; |
| float[] tmp2 = new float[9]; |
| |
| // TODO: do it in one pass |
| |
| // translate tmp so that the pivot is in 0,0 |
| setTranslate(tmp, -px, -py); |
| |
| // scale into tmp2 |
| multiply(tmp2, tmp, getScale(sx, sy)); |
| |
| // translate back the pivot back into tmp |
| multiply(tmp, tmp2, getTranslate(px, py)); |
| |
| return tmp; |
| } |
| |
| |
| /*package*/ static float[] getRotate(float degrees) { |
| double rad = Math.toRadians(degrees); |
| float sin = (float)Math.sin(rad); |
| float cos = (float)Math.cos(rad); |
| |
| return getRotate(sin, cos); |
| } |
| |
| /*package*/ static float[] getRotate(float sin, float cos) { |
| return setRotate(new float[9], sin, cos); |
| } |
| |
| /*package*/ static float[] setRotate(float[] dest, float degrees) { |
| double rad = Math.toRadians(degrees); |
| float sin = (float)Math.sin(rad); |
| float cos = (float)Math.cos(rad); |
| |
| return setRotate(dest, sin, cos); |
| } |
| |
| /*package*/ static float[] setRotate(float[] dest, float sin, float cos) { |
| dest[0] = cos; |
| dest[1] = -sin; |
| dest[2] = 0; |
| dest[3] = sin; |
| dest[4] = cos; |
| dest[5] = 0; |
| dest[6] = 0; |
| dest[7] = 0; |
| dest[8] = 1; |
| return dest; |
| } |
| |
| /*package*/ static float[] getRotate(float degrees, float px, float py) { |
| float[] tmp = new float[9]; |
| float[] tmp2 = new float[9]; |
| |
| // TODO: do it in one pass |
| |
| // translate so that the pivot is in 0,0 |
| setTranslate(tmp, -px, -py); |
| |
| // rotate into tmp2 |
| double rad = Math.toRadians(degrees); |
| float cos = (float)Math.cos(rad); |
| float sin = (float)Math.sin(rad); |
| multiply(tmp2, tmp, getRotate(sin, cos)); |
| |
| // translate back the pivot back into tmp |
| multiply(tmp, tmp2, getTranslate(px, py)); |
| |
| return tmp; |
| } |
| |
| /*package*/ static float[] getSkew(float kx, float ky) { |
| return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }; |
| } |
| |
| /*package*/ static float[] getSkew(float kx, float ky, float px, float py) { |
| float[] tmp = new float[9]; |
| float[] tmp2 = new float[9]; |
| |
| // TODO: do it in one pass |
| |
| // translate so that the pivot is in 0,0 |
| setTranslate(tmp, -px, -py); |
| |
| // skew into tmp2 |
| multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); |
| // translate back the pivot back into tmp |
| multiply(tmp, tmp2, getTranslate(px, py)); |
| |
| return tmp; |
| } |
| } |