Home » Android » android – How does Matrix.postScale( sx, sy, px, py) work?

# android – How does Matrix.postScale( sx, sy, px, py) work?

Questions:

Taig said:

When calling Matrix.postScale( sx, sy, px, py ); the matrix gets
scaled and also translated (depending on the given point x, y). That
predestines this method to be used for zooming into images because I
can easily focus one specific point.
The android doc describes the method like this:

``````Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
``````

At a first glance this seems ridiculous because M is supposed to be a
3×3-Matrix. Digging around I’ve found out that android uses a
4×4-Matrix for its computations (while only providing 3×3 on its API).
Since this code is written in C I’m having a hard time trying to
understand what is actually happening.

I saw the visual transform at Wolfram

My question is same as Taig

What I actually want to know: How can I apply this kind of scaling
(with a focused point) to the 3×3 Matrix that I can access within my
Java-code?

Who can give me a example and 2d-scaling formula with 4 parameters (sx, sy, px, py) that a 10-year-old-kid would understand?

Look more closely at the Matrix methods. You will see `getValue()` and `setValue()`. The docs say they work with a `float` array with 9 values. There are also a bunch of constants: `MSCALE_X`, `MSCALE_Y`, `MTRANS_X`, `MTRANS_Y` etc. etc. Those constants are indices into the `float[9]` array.

Since we are only working in 2 dimensions, the matrix would actually be a 2×2 matrix. But because this matrix supports affine transforms, the matrix is extended to become a 3×3 matrix. 3×3 = 9, which corresponds to the `float[9]` array. That is, essentially, your 3×3 matrix.

The actual guts of `Matrix` are written in C++ and accessed through JNI because the operations have to be fast fast fast fast fast. They even use a special non-standard floating point number format (“16.16”) that is optimized for calculation speed.

I don’t know where you are getting the information about a 4×4 array. Here’s a code snippet from the C++ JNI:

``````SkScalar         fMat[9];

void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) {
fMat[kMScaleX] = sx;
fMat[kMSkewX]  = 0;
fMat[kMTransX] = tx;

fMat[kMSkewY]  = 0;
fMat[kMScaleY] = sy;
fMat[kMTransY] = ty;

fMat[kMPersp0] = 0;
fMat[kMPersp1] = 0;
fMat[kMPersp2] = 1;

if (sx != 1 || sy != 1) {
}
if (tx || ty) {
}
}
``````

It’s a 3×3 matrix for an affine transform.

When you call `matrix.postScale()`, you are modifying scaleX, scaleY, transX, and transY. (The `pre..()` and `post...()` methods preserve any transform that was in your matrix to start with.) The `Matrix` applies the new transform like this:

```X' = X * scaleX + transX
Y' = Y * scaleY + transY
```

That’s the simplified version of the entire matrix multiplication. If I have a figure with point (2,2) and I scale it 2x, the new point will be (4,4). To move along the X or Y axis, I just add a constant.

Because `Matrix.postScale()` actually takes a focus point, the method internally adjusts transX & transY as though you are translating in, scaling, then translating back out. This makes the scaling appear as though the expansion/shrinking is centered around a point px, py.

```transX = (1 - scaleX) * px
transY = (1 - scaleY) * py
```

So for the focus point, I move the figure to (px,py) by adding px and py directly to the original x,y values. Then I do the scaling. But to undo the translation, I have to take into account that my original focus point is now scaled itself, so instead of subtracting px and py, I have to subtract scaleX * px and scaleY * py.

Skew or Shear is like scaling but with opposing axes:

```X' = Y * skewX
Y' = X * skewY
```

Since you’re scaling and translating without warping, skewX and skewY are set to zero. So they’re still used in the matrix multiplication, they just don’t affect the final outcome.

Rotation is done by adding in a little trig:

```theta = angle of rotation
scaleX = cos(theta)
skewX = -sin(theta)
skewY = sin(theta)
scaleY = cos(theta)
```

Then there is the `android.graphics.Camera` (as opposed to `android.hardware.Camera`) that can take a 2D plane and rotate/translate it in 3D space. This is where `MPERSP_0`, `MPERSP_1`, & `MPERSP_2` come into play. I’m not doing those equations; I’m a programmer, not a mathematician.

But I don’t need to be a mathematician. I don’t even need to know how `Matrix` does its calculations. I have been working on an ImageView subclass that supports pinch/zoom. So I use a `ScaleGestureDetector` to tell me when the user is zooming. It has methods `getScaleFactor()`, `getFocusX()` and `getFocusY()`. I plug those values into `matrix.postScale()`, and with my `ImageView` having a scale type set to `MATRIX`, I call `ImageView.setImageMatrix()` with my scaled matrix. Voilà, the image zooms exactly the way the user expects to see it based on their gestures.

So I don’t understand all the angst about grokking how `Matrix` works under the hood. Still, I hope something I wrote here gives you the answers you are looking for.