#Topic Matrix
#Alias Matrices
#Alias Matrix_Reference

#Subtopic Overview
    #Subtopic Subtopics
    #Populate
    ##
##

#Class SkMatrix

Matrix holds a 3x3 matrix for transforming coordinates. This allows mapping
Points and Vectors with translation, scaling, skewing, rotation, and
perspective.

Matrix elements are in row major order. Matrix does not have a constructor,
so it must be explicitly initialized. setIdentity initializes Matrix
so it has no effect. setTranslate, setScale, setSkew, setRotate, set9 and setAll
initializes all Matrix elements with the corresponding mapping.

Matrix includes a hidden variable that classifies the type of matrix to 
improve performance. Matrix is not thread safe unless getType is called first.

#Subtopic Constructors
#Populate
##

#Subtopic Operators
#Populate
##

#Subtopic Member_Functions
#Populate
##

# ------------------------------------------------------------------------------

#Method static SkMatrix SK_WARN_UNUSED_RESULT MakeScale(SkScalar sx, SkScalar sy)

#Line # constructs from scale in x and y ##
Sets Matrix to scale by (sx, sy). Returned matrix is:

#Code
#Literal
| sx  0  0 |
|  0 sy  0 |
|  0  0  1 |
##

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##

#Return  Matrix with scale ##

#Example
#Image 4
canvas->concat(SkMatrix::MakeScale(4, 3));
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso setScale postScale preScale

##

# ------------------------------------------------------------------------------

#Method static SkMatrix SK_WARN_UNUSED_RESULT MakeScale(SkScalar scale)

Sets Matrix to scale by (scale, scale). Returned matrix is:

#Code
#Literal
| scale   0   0 |
|   0   scale 0 |
|   0     0   1 |
##

#Param scale  horizontal and vertical scale factor ##

#Return  Matrix with scale ##

#Example
#Image 4
canvas->concat(SkMatrix::MakeScale(4));
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso setScale postScale preScale

##

# ------------------------------------------------------------------------------

#Method static SkMatrix SK_WARN_UNUSED_RESULT MakeTrans(SkScalar dx, SkScalar dy)

#Line # constructs from translate in x and y ##
Sets Matrix to translate by (dx, dy). Returned matrix is:

#Code
#Literal
| 1 0 dx |
| 0 1 dy |
| 0 0  1 |
##

#Param dx  horizontal translation ##
#Param dy  vertical translation ##

#Return  Matrix with translation ##

#Example
#Image 4
SkMatrix matrix = SkMatrix::MakeTrans(64, 48);
for (int i = 0; i < 4; ++i) {
    canvas->drawBitmap(source, 0, 0);
    canvas->concat(matrix);
}
##

#SeeAlso setTranslate postTranslate preTranslate

##

# ------------------------------------------------------------------------------

#Method static SkMatrix SK_WARN_UNUSED_RESULT MakeAll(SkScalar scaleX, SkScalar skewX,  SkScalar transX,
                                                      SkScalar skewY,  SkScalar scaleY, SkScalar transY,
                                                      SkScalar pers0, SkScalar pers1, SkScalar pers2)
#Line # constructs all nine values ##


Sets Matrix to:

#Code
#Literal
| scaleX  skewX transX |
|  skewY scaleY transY |
|  pers0  pers1  pers2 |
##

#Param scaleX  horizontal scale factor ##
#Param skewX   horizontal skew factor ##
#Param transX  horizontal translation ##
#Param skewY   vertical skew factor ##
#Param scaleY  vertical scale factor ##
#Param transY  vertical translation ##
#Param pers0   input x perspective factor ##
#Param pers1   input y perspective factor ##
#Param pers2   perspective scale factor ##

#Return  Matrix constructed from parameters ##

#Example
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(64);
    for (SkScalar sx : { -1, 1 } ) {
        for (SkScalar sy : { -1, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            SkMatrix m = SkMatrix::MakeAll(sx, 1, 128,    0, sy, 128,   0, 0, 1);
            canvas->concat(m);
            canvas->drawString("K", 0, 0, p);
        }
    }
##

#SeeAlso setAll set9 postConcat preConcat

##


# ------------------------------------------------------------------------------

#Enum TypeMask

#Code
    enum TypeMask {
        kIdentity_Mask = 0,
        kTranslate_Mask = 0x01,
        kScale_Mask = 0x02,
        kAffine_Mask = 0x04,
        kPerspective_Mask = 0x08,
    };
##

Enum of bit fields for mask returned by getType.
Used to identify the complexity of Matrix, to optimize performance.

#Const kIdentity_Mask 0
all bits clear if Matrix is identity
##
#Const kTranslate_Mask 1
set if Matrix has translation
##
#Const kScale_Mask 2
set if Matrix has x or y scale
##
#Const kAffine_Mask 4
set if Matrix skews or rotates
##
#Const kPerspective_Mask 8
set if Matrix has perspective
##

#Example
    auto debugster = [](const char* prefix, const SkMatrix& matrix) -> void {
        SkString typeMask;
        typeMask += SkMatrix::kIdentity_Mask == matrix.getType() ? "kIdentity_Mask " : "";
        typeMask += SkMatrix::kTranslate_Mask & matrix.getType() ? "kTranslate_Mask " : "";
        typeMask += SkMatrix::kScale_Mask & matrix.getType() ? "kScale_Mask " : "";
        typeMask += SkMatrix::kAffine_Mask & matrix.getType() ? "kAffine_Mask " : "";
        typeMask += SkMatrix::kPerspective_Mask & matrix.getType() ? "kPerspective_Mask" : "";
        SkDebugf("after %s: %s\n", prefix, typeMask.c_str());
    };
SkMatrix matrix;
matrix.reset();
debugster("reset", matrix);
matrix.postTranslate(1, 0);
debugster("postTranslate", matrix);
matrix.postScale(2, 1);
debugster("postScale", matrix);
matrix.postRotate(45);
debugster("postScale", matrix);
SkPoint polys[][4] = {{{0, 0}, {0, 1}, {1, 1}, {1, 0}}, {{0, 0}, {0, 1}, {2, 1}, {1, 0}}};
matrix.setPolyToPoly(polys[0], polys[1], 4);
debugster("setPolyToPoly", matrix);
#StdOut
after reset: kIdentity_Mask 
after postTranslate: kTranslate_Mask 
after postScale: kTranslate_Mask kScale_Mask 
after postScale: kTranslate_Mask kScale_Mask kAffine_Mask 
after setPolyToPoly: kTranslate_Mask kScale_Mask kAffine_Mask kPerspective_Mask
##
##

#SeeAlso getType

##

# ------------------------------------------------------------------------------

#Method TypeMask getType() const

#Line # returns transform complexity ##
Returns a bit field describing the transformations the matrix may
perform. The bit field is computed conservatively, so it may include
false positives. For example, when kPerspective_Mask is set, all
other bits are set.

#Return  kIdentity_Mask, or combinations of: kTranslate_Mask, kScale_Mask,
         kAffine_Mask, kPerspective_Mask 
##

#Example
SkMatrix matrix;
matrix.setAll(1, 0, 0,   0, 1, 0,    0, 0, 1);
SkDebugf("identity flags hex: %0x decimal: %d\n", matrix.getType(), matrix.getType());
matrix.setAll(1, 0, 0,   0, 1, 0,    0, 0, .5f);
SkDebugf("set all  flags hex: %0x decimal: %d\n", matrix.getType(), matrix.getType());
#StdOut
identity flags hex: 0 decimal: 0
set all  flags hex: f decimal: 15
##
##

#SeeAlso TypeMask

##

# ------------------------------------------------------------------------------

#Method bool isIdentity() const

#Line # returns if matrix equals the identity Matrix  ##
Returns true if Matrix is identity.  Identity matrix is:

#Code
#Literal
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
##

#Return  true if Matrix has no effect ##

#Example
SkMatrix matrix;
matrix.setAll(1, 0, 0,   0, 1, 0,    0, 0, 1);
SkDebugf("is identity: %s\n", matrix.isIdentity() ? "true" : "false");
matrix.setAll(1, 0, 0,   0, 1, 0,    0, 0, 2);
SkDebugf("is identity: %s\n", matrix.isIdentity() ? "true" : "false");
#StdOut
is identity: true
is identity: false
##
##

#SeeAlso reset() setIdentity getType

##

# ------------------------------------------------------------------------------

#Method bool isScaleTranslate() const

#Line # returns if transform is limited to scale and translate ##
Returns true if Matrix at most scales and translates. Matrix may be identity,
contain only scale elements, only translate elements, or both. Matrix form is:

#Code
#Literal
| scale-x    0    translate-x |
|    0    scale-y translate-y |
|    0       0         1      |
##

#Return  true if Matrix is identity; or scales, translates, or both ##

#Example
SkMatrix matrix;
for (SkScalar scaleX : { 1, 2 } ) {
    for (SkScalar translateX : { 0, 20 } ) {
        matrix.setAll(scaleX, 0, translateX,   0, 1, 0,    0, 0, 1);
        SkDebugf("is scale-translate: %s\n", matrix.isScaleTranslate() ? "true" : "false");
    }
}
#StdOut
is scale-translate: true
is scale-translate: true
is scale-translate: true
is scale-translate: true
##
##

#SeeAlso setScale isTranslate setTranslate getType

##

# ------------------------------------------------------------------------------

#Method bool isTranslate() const

#Line # returns if transform is limited to translate ##
Returns true if Matrix is identity, or translates. Matrix form is:

#Code
#Literal
| 1 0 translate-x |
| 0 1 translate-y |
| 0 0      1      |
##

#Return  true if Matrix is identity, or translates ##

#Example
SkMatrix matrix;
for (SkScalar scaleX : { 1, 2 } ) {
    for (SkScalar translateX : { 0, 20 } ) {
        matrix.setAll(scaleX, 0, translateX,   0, 1, 0,    0, 0, 1);
        SkDebugf("is translate: %s\n", matrix.isTranslate() ? "true" : "false");
    }
}
#StdOut
is translate: true
is translate: true
is translate: false
is translate: false
##
##

#SeeAlso setTranslate getType

##

# ------------------------------------------------------------------------------

#Method bool rectStaysRect() const

#Line # returns if mapped Rect can be represented by another Rect ##
Returns true Matrix maps Rect to another Rect. If true, Matrix is identity,
or scales, or rotates a multiple of 90 degrees, or mirrors in x or y. In all
cases, Matrix may also have translation. Matrix form is either:

#Code
#Literal
| scale-x    0    translate-x |
|    0    scale-y translate-y |
|    0       0         1      |
##

or

#Code
#Literal
|    0     rotate-x translate-x |
| rotate-y    0     translate-y |
|    0        0          1      |
##

for non-zero values of scale-x, scale-y, rotate-x, and rotate-y.

Also called preservesAxisAlignment; use the one that provides better inline
documentation.

#Return  true if Matrix maps one Rect into another  ##

#Example
SkMatrix matrix;
for (SkScalar angle: { 0, 90, 180, 270 } ) {
    matrix.setRotate(angle);
    SkDebugf("rectStaysRect: %s\n", matrix.rectStaysRect() ? "true" : "false");
}
#StdOut
rectStaysRect: true
rectStaysRect: true
rectStaysRect: true
rectStaysRect: true
##
##

#SeeAlso preservesAxisAlignment preservesRightAngles

##

# ------------------------------------------------------------------------------

#Method bool preservesAxisAlignment() const

#Line # returns if mapping restricts to 90 degree multiples and mirroring ##

Returns true Matrix maps Rect to another Rect. If true, Matrix is identity,
or scales, or rotates a multiple of 90 degrees, or mirrors in x or y. In all
cases, Matrix may also have translation. Matrix form is either:

#Code
#Literal
| scale-x    0    translate-x |
|    0    scale-y translate-y |
|    0       0         1      |
##

or

#Code
#Literal
|    0     rotate-x translate-x |
| rotate-y    0     translate-y |
|    0        0          1      |
##

for non-zero values of scale-x, scale-y, rotate-x, and rotate-y.

Also called rectStaysRect; use the one that provides better inline
documentation.

#Return  true if Matrix maps one Rect into another  ##

#Example
SkMatrix matrix;
for (SkScalar angle: { 0, 90, 180, 270 } ) {
    matrix.setRotate(angle);
    SkDebugf("preservesAxisAlignment: %s\n", matrix.preservesAxisAlignment() ? "true" : "false");
}
#StdOut
preservesAxisAlignment: true
preservesAxisAlignment: true
preservesAxisAlignment: true
preservesAxisAlignment: true
##
##

#SeeAlso rectStaysRect preservesRightAngles

##

# ------------------------------------------------------------------------------

#Method bool hasPerspective() const

#Line # returns if transform includes perspective ##
Returns true if the matrix contains perspective elements. Matrix form is:

#Code
#Literal
|       --            --              --          |
|       --            --              --          |
| perspective-x  perspective-y  perspective-scale |
##

where perspective-x or perspective-y is non-zero, or perspective-scale is 
not one. All other elements may have any value.

#Return  true if Matrix is in most general form ##

#Example
#Image 4
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
canvas->concat(matrix);
SkString string;
string.printf("hasPerspective %s", matrix.hasPerspective() ? "true" : "false");
canvas->drawBitmap(source, 0, 0);
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(48);
canvas->drawString(string, 0, source.bounds().height() + 48, paint);
##

#SeeAlso setAll set9 MakeAll

##

# ------------------------------------------------------------------------------

#Method bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const

#Line # returns if transform is limited to square scale and rotation ##
Returns true if Matrix contains only translation, rotation, reflection, and
uniform scale.
Returns false if Matrix contains different scales, skewing, perspective, or
degenerate forms that collapse to a line or point.

Describes that the Matrix makes rendering with and without the matrix are 
visually alike; a transformed circle remains a circle. Mathematically, this is
referred to as similarity of a Euclidean_Space, or a similarity transformation.

Preserves right angles, keeping the arms of the angle equal lengths.

#Param tol  to be deprecated ##

#Return  true if Matrix only rotates, uniformly scales, translates ##

#Example
#Description
String is drawn four times through but only two are visible. Drawing the pair
with isSimilarity false reveals the pair not visible through the matrix.
##
    SkPaint p;
    p.setAntiAlias(true);
    SkMatrix m;
    int below = 175;
    for (SkScalar sx : { -1, 1 } ) {
        for (SkScalar sy : { -1, 1 } ) {
            m.setAll(sx, 1, 128,    1, sy, 32,   0, 0, 1);
            bool isSimilarity = m.isSimilarity();
            SkString str;
            str.printf("sx: %g sy: %g sim: %s", sx, sy, isSimilarity ? "true" : "false");
            {
                SkAutoCanvasRestore autoRestore(canvas, true);
                canvas->concat(m);
                canvas->drawString(str, 0, 0, p);    
            }
            if (!isSimilarity) {
                canvas->drawString(str, 40, below, p);
                below += 20;
            }
        }
    }
##

#SeeAlso isScaleTranslate preservesRightAngles rectStaysRect isFixedStepInX

##

# ------------------------------------------------------------------------------

#Method bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const

#Line # returns if mapped 90 angle remains 90 degrees ##
Returns true if Matrix contains only translation, rotation, reflection, and
scale. Scale may differ along rotated axes.
Returns false if Matrix skewing, perspective, or degenerate forms that collapse 
to a line or point.

Preserves right angles, but not requiring that the arms of the angle
retain equal lengths.

#Param tol  to be deprecated ##

#Return  true if Matrix only rotates, scales, translates ##

#Example
#Height 128
#Description
Equal scale is both similar and preserves right angles.
Unequal scale is not similar but preserves right angles.
Skews are not similar and do not preserve right angles.
##
SkPaint p;
p.setAntiAlias(true);
SkMatrix m;
int pos = 0;
for (SkScalar sx : { 1, 2 } ) {
    for (SkScalar kx : { 0, 1 } ) {
        m.setAll(sx, kx, 16,    0, 1, 32,   0, 0, 1);
        bool isSimilarity = m.isSimilarity();
        bool preservesRightAngles = m.preservesRightAngles();
        SkString str;
        str.printf("sx: %g kx: %g %s %s", sx, kx, isSimilarity ? "sim" : "",
                    preservesRightAngles ? "right" : "");
        SkAutoCanvasRestore autoRestore(canvas, true);
        canvas->concat(m);
        canvas->drawString(str, 0, pos, p);    
        pos += 20;
    }
}
##

#SeeAlso isScaleTranslate isSimilarity rectStaysRect isFixedStepInX

##

# ------------------------------------------------------------------------------

#Enum

#Code
    enum {
        kMScaleX,
        kMSkewX,
        kMTransX,
        kMSkewY,
        kMScaleY,
        kMTransY,
        kMPersp0,
        kMPersp1,
        kMPersp2,
    };
##

Matrix organizes its values in row order. These members correspond to
each value in Matrix.

#Const kMScaleX 0
horizontal scale factor
##
#Const kMSkewX 1
horizontal skew factor
##
#Const kMTransX 2
horizontal translation
##
#Const kMSkewY 3
vertical skew factor
##
#Const kMScaleY 4
vertical scale factor
##
#Const kMTransY 5
vertical translation
##
#Const kMPersp0 6
input x perspective factor
##
#Const kMPersp1 7
input y perspective factor
##
#Const kMPersp2 8
perspective bias
##

#Example
SkPaint black;
black.setAntiAlias(true);
black.setTextSize(48);
SkPaint gray = black;
gray.setColor(0xFF9f9f9f);
SkScalar offset[] = { 1.5f, 1.5f, 20,   1.5f, 1.5f, 20,   .03f, .01f, 2 };
for (int i : { SkMatrix::kMScaleX, SkMatrix::kMSkewX,  SkMatrix::kMTransX,
               SkMatrix::kMSkewY,  SkMatrix::kMScaleY, SkMatrix::kMTransY,
               SkMatrix::kMPersp0, SkMatrix::kMPersp1, SkMatrix::kMPersp2 } ) {
    SkMatrix m;
    m.setIdentity();
    m.set(i, offset[i]);
    SkAutoCanvasRestore autoRestore(canvas, true);
    canvas->translate(22 + (i % 3) * 88, 44 + (i / 3) * 88);
    canvas->drawString("&", 0, 0, gray);
    canvas->concat(m);
    canvas->drawString("&", 0, 0, black);
}
##

#SeeAlso get() set()

##

# ------------------------------------------------------------------------------

#Enum

#Code
    enum {
        kAScaleX,
        kASkewY,
        kASkewX,
        kAScaleY,
        kATransX,
        kATransY,
    };
##

Affine arrays are in column major order to match the matrix used by
PDF and XPS.

#Const kAScaleX 0
horizontal scale factor
##
#Const kASkewY 1
vertical skew factor
##
#Const kASkewX 2
horizontal skew factor
##
#Const kAScaleY 3
vertical scale factor
##
#Const kATransX 4
horizontal translation
##
#Const kATransY 5
vertical translation
##

#NoExample
##

#SeeAlso SetAffineIdentity asAffine setAffine

##

# ------------------------------------------------------------------------------

#Method SkScalar operator[](int index)_const

#Line # returns Matrix value ##
Returns one matrix value. Asserts if index is out of range and SK_DEBUG is
defined.

#Param index  one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
                      kMPersp0, kMPersp1, kMPersp2
##

#Return  value corresponding to index ##

#Example
SkMatrix matrix;
matrix.setScale(42, 24);
SkDebugf("matrix[SkMatrix::kMScaleX] %c= 42\n", matrix[SkMatrix::kMScaleX] == 42 ? '=' : '!');
SkDebugf("matrix[SkMatrix::kMScaleY] %c= 24\n", matrix[SkMatrix::kMScaleY] == 24 ? '=' : '!');
#StdOut
matrix[SkMatrix::kMScaleX] == 42
matrix[SkMatrix::kMScaleY] == 24
##
##

#SeeAlso get set

##

# ------------------------------------------------------------------------------

#Method SkScalar get(int index) const

#Line # returns one of nine Matrix values ##
Returns one matrix value. Asserts if index is out of range and SK_DEBUG is
defined.

#Param index  one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
                      kMPersp0, kMPersp1, kMPersp2
##

#Return  value corresponding to index ##

#Example
SkMatrix matrix;
matrix.setSkew(42, 24);
SkDebugf("matrix.get(SkMatrix::kMSkewX) %c= 42\n",
         matrix.get(SkMatrix::kMSkewX) == 42 ? '=' : '!');
SkDebugf("matrix.get(SkMatrix::kMSkewY) %c= 24\n",
         matrix.get(SkMatrix::kMSkewY) == 24 ? '=' : '!');
#StdOut
matrix.get(SkMatrix::kMSkewX) == 42
matrix.get(SkMatrix::kMSkewY) == 24
##
##

#SeeAlso operator[](int index) set

##

# ------------------------------------------------------------------------------

#Method SkScalar getScaleX() const

#Line # returns horizontal scale factor ##
Returns scale factor multiplied by x input, contributing to x output.
With mapPoints, scales Points along the x-axis.

#Return  horizontal scale factor ##

#Example
SkMatrix matrix;
matrix.setScale(42, 24);
SkDebugf("matrix.getScaleX() %c= 42\n", matrix.getScaleX() == 42 ? '=' : '!');
#StdOut
matrix.getScaleX() == 42
##
##

#SeeAlso get getScaleY setScaleX setScale

##

# ------------------------------------------------------------------------------

#Method SkScalar getScaleY() const

#Line # returns vertical scale factor ##
Returns scale factor multiplied by y input, contributing to y output.
With mapPoints, scales Points along the y-axis.

#Return  vertical scale factor ##

#Example
SkMatrix matrix;
matrix.setScale(42, 24);
SkDebugf("matrix.getScaleY() %c= 24\n", matrix.getScaleY() == 24 ? '=' : '!');
#StdOut
matrix.getScaleY() == 24
##
##

#SeeAlso get getScaleX setScaleY setScale

##

# ------------------------------------------------------------------------------

#Method SkScalar getSkewY() const

#Line # returns vertical skew factor ##
Returns scale factor multiplied by x input, contributing to y output.
With mapPoints, skews Points along the y-axis.
Skew x and y together can rotate Points.

#Return  vertical skew factor ##

#Example
SkMatrix matrix;
matrix.setSkew(42, 24);
SkDebugf("matrix.getSkewY() %c= 24\n", matrix.getSkewY() == 24 ? '=' : '!');
#StdOut
matrix.getSkewY() == 24
##
##

#SeeAlso get getSkewX setSkewY setSkew

##

# ------------------------------------------------------------------------------

#Method SkScalar getSkewX() const

#Line # returns horizontal skew factor ##
Returns scale factor multiplied by y input, contributing to x output.
With mapPoints, skews Points along the x-axis.
Skew x and y together can rotate Points.

#Return  horizontal scale factor ##

#Example
SkMatrix matrix;
matrix.setSkew(42, 24);
SkDebugf("matrix.getSkewX() %c= 42\n", matrix.getSkewX() == 42 ? '=' : '!');
#StdOut
matrix.getSkewX() == 42
##
##

#SeeAlso get getSkewY setSkewX setSkew

##

# ------------------------------------------------------------------------------

#Method SkScalar getTranslateX() const

#Line # returns horizontal translation ##
Returns translation contributing to x output.
With mapPoints, moves Points along the x-axis.

#Return  horizontal translation factor ##

#Example
SkMatrix matrix;
matrix.setTranslate(42, 24);
SkDebugf("matrix.getTranslateX() %c= 42\n", matrix.getTranslateX() == 42 ? '=' : '!');
#StdOut
matrix.getTranslateX() == 42
##
##

#SeeAlso get getTranslateY setTranslateX setTranslate

##

# ------------------------------------------------------------------------------

#Method SkScalar getTranslateY() const

#Line # returns vertical translation ##
Returns translation contributing to y output.
With mapPoints, moves Points along the y-axis.

#Return  vertical translation factor ##

#Example
SkMatrix matrix;
matrix.setTranslate(42, 24);
SkDebugf("matrix.getTranslateY() %c= 24\n", matrix.getTranslateY() == 24 ? '=' : '!');
#StdOut
matrix.getTranslateY() == 24
##
##

#SeeAlso get getTranslateX setTranslateY setTranslate

##

# ------------------------------------------------------------------------------

#Method SkScalar getPerspX() const

#Line # returns input x perspective factor ##
Returns factor scaling input x relative to input y. 

#Return  input x perspective factor ##

#Example
    SkMatrix m;
    m.setIdentity();
    m.set(SkMatrix::kMPersp0, -0.004f);
    SkAutoCanvasRestore autoRestore(canvas, true);
    canvas->translate(22, 144);
    SkPaint black;
    black.setAntiAlias(true);
    black.setTextSize(24);
    SkPaint gray = black;
    gray.setColor(0xFF9f9f9f);
    SkString string;
    string.appendScalar(m.getPerspX());
    canvas->drawString(string, 0, -72, gray);
    canvas->concat(m);
    canvas->drawString(string, 0, 0, black);
##

#SeeAlso kMPersp0 getPerspY

##

# ------------------------------------------------------------------------------

#Method SkScalar getPerspY() const

#Line # returns input y perspective factor ##

Returns factor scaling input y relative to input x. 

#Return  input y perspective factor ##

#Example
    SkMatrix m;
    m.setIdentity();
    m.set(SkMatrix::kMPersp1, -0.004f);
    SkAutoCanvasRestore autoRestore(canvas, true);
    canvas->translate(22, 144);
    SkPaint black;
    black.setAntiAlias(true);
    black.setTextSize(24);
    SkPaint gray = black;
    gray.setColor(0xFF9f9f9f);
    SkString string;
    string.appendScalar(m.getPerspY());
    canvas->drawString(string, 0, -72, gray);
    canvas->concat(m);
    canvas->drawString(string, 0, 0, black);
##

#SeeAlso kMPersp1 getPerspX

##

# ------------------------------------------------------------------------------

#Method SkScalar& operator[](int index)

#Line # returns writable reference to Matrix value ##
Returns writable Matrix value. Asserts if index is out of range and SK_DEBUG is
defined. Clears internal cache anticipating that caller will change Matrix value.

Next call to read Matrix state may recompute cache; subsequent writes to Matrix
value must be followed by dirtyMatrixTypeCache.

#Param index  one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
                      kMPersp0, kMPersp1, kMPersp2
##

#Return  writable value corresponding to index ##

#Example
SkMatrix matrix;
matrix.setIdentity();
SkDebugf("with identity matrix: x = %g\n", matrix.mapXY(24, 42).fX);
SkScalar& skewRef = matrix[SkMatrix::kMSkewX];
skewRef = 0;
SkDebugf("after skew x mod:     x = %g\n", matrix.mapXY(24, 42).fX);
skewRef = 1;
SkDebugf("after 2nd skew x mod: x = %g\n", matrix.mapXY(24, 42).fX);
matrix.dirtyMatrixTypeCache();
SkDebugf("after dirty cache:    x = %g\n", matrix.mapXY(24, 42).fX);
#StdOut
with identity matrix: x = 24
after skew x mod:     x = 24
after 2nd skew x mod: x = 24
after dirty cache:    x = 66
##
##

#SeeAlso get dirtyMatrixTypeCache set

##

# ------------------------------------------------------------------------------

#Method void set(int index, SkScalar value)

#Line # sets one value ##
Sets Matrix value. Asserts if index is out of range and SK_DEBUG is
defined. Safer than operator[]; internal cache is always maintained.

#Param index  one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
                      kMPersp0, kMPersp1, kMPersp2
##
#Param value  Scalar to store in Matrix ##

#Example
SkMatrix matrix;
matrix.setIdentity();
SkDebugf("with identity matrix: x = %g\n", matrix.mapXY(24, 42).fX);
matrix.set(SkMatrix::kMSkewX, 0);
SkDebugf("after skew x mod:     x = %g\n", matrix.mapXY(24, 42).fX);
matrix.set(SkMatrix::kMSkewX, 1);
SkDebugf("after 2nd skew x mod: x = %g\n", matrix.mapXY(24, 42).fX);
#StdOut
with identity matrix: x = 24
after skew x mod:     x = 24
after 2nd skew x mod: x = 66
##
##

#SeeAlso operator[] get

#Method ##

# ------------------------------------------------------------------------------

#Method void setScaleX(SkScalar v)

#Line # sets horizontal scale factor ##
Sets horizontal scale factor. 

#Param v  horizontal scale factor to store ##

#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 12, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setScaleX(3);
canvas->concat(matrix);
canvas->drawString("x scale", 0, 48, paint);
##

#SeeAlso set setScale setScaleY

#Method ##

# ------------------------------------------------------------------------------

#Method void setScaleY(SkScalar v)

#Line # sets vertical scale factor ##
Sets vertical scale factor. 

#Param v  vertical scale factor to store ##

#Example
#Height 192
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 12, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setScaleY(3);
canvas->concat(matrix);
canvas->drawString("y scale", 12, 48, paint);
##

#SeeAlso set setScale setScaleX

#Method ##

# ------------------------------------------------------------------------------

#Method void setSkewY(SkScalar v)

#Line # sets vertical skew factor ##
Sets vertical skew factor. 

#Param v  vertical skew factor to store ##

#Example
#Height 96
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 12, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setSkewY(.3f);
canvas->concat(matrix);
canvas->drawString("y skew", 12, 48, paint);
##

#SeeAlso set setSkew setSkewX

#Method ##

# ------------------------------------------------------------------------------

#Method void setSkewX(SkScalar v)

#Line # sets horizontal skew factor ##
Sets horizontal skew factor. 

#Param v  horizontal skew factor to store ##

#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 12, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setSkewX(-.7f);
canvas->concat(matrix);
canvas->drawString("x skew", 36, 48, paint);
##

#SeeAlso set setSkew setSkewX

#Method ##

# ------------------------------------------------------------------------------

#Method void setTranslateX(SkScalar v)

#Line # sets horizontal translation ##
Sets horizontal translation.

#Param v  horizontal translation to store ##

#Example
#Height 48
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 8, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setTranslateX(96);
canvas->concat(matrix);
canvas->drawString("x translate", 8, 24, paint);
##

#SeeAlso set setTranslate setTranslateY

#Method ##

# ------------------------------------------------------------------------------

#Method void setTranslateY(SkScalar v)

#Line # sets vertical translation ##
Sets vertical translation.

#Param v  vertical translation to store ##

#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 8, 24, paint);
SkMatrix matrix;
matrix.setIdentity();
matrix.setTranslateY(24);
canvas->concat(matrix);
canvas->drawString("y translate", 8, 24, paint);
##

#SeeAlso set setTranslate setTranslateX

#Method ##

# ------------------------------------------------------------------------------

#Method void setPerspX(SkScalar v)

#Line # sets input x perspective factor ##
Sets input x perspective factor, which causes mapXY to vary input x inversely
proportional to input y.

#Param v  perspective factor ##

#Example
#Image 4
for (SkScalar perspX : { -.003f, 0.f, .003f, .012f } ) {
    SkMatrix matrix;
    matrix.setIdentity();
    matrix.setPerspX(perspX);
    canvas->save();
    canvas->concat(matrix);
    canvas->drawBitmap(source, 0, 0);
    canvas->restore();
    canvas->translate(64, 64);
}
##

#SeeAlso getPerspX set setAll set9 MakeAll

#Method ##

# ------------------------------------------------------------------------------

#Method void setPerspY(SkScalar v)

#Line # sets input y perspective factor ##
Sets input y perspective factor, which causes mapXY to vary input y inversely
proportional to input x.

#Param v  perspective factor ##

#Example
#Image 4
for (SkScalar perspX : { -.003f, 0.f, .003f, .012f } ) {
    SkMatrix matrix;
    matrix.setIdentity();
    matrix.setPerspY(perspX);
    canvas->save();
    canvas->concat(matrix);
    canvas->drawBitmap(source, 0, 0);
    canvas->restore();
    canvas->translate(64, 64);
}
##

#SeeAlso getPerspY set setAll set9 MakeAll

#Method ##

# ------------------------------------------------------------------------------

#Method void setAll(SkScalar scaleX, SkScalar skewX,  SkScalar transX,
                SkScalar skewY,  SkScalar scaleY, SkScalar transY,
                SkScalar persp0, SkScalar persp1, SkScalar persp2)
#Line # sets all values from parameters ##

Sets all values from parameters. Sets matrix to:

#Code
#Literal
| scaleX  skewX transX |
|  skewY scaleY transY |
| persp0 persp1 persp2 |
##

#Param scaleX  horizontal scale factor to store ##
#Param skewX  horizontal skew factor to store ##
#Param transX  horizontal translation to store ##
#Param skewY  vertical skew factor to store ##
#Param scaleY  vertical scale factor to store ##
#Param transY  vertical translation to store ##
#Param persp0  input x perspective factor to store ##
#Param persp1  input y perspective factor to store ##
#Param persp2  perspective scale factor to store ##

#Example
#Height 128
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(64);
    SkMatrix m;
    for (SkScalar sx : { -1, 1 } ) {
        for (SkScalar sy : { -1, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            m.setAll(sx, 1, 128,    0, sy, 64,   0, 0, 1);
            canvas->concat(m);
            canvas->drawString("K", 0, 0, p);
        }
    }
##

#SeeAlso set9 MakeAll

#Method ##

# ------------------------------------------------------------------------------

#Method void get9(SkScalar buffer[9]) const

#Line # returns all nine Matrix values ##
Copies nine Scalar values contained by Matrix into buffer, in member value
ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
kMPersp0, kMPersp1, kMPersp2.

#Param buffer  storage for nine Scalar values ##

#Example
SkMatrix matrix = SkMatrix::MakeRectToRect({0, 0, 1, 1}, {3, 4, 7, 9},
                                           SkMatrix::kFill_ScaleToFit);
SkScalar b[9];
matrix.get9(b);
SkDebugf("{%g, %g, %g},\n{%g, %g, %g},\n{%g, %g, %g}\n", b[0], b[1], b[2], 
         b[3], b[4], b[5], b[6], b[7], b[8]);
#StdOut
{4, 0, 3},
{0, 5, 4},
{0, 0, 1}
##    
##

#SeeAlso set9

#Method ##

# ------------------------------------------------------------------------------

#Method void set9(const SkScalar buffer[9])

#Line # sets all values from Scalar array ##
Sets Matrix to nine Scalar values in buffer, in member value ascending order:
kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1,
kMPersp2.

Sets matrix to:

#Code
#Literal
| buffer[0] buffer[1] buffer[2] |
| buffer[3] buffer[4] buffer[5] |
| buffer[6] buffer[7] buffer[8] |
##

In the future, set9 followed by get9 may not return the same values. Since Matrix
maps non-homogeneous coordinates, scaling all nine values produces an equivalent
transformation, possibly improving precision.

#Param buffer  nine Scalar values ##

#Example
#Image 4
SkMatrix m;
SkScalar buffer[9] = {4, 0, 3,    0, 5, 4,     0, 0, 1};
m.set9(buffer);
canvas->concat(m);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso setAll get9 MakeAll

#Method ##

# ------------------------------------------------------------------------------

#Method void reset()

#Line # sets Matrix to identity ##
Sets Matrix to identity; which has no effect on mapped Points. Sets Matrix to:

#Code
#Literal
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
##

Also called setIdentity(); use the one that provides better inline
documentation.

#Example
SkMatrix m;
m.reset();
SkDebugf("m.isIdentity(): %s\n", m.isIdentity() ? "true" : "false");
#StdOut
m.isIdentity(): true
##
##

#SeeAlso isIdentity setIdentity

#Method ##

# ------------------------------------------------------------------------------

#Method void setIdentity()

#Line # sets Matrix to identity ##
Sets Matrix to identity; which has no effect on mapped Points. Sets Matrix to:

#Code
#Literal
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
##

Also called reset(); use the one that provides better inline
documentation.

#Example
SkMatrix m;
m.setIdentity();
SkDebugf("m.isIdentity(): %s\n", m.isIdentity() ? "true" : "false");
#StdOut
m.isIdentity(): true
##
##

#SeeAlso isIdentity reset

#Method ##

# ------------------------------------------------------------------------------

#Method void setTranslate(SkScalar dx, SkScalar dy)

#Line # sets to translate in x and y ##
Sets Matrix to translate by (dx, dy).

#Param dx  horizontal translation ##
#Param dy  vertical translation ##

#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 8, 24, paint);
SkMatrix matrix;
matrix.setTranslate(96, 24);
canvas->concat(matrix);
canvas->drawString("translate", 8, 24, paint);
##

#SeeAlso setTranslateX setTranslateY

#Method ##

# ------------------------------------------------------------------------------

#Method void setTranslate(const SkVector& v)

Sets Matrix to translate by (v.fX, v.fY).

#Param v  Vector containing horizontal and vertical translation ##

#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(24);
canvas->drawString("normal", 8, 24, paint);
SkMatrix matrix;
matrix.setTranslate({96, 24});
canvas->concat(matrix);
canvas->drawString("translate", 8, 24, paint);
##

#SeeAlso setTranslateX setTranslateY MakeTrans

#Method ##

# ------------------------------------------------------------------------------

#Method void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)

#Line # sets to scale about a point ##
Sets Matrix to scale by sx and sy, about a pivot point at (px, py).
The pivot point is unchanged when mapped with Matrix.

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Height 128
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(64);
    SkMatrix m;
    for (SkScalar sx : { -1, 1 } ) {
        for (SkScalar sy : { -1, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            m.setScale(sx, sy, 128, 64);
            canvas->concat(m);
            canvas->drawString("%", 128, 64, p);
        }
    }
##

#SeeAlso setScaleX setScaleY MakeScale preScale postScale

#Method ##

# ------------------------------------------------------------------------------

#Method void setScale(SkScalar sx, SkScalar sy)

Sets Matrix to scale by sx and sy about at pivot point at (0, 0).

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##

#Example
#Height 128
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(64);
    SkMatrix m;
    for (SkScalar sx : { -1, 1 } ) {
        for (SkScalar sy : { -1, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            m.setScale(sx, sy);
            m.postTranslate(128, 64);
            canvas->concat(m);
            canvas->drawString("@", 0, 0, p);
        }
    }
##

#SeeAlso setScaleX setScaleY MakeScale preScale postScale

#Method ##

# ------------------------------------------------------------------------------

#Method void setRotate(SkScalar degrees, SkScalar px, SkScalar py)

#Line # sets to rotate about a point ##
Sets Matrix to rotate by degrees about a pivot point at (px, py).
The pivot point is unchanged when mapped with Matrix.

Positive degrees rotates clockwise.

#Param degrees  angle of axes relative to upright axes ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Height 128
    SkPaint paint;
    paint.setColor(SK_ColorGRAY);
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    SkMatrix matrix;
    matrix.setRotate(25, rect.centerX(), rect.centerY());
    canvas->concat(matrix);
    canvas->drawRect(rect, paint);
##

#SeeAlso setSinCos preRotate postRotate

#Method ##

# ------------------------------------------------------------------------------

#Method void setRotate(SkScalar degrees)

Sets Matrix to rotate by degrees about a pivot point at (0, 0).
Positive degrees rotates clockwise.

#Param degrees  angle of axes relative to upright axes ##

#Example
#Height 128
    SkPaint paint;
    paint.setColor(SK_ColorGRAY);
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    SkMatrix matrix;
    matrix.setRotate(25);
    canvas->translate(rect.centerX(), rect.centerY());
    canvas->concat(matrix);
    canvas->translate(-rect.centerX(), -rect.centerY());
    canvas->drawRect(rect, paint);
##

#SeeAlso setSinCos preRotate postRotate

#Method ##

# ------------------------------------------------------------------------------

#Method void setSinCos(SkScalar sinValue, SkScalar cosValue,
                   SkScalar px, SkScalar py)
#Line # sets to rotate and scale about a point ##

Sets Matrix to rotate by sinValue and cosValue, about a pivot point at (px, py). 
The pivot point is unchanged when mapped with Matrix.

Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1).
Vector length specifies scale.

#Param sinValue  rotation vector x component ##
#Param cosValue  rotation vector y component ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Height 128
    SkPaint paint;
    paint.setColor(SK_ColorGRAY);
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    SkMatrix matrix;
    matrix.setSinCos(.25f, .85f, rect.centerX(), rect.centerY());
    canvas->concat(matrix);
    canvas->drawRect(rect, paint);
##

#SeeAlso setRotate setScale setRSXform

#Method ##

# ------------------------------------------------------------------------------

#Method void setSinCos(SkScalar sinValue, SkScalar cosValue)

Sets Matrix to rotate by sinValue and cosValue, about a pivot point at (0, 0). 

Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1).
Vector length specifies scale.

#Param sinValue  rotation vector x component ##
#Param cosValue  rotation vector y component ##

#Example
#Description
Canvas needs offset after applying Matrix to pivot about Rect center.
##
#Height 128
    SkPaint paint;
    paint.setColor(SK_ColorGRAY);
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    SkMatrix matrix;
    matrix.setSinCos(.25f, .85f);
    matrix.postTranslate(rect.centerX(), rect.centerY());
    canvas->concat(matrix);
    canvas->translate(-rect.centerX(), -rect.centerY());
    canvas->drawRect(rect, paint);
##

#SeeAlso setRotate setScale setRSXform

#Method ##

# ------------------------------------------------------------------------------

#Method SkMatrix& setRSXform(const SkRSXform& rsxForm)

#Line # sets to rotate, scale, and translate ##
Sets Matrix to rotate, scale, and translate using a compressed matrix form.

Vector (rsxForm.fSSin, rsxForm.fSCos) describes the angle of rotation relative
to (0, 1). Vector length specifies scale. Mapped point is rotated and scaled
by Vector, then translated by (rsxForm.fTx, rsxForm.fTy).

#Param rsxForm  compressed RSXform matrix ##

#Return  reference to Matrix ##

#Example
#Description
Canvas needs offset after applying Matrix to pivot about Rect center.
##
#Height 128
    SkPaint paint;
    paint.setColor(SK_ColorGRAY);
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    SkMatrix matrix;
    matrix.setRSXform(SkRSXform::Make(.85f, .25f, rect.centerX(), rect.centerY()));
    canvas->concat(matrix);
    canvas->translate(-rect.centerX(), -rect.centerY());
    canvas->drawRect(rect, paint);
##

#SeeAlso setSinCos setScale setTranslate

#Method ##

# ------------------------------------------------------------------------------

#Method void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py)

#Line # sets to skew about a point ##
Sets Matrix to skew by kx and ky, about a pivot point at (px, py).
The pivot point is unchanged when mapped with Matrix.

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(48);
    SkMatrix m;
    for (SkScalar sx : { -1, 0, 1 } ) {
        for (SkScalar sy : { -1, 0, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            m.setSkew(sx, sy, 96 + 64 * sx, 128 + 48 * sy);
            canvas->concat(m);
            canvas->drawString("K", 96 + 64 * sx, 128 + 48 * sy, p);
        }
    }
##

#SeeAlso setSkewX setSkewY preSkew postSkew

#Method ##

# ------------------------------------------------------------------------------

#Method void setSkew(SkScalar kx, SkScalar ky)

Sets Matrix to skew by kx and ky, about a pivot point at (0, 0).

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##

#Example
    SkPaint p;
    p.setAntiAlias(true);
    p.setTextSize(48);
    SkMatrix m;
    for (SkScalar sx : { -1, 0, 1 } ) {
        for (SkScalar sy : { -1, 0, 1 } ) {
            SkAutoCanvasRestore autoRestore(canvas, true);
            m.setSkew(sx, sy);
            m.postTranslate(96 + 64 * sx, 128 + 48 * sy);
            canvas->concat(m);
            canvas->drawString("K", 0, 0, p);
        }
    }
##

#SeeAlso setSkewX setSkewY preSkew postSkew

#Method ##

# ------------------------------------------------------------------------------

#Method void setConcat(const SkMatrix& a, const SkMatrix& b)

#Line # sets to Matrix parameter multiplied by Matrix parameter ##
Sets Matrix to Matrix a multiplied by Matrix b. Either a or b may be this.

Given:

#Code
#Literal
    | A B C |      | J K L |
a = | D E F |, b = | M N O |
    | G H I |      | P Q R |
##

sets Matrix to:

#Code
#Literal
        | A B C |   | J K L |   | AJ+BM+CP AK+BN+CQ AL+BO+CR |
a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
        | G H I |   | P Q R |   | GJ+HM+IP GK+HN+IQ GL+HO+IR |
##

#Param a  Matrix on left side of multiply expression ##
#Param b  Matrix on right side of multiply expression ##

#Example
#Image 3
#Description
setPolyToPoly creates perspective matrices, one the inverse of the other.
Multiplying the matrix by its inverse turns into an identity matrix.
##
SkMatrix matrix, matrix2;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix2.setPolyToPoly(perspect, bitmapBounds, 4);
matrix.setConcat(matrix, matrix2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso Concat preConcat postConcat SkCanvas::concat

#Method ##

# ------------------------------------------------------------------------------

#Method void preTranslate(SkScalar dx, SkScalar dy)

#Line # pre-multiplies Matrix by translation ##
Sets Matrix to Matrix multiplied by Matrix constructed from translation (dx, dy).
This can be thought of as moving the point to be mapped before applying Matrix.

Given:

#Code
#Literal
         | A B C |               | 1 0 dx |
Matrix = | D E F |,  T(dx, dy) = | 0 1 dy |
         | G H I |               | 0 0  1 |
##

sets Matrix to:

#Code
#Literal
                     | A B C | | 1 0 dx |   | A B A*dx+B*dy+C |
Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E D*dx+E*dy+F |
                     | G H I | | 0 0  1 |   | G H G*dx+H*dy+I |
##

#Param dx  x translation before applying Matrix ##
#Param dy  y translation before applying Matrix ##

#Example
#Height 160
    SkPaint paint;
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    for (int i = 0; i < 2; ++i ) {
        SkMatrix matrix;
        i == 0 ? matrix.reset(): matrix.setRotate(25, rect.centerX(), 320);
        { 
            SkAutoCanvasRestore acr(canvas, true);
            canvas->concat(matrix);
            paint.setColor(SK_ColorGRAY);
            canvas->drawRect(rect, paint);
        }
        paint.setColor(SK_ColorRED);
        for (int j = 0; j < 2; ++j ) {
            SkAutoCanvasRestore acr(canvas, true);
            matrix.preTranslate(40, 40);
            canvas->concat(matrix);
            canvas->drawCircle(0, 0, 3, paint);
        }
    }
##

#SeeAlso postTranslate setTranslate MakeTrans

#Method ##

# ------------------------------------------------------------------------------

#Method void preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)

#Line # pre-multiplies Matrix by scale ##
Sets Matrix to Matrix multiplied by Matrix constructed from scaling by (sx, sy)
about pivot point (px, py).
This can be thought of as scaling about a pivot point before applying Matrix.

Given:

#Code
#Literal
         | A B C |                       | sx  0 dx |
Matrix = | D E F |,  S(sx, sy, px, py) = |  0 sy dy |
         | G H I |                       |  0  0  1 |
##

where 

#Code
#Literal
dx = px - sx * px
dy = py - sy * py
##

sets Matrix to:

#Code
#Literal
                             | A B C | | sx  0 dx |   | A*sx B*sy A*dx+B*dy+C |
Matrix * S(sx, sy, px, py) = | D E F | |  0 sy dy | = | D*sx E*sy D*dx+E*dy+F |
                             | G H I | |  0  0  1 |   | G*sx H*sy G*dx+H*dy+I |
##

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preScale(.75f, 1.5f, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postScale setScale MakeScale

#Method ##

# ------------------------------------------------------------------------------

#Method void preScale(SkScalar sx, SkScalar sy)

Sets Matrix to Matrix multiplied by Matrix constructed from scaling by (sx, sy)
about pivot point (0, 0).
This can be thought of as scaling about the origin before applying Matrix.

Given:

#Code
#Literal
         | A B C |               | sx  0  0 |
Matrix = | D E F |,  S(sx, sy) = |  0 sy  0 |
         | G H I |               |  0  0  1 |
##

sets Matrix to:

#Code
#Literal
                     | A B C | | sx  0  0 |   | A*sx B*sy C |
Matrix * S(sx, sy) = | D E F | |  0 sy  0 | = | D*sx E*sy F |
                     | G H I | |  0  0  1 |   | G*sx H*sy I |
##

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preScale(.75f, 1.5f);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postScale setScale MakeScale

#Method ##

# ------------------------------------------------------------------------------

#Method void preRotate(SkScalar degrees, SkScalar px, SkScalar py)

#Line # pre-multiplies Matrix by rotation ##
Sets Matrix to Matrix multiplied by Matrix constructed from rotating by degrees
about pivot point (px, py).
This can be thought of as rotating about a pivot point before applying Matrix.

Positive degrees rotates clockwise.

Given:

#Code
#Literal
         | A B C |                        | c -s dx |
Matrix = | D E F |,  R(degrees, px, py) = | s  c dy |
         | G H I |                        | 0  0  1 |
##

where 

#Code
#Literal
c  = cos(degrees)
s  = sin(degrees)
dx =  s * py + (1 - c) * px
dy = -s * px + (1 - c) * py
##

sets Matrix to:

#Code
#Literal
                              | A B C | | c -s dx |   | Ac+Bs -As+Bc A*dx+B*dy+C |
Matrix * R(degrees, px, py) = | D E F | | s  c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F |
                              | G H I | | 0  0  1 |   | Gc+Hs -Gs+Hc G*dx+H*dy+I |
##

#Param degrees  angle of axes relative to upright axes ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preRotate(45, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postRotate setRotate

#Method ##

# ------------------------------------------------------------------------------

#Method void preRotate(SkScalar degrees)

Sets Matrix to Matrix multiplied by Matrix constructed from rotating by degrees
about pivot point (0, 0).
This can be thought of as rotating about the origin before applying Matrix.

Positive degrees rotates clockwise.

Given:

#Code
#Literal
         | A B C |                        | c -s 0 |
Matrix = | D E F |,  R(degrees, px, py) = | s  c 0 |
         | G H I |                        | 0  0 1 |
##

where 

#Code
#Literal
c  = cos(degrees)
s  = sin(degrees)
##

sets Matrix to:

#Code
#Literal
                              | A B C | | c -s 0 |   | Ac+Bs -As+Bc C |
Matrix * R(degrees, px, py) = | D E F | | s  c 0 | = | Dc+Es -Ds+Ec F |
                              | G H I | | 0  0 1 |   | Gc+Hs -Gs+Hc I |
##

#Param degrees  angle of axes relative to upright axes ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preRotate(45);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postRotate setRotate

#Method ##

# ------------------------------------------------------------------------------

#Method void preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py)

#Line # pre-multiplies Matrix by skew ##
Sets Matrix to Matrix multiplied by Matrix constructed from skewing by (kx, ky)
about pivot point (px, py).
This can be thought of as skewing about a pivot point before applying Matrix.

Given:

#Code
#Literal
         | A B C |                       |  1 kx dx |
Matrix = | D E F |,  K(kx, ky, px, py) = | ky  1 dy |
         | G H I |                       |  0  0  1 |
##

where 

#Code
#Literal
dx = -kx * py
dy = -ky * px
##

sets Matrix to:

#Code
#Literal
                             | A B C | |  1 kx dx |   | A+B*ky A*kx+B A*dx+B*dy+C |
Matrix * K(kx, ky, px, py) = | D E F | | ky  1 dy | = | D+E*ky D*kx+E D*dx+E*dy+F |
                             | G H I | |  0  0  1 |   | G+H*ky G*kx+H G*dx+H*dy+I |
##

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preSkew(.5f, 0, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postSkew setSkew

#Method ##

# ------------------------------------------------------------------------------

#Method void preSkew(SkScalar kx, SkScalar ky)

Sets Matrix to Matrix multiplied by Matrix constructed from skewing by (kx, ky)
about pivot point (0, 0).
This can be thought of as skewing about the origin before applying Matrix.

Given:

#Code
#Literal
         | A B C |               |  1 kx 0 |
Matrix = | D E F |,  K(kx, ky) = | ky  1 0 |
         | G H I |               |  0  0 1 |
##

sets Matrix to:

#Code
#Literal
                     | A B C | |  1 kx 0 |   | A+B*ky A*kx+B C |
Matrix * K(kx, ky) = | D E F | | ky  1 0 | = | D+E*ky D*kx+E F |
                     | G H I | |  0  0 1 |   | G+H*ky G*kx+H I |
##

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.preSkew(.5f, 0);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postSkew setSkew

#Method ##

# ------------------------------------------------------------------------------

#Method void preConcat(const SkMatrix& other)

#Line # pre-multiplies Matrix by Matrix parameter ##
Sets Matrix to Matrix multiplied by Matrix other.
This can be thought of mapping by other before applying Matrix.

Given:

#Code
#Literal
         | A B C |          | J K L |
Matrix = | D E F |, other = | M N O |
         | G H I |          | P Q R |
##

sets Matrix to:

#Code
#Literal
                 | A B C |   | J K L |   | AJ+BM+CP AK+BN+CQ AL+BO+CR |
Matrix * other = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
                 | G H I |   | P Q R |   | GJ+HM+IP GK+HN+IQ GL+HO+IR |
##

#Param other  Matrix on right side of multiply expression ##

#Example
#Image 3
#Description
setPolyToPoly creates perspective matrices, one the inverse of the other.
Multiplying the matrix by its inverse turns into an identity matrix.
##
SkMatrix matrix, matrix2;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix2.setPolyToPoly(perspect, bitmapBounds, 4);
matrix.preConcat(matrix2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postConcat setConcat Concat

#Method ##

# ------------------------------------------------------------------------------

#Method void postTranslate(SkScalar dx, SkScalar dy)

#Line # post-multiplies Matrix by translation ##
Sets Matrix to Matrix constructed from translation (dx, dy) multiplied by Matrix.
This can be thought of as moving the point to be mapped after applying Matrix.

Given:

#Code
#Literal
         | J K L |               | 1 0 dx |
Matrix = | M N O |,  T(dx, dy) = | 0 1 dy |
         | P Q R |               | 0 0  1 |
##

sets Matrix to:

#Code
#Literal
                     | 1 0 dx | | J K L |   | J+dx*P K+dx*Q L+dx*R |
T(dx, dy) * Matrix = | 0 1 dy | | M N O | = | M+dy*P N+dy*Q O+dy*R |
                     | 0 0  1 | | P Q R |   |      P      Q      R |
##

#Param dx  x translation after applying Matrix ##
#Param dy  y translation after applying Matrix ##

#Example
#Height 160
#Description
Compare with preTranslate example.
##
    SkPaint paint;
    paint.setAntiAlias(true);
    SkRect rect = {20, 20, 100, 100};
    for (int i = 0; i < 2; ++i ) {
        SkMatrix matrix;
        i == 0 ? matrix.reset(): matrix.setRotate(25, rect.centerX(), 320);
        { 
            SkAutoCanvasRestore acr(canvas, true);
            canvas->concat(matrix);
            paint.setColor(SK_ColorGRAY);
            canvas->drawRect(rect, paint);
        }
        paint.setColor(SK_ColorRED);
        for (int j = 0; j < 2; ++j ) {
            SkAutoCanvasRestore acr(canvas, true);
            matrix.postTranslate(40, 40);
            canvas->concat(matrix);
            canvas->drawCircle(0, 0, 3, paint);
        }
    }
##

#SeeAlso preTranslate setTranslate MakeTrans 

#Method ##

# ------------------------------------------------------------------------------

#Method void postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)

#Line # post-multiplies Matrix by scale ##
Sets Matrix to Matrix constructed from scaling by (sx, sy) about pivot point
(px, py), multiplied by Matrix.
This can be thought of as scaling about a pivot point after applying Matrix.

Given:

#Code
#Literal
         | J K L |                       | sx  0 dx |
Matrix = | M N O |,  S(sx, sy, px, py) = |  0 sy dy |
         | P Q R |                       |  0  0  1 |
##

where 

#Code
#Literal
dx = px - sx * px
dy = py - sy * py
##

sets Matrix to:

#Code
#Literal
                             | sx  0 dx | | J K L |   | sx*J+dx*P sx*K+dx*Q sx*L+dx+R |
S(sx, sy, px, py) * Matrix = |  0 sy dy | | M N O | = | sy*M+dy*P sy*N+dy*Q sy*O+dy*R |
                             |  0  0  1 | | P Q R |   |         P         Q         R |
##

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postScale(.75f, 1.5f, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preScale setScale MakeScale

##

# ------------------------------------------------------------------------------

#Method void postScale(SkScalar sx, SkScalar sy)

Sets Matrix to Matrix constructed from scaling by (sx, sy) about pivot point
(0, 0), multiplied by Matrix.
This can be thought of as scaling about the origin after applying Matrix.

Given:

#Code
#Literal
         | J K L |               | sx  0  0 |
Matrix = | M N O |,  S(sx, sy) = |  0 sy  0 |
         | P Q R |               |  0  0  1 |
##

sets Matrix to:

#Code
#Literal
                     | sx  0  0 | | J K L |   | sx*J sx*K sx*L |
S(sx, sy) * Matrix = |  0 sy  0 | | M N O | = | sy*M sy*N sy*O |
                     |  0  0  1 | | P Q R |   |    P    Q    R |
##

#Param sx  horizontal scale factor ##
#Param sy  vertical scale factor ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postScale(.75f, 1.5f);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preScale setScale MakeScale

##

# ------------------------------------------------------------------------------

#Method bool postIDiv(int divx, int divy)

#Line # post-multiplies Matrix by inverse scale ##
Sets Matrix to Matrix constructed from scaling by
#Formula
(1/divx, 1/divy)
##
about pivot point (px, py), multiplied by Matrix.

Returns false if either divx or divy is zero.

Given:

#Code
#Literal
         | J K L |                   | sx  0  0 |
Matrix = | M N O |,  I(divx, divy) = |  0 sy  0 |
         | P Q R |                   |  0  0  1 |
##

where 

#Code
#Literal
sx = 1 / divx
sy = 1 / divy
##

sets Matrix to:

#Code
#Literal
                         | sx  0  0 | | J K L |   | sx*J sx*K sx*L |
I(divx, divy) * Matrix = |  0 sy  0 | | M N O | = | sy*M sy*N sy*O |
                         |  0  0  1 | | P Q R |   |    P    Q    R |
##

#Param divx  integer divisor for inverse scale in x ##
#Param divy  integer divisor for inverse scale in y ##

#Return  true on successful scale ##

#Example
#Image 3
SkMatrix matrix, matrix2;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postIDiv(1, 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso postScale MakeScale

##

# ------------------------------------------------------------------------------

#Method void postRotate(SkScalar degrees, SkScalar px, SkScalar py)

#Line # post-multiplies Matrix by rotation ##
Sets Matrix to Matrix constructed from rotating by degrees about pivot point
(px, py), multiplied by Matrix.
This can be thought of as rotating about a pivot point after applying Matrix.

Positive degrees rotates clockwise.

Given:

#Code
#Literal
         | J K L |                        | c -s dx |
Matrix = | M N O |,  R(degrees, px, py) = | s  c dy |
         | P Q R |                        | 0  0  1 |
##

where 

#Code
#Literal
c  = cos(degrees)
s  = sin(degrees)
dx =  s * py + (1 - c) * px
dy = -s * px + (1 - c) * py
##

sets Matrix to:

#Code
#Literal
                              |c -s dx| |J K L|   |cJ-sM+dx*P cK-sN+dx*Q cL-sO+dx+R|
R(degrees, px, py) * Matrix = |s  c dy| |M N O| = |sJ+cM+dy*P sK+cN+dy*Q sL+cO+dy*R|
                              |0  0  1| |P Q R|   |         P          Q          R|
##

#Param degrees  angle of axes relative to upright axes ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postRotate(45, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preRotate setRotate

##

# ------------------------------------------------------------------------------

#Method void postRotate(SkScalar degrees)

Sets Matrix to Matrix constructed from rotating by degrees about pivot point
(0, 0), multiplied by Matrix.
This can be thought of as rotating about the origin after applying Matrix.

Positive degrees rotates clockwise.

Given:

#Code
#Literal
         | J K L |                        | c -s 0 |
Matrix = | M N O |,  R(degrees, px, py) = | s  c 0 |
         | P Q R |                        | 0  0 1 |
##

where 

#Code
#Literal
c  = cos(degrees)
s  = sin(degrees)
##

sets Matrix to:

#Code
#Literal
                              | c -s dx | | J K L |   | cJ-sM cK-sN cL-sO |
R(degrees, px, py) * Matrix = | s  c dy | | M N O | = | sJ+cM sK+cN sL+cO |
                              | 0  0  1 | | P Q R |   |     P     Q     R |
##

#Param degrees  angle of axes relative to upright axes ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postRotate(45);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preRotate setRotate

##

# ------------------------------------------------------------------------------

#Method void postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py)

#Line # post-multiplies Matrix by skew ##
Sets Matrix to Matrix constructed from skewing by (kx, ky) about pivot point
(px, py), multiplied by Matrix.
This can be thought of as skewing about a pivot point after applying Matrix.

Given:

#Code
#Literal
         | J K L |                       |  1 kx dx |
Matrix = | M N O |,  K(kx, ky, px, py) = | ky  1 dy |
         | P Q R |                       |  0  0  1 |
##

where 

#Code
#Literal
dx = -kx * py
dy = -ky * px
##

sets Matrix to:

#Code
#Literal
                             | 1 kx dx| |J K L|   |J+kx*M+dx*P K+kx*N+dx*Q L+kx*O+dx+R|
K(kx, ky, px, py) * Matrix = |ky  1 dy| |M N O| = |ky*J+M+dy*P ky*K+N+dy*Q ky*L+O+dy*R|
                             | 0  0  1| |P Q R|   |          P           Q           R|
##

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##
#Param px  pivot x ##
#Param py  pivot y ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postSkew(.5f, 0, source.width() / 2, source.height() / 2);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preSkew setSkew

##

# ------------------------------------------------------------------------------

#Method void postSkew(SkScalar kx, SkScalar ky)

Sets Matrix to Matrix constructed from skewing by (kx, ky) about pivot point
(0, 0), multiplied by Matrix.
This can be thought of as skewing about the origin after applying Matrix.

Given:

#Code
#Literal
         | J K L |               |  1 kx 0 |
Matrix = | M N O |,  K(kx, ky) = | ky  1 0 |
         | P Q R |               |  0  0 1 |
##

sets Matrix to:

#Code
#Literal
                     |  1 kx 0 | | J K L |   | J+kx*M K+kx*N L+kx*O |
K(kx, ky) * Matrix = | ky  1 0 | | M N O | = | ky*J+M ky*K+N ky*L+O |
                     |  0  0 1 | | P Q R |   |      P      Q      R |
##

#Param kx  horizontal skew factor ##
#Param ky  vertical skew factor ##

#Example
#Image 3
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postSkew(.5f, 0);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preSkew setSkew

##

# ------------------------------------------------------------------------------

#Method void postConcat(const SkMatrix& other)

#Line # post-multiplies Matrix by Matrix parameter ##
Sets Matrix to Matrix other multiplied by Matrix.
This can be thought of mapping by other after applying Matrix.

Given:

#Code
#Literal
         | J K L |           | A B C |
Matrix = | M N O |,  other = | D E F |
         | P Q R |           | G H I |
##

sets Matrix to:

#Code
#Literal
                 | A B C |   | J K L |   | AJ+BM+CP AK+BN+CQ AL+BO+CR |
other * Matrix = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
                 | G H I |   | P Q R |   | GJ+HM+IP GK+HN+IQ GL+HO+IR |
##

#Param other  Matrix on left side of multiply expression ##

#Example
#Image 3
#Height 64
SkMatrix matrix, matrix2;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix.postConcat(matrix);
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preConcat setConcat Concat

##

# ------------------------------------------------------------------------------

#Enum ScaleToFit

#Code
    enum ScaleToFit {
        kFill_ScaleToFit,
        kStart_ScaleToFit,
        kCenter_ScaleToFit,
        kEnd_ScaleToFit,
    };
##

ScaleToFit describes how Matrix is constructed to map one Rect to another.
ScaleToFit may allow Matrix to have unequal horizontal and vertical scaling,
or may restrict Matrix to square scaling. If restricted, ScaleToFit specifies
how Matrix maps to the side or center of the destination Rect.

#Const kFill_ScaleToFit 0
    Computes Matrix that scales in x and y independently, so that source Rect is
    mapped to completely fill destination Rect. The aspect ratio of source Rect
    may change.
##
#Const kStart_ScaleToFit 1
    Computes Matrix that maintains source Rect aspect ratio, mapping source Rect
    width or height to destination Rect. Aligns mapping to left and top edges
    of destination Rect.
##
#Const kCenter_ScaleToFit 2
    Computes Matrix that maintains source Rect aspect ratio, mapping source Rect
    width or height to destination Rect. Aligns mapping to center of destination
    Rect.
##
#Const kEnd_ScaleToFit 3
    Computes Matrix that maintains source Rect aspect ratio, mapping source Rect
    width or height to destination Rect. Aligns mapping to right and bottom
    edges of destination Rect.
##

#Example
   const char* labels[] = { "Fill", "Start", "Center", "End" };
   SkRect rects[] = {{5, 5, 59, 59}, {5, 74, 59, 108}, {10, 123, 44, 172}, {10, 187, 54, 231}};
   SkRect bounds;
   source.getBounds(&bounds);
   SkPaint paint;
   paint.setAntiAlias(true);
   for (auto fit : { SkMatrix::kFill_ScaleToFit, SkMatrix::kStart_ScaleToFit,
                     SkMatrix::kCenter_ScaleToFit, SkMatrix::kEnd_ScaleToFit } ) {
       for (auto rect : rects ) {
           canvas->drawRect(rect, paint);
           SkMatrix matrix;
           if (!matrix.setRectToRect(bounds, rect, fit)) {
               continue;
           }
           SkAutoCanvasRestore acr(canvas, true);
           canvas->concat(matrix);
           canvas->drawBitmap(source, 0, 0);
       }
       canvas->drawString(labels[fit], 10, 255, paint);
       canvas->translate(64, 0);
   }
##

#SeeAlso setRectToRect MakeRectToRect setPolyToPoly

##

# ------------------------------------------------------------------------------

#Method bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf)

#Line # sets to map one Rect to another ##
Sets Matrix to scale and translate src Rect to dst Rect. stf selects whether
mapping completely fills dst or preserves the aspect ratio, and how to align
src within dst. Returns false if src is empty, and sets Matrix to identity.
Returns true if dst is empty, and sets Matrix to:

#Code
#Literal
| 0 0 0 |
| 0 0 0 |
| 0 0 1 |
##

#Param src  Rect to map from ##
#Param dst  Rect to map to ##
#Param stf  one of: kFill_ScaleToFit, kStart_ScaleToFit,
                    kCenter_ScaleToFit, kEnd_ScaleToFit
##

#Return  true if Matrix can represent Rect mapping ##

#Example
    const SkRect srcs[] = { {0, 0, 0, 0}, {1, 2, 3, 4} };
    const SkRect dsts[] = { {0, 0, 0, 0}, {5, 6, 8, 9} };
    for (auto src : srcs) {
        for (auto dst : dsts) {
             SkMatrix matrix;
             matrix.setAll(-1, -1, -1, -1, -1, -1, -1, -1, -1);
             bool success = matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
             SkDebugf("src: %g, %g, %g, %g  dst: %g, %g, %g, %g  success: %s\n",
                      src.fLeft, src.fTop, src.fRight, src.fBottom,
                      dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, success ? "true" : "false");
             matrix.dump();
        }
    }
#StdOut
src: 0, 0, 0, 0  dst: 0, 0, 0, 0  success: false
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
src: 0, 0, 0, 0  dst: 5, 6, 8, 9  success: false
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
src: 1, 2, 3, 4  dst: 0, 0, 0, 0  success: true
[  0.0000   0.0000   0.0000][  0.0000   0.0000   0.0000][  0.0000   0.0000   1.0000]
src: 1, 2, 3, 4  dst: 5, 6, 8, 9  success: true
[  1.5000   0.0000   3.5000][  0.0000   1.5000   3.0000][  0.0000   0.0000   1.0000]
##
##

#SeeAlso MakeRectToRect ScaleToFit setPolyToPoly SkRect::isEmpty 

##

# ------------------------------------------------------------------------------

#Method static SkMatrix MakeRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf)

#Line # constructs from source Rect to destination Rect ##
Returns Matrix set to scale and translate src Rect to dst Rect. stf selects
whether mapping completely fills dst or preserves the aspect ratio, and how to
align src within dst. Returns the identity Matrix if src is empty. If dst is
empty, returns Matrix set to:

#Code
#Literal
| 0 0 0 |
| 0 0 0 |
| 0 0 1 |
##

#Param src  Rect to map from ##
#Param dst  Rect to map to ##
#Param stf  one of: kFill_ScaleToFit, kStart_ScaleToFit,
                    kCenter_ScaleToFit, kEnd_ScaleToFit
##

#Return  Matrix mapping src to dst ##

#Example
    const SkRect srcs[] = { {0, 0, 0, 0}, {1, 2, 3, 4} };
    const SkRect dsts[] = { {0, 0, 0, 0}, {5, 6, 8, 9} };
    for (auto src : srcs) {
        for (auto dst : dsts) {
             SkMatrix matrix = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
             SkDebugf("src: %g, %g, %g, %g  dst: %g, %g, %g, %g\n",
                      src.fLeft, src.fTop, src.fRight, src.fBottom,
                      dst.fLeft, dst.fTop, dst.fRight, dst.fBottom);
             matrix.dump();
        }
    }
#StdOut
src: 0, 0, 0, 0  dst: 0, 0, 0, 0
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
src: 0, 0, 0, 0  dst: 5, 6, 8, 9
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
src: 1, 2, 3, 4  dst: 0, 0, 0, 0
[  0.0000   0.0000   0.0000][  0.0000   0.0000   0.0000][  0.0000   0.0000   1.0000]
src: 1, 2, 3, 4  dst: 5, 6, 8, 9
[  1.5000   0.0000   3.5000][  0.0000   1.5000   3.0000][  0.0000   0.0000   1.0000]
##
##

#SeeAlso setRectToRect ScaleToFit setPolyToPoly SkRect::isEmpty

##

# ------------------------------------------------------------------------------

#Method bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count)

#Line # sets to map one to four points to an equal array of points ##
Sets Matrix to map src to dst. count must be zero or greater, and four or less.

If count is zero, sets Matrix to identity and returns true.
If count is one, sets Matrix to translate and returns true.
If count is two or more, sets Matrix to map Points if possible; returns false
if Matrix cannot be constructed. If count is four, Matrix may include
perspective. 

#Param src  Points to map from ##
#Param dst  Points to map to ##
#Param count  number of Points in src and dst ##

#Return  true if Matrix was constructed successfully
##

#Example
    const SkPoint src[] = { { 0, 0}, {30,   0}, {30, -30}, { 0, -30} };
    const SkPoint dst[] = { {50, 0}, {80, -10}, {90, -30}, {60, -40} };
    SkPaint blackPaint;
    blackPaint.setAntiAlias(true);
    blackPaint.setTextSize(42);
    SkPaint redPaint = blackPaint;
    redPaint.setColor(SK_ColorRED);
    for (int count : { 1, 2, 3, 4 } ) {
        canvas->translate(35, 55);
        for (int index = 0; index < count; ++index) {
            canvas->drawCircle(src[index], 3, blackPaint);
            canvas->drawCircle(dst[index], 3, blackPaint);
            if (index > 0) {
                canvas->drawLine(src[index], src[index - 1], blackPaint);
                canvas->drawLine(dst[index], dst[index - 1], blackPaint);
            }
        }
        SkMatrix matrix;
        matrix.setPolyToPoly(src, dst, count);
        canvas->drawString("A", src[0].fX, src[0].fY, redPaint);
        SkAutoCanvasRestore acr(canvas, true);
        canvas->concat(matrix);
        canvas->drawString("A", src[0].fX, src[0].fY, redPaint);
    }
##

#SeeAlso setRectToRect MakeRectToRect

##

# ------------------------------------------------------------------------------

#Method bool SK_WARN_UNUSED_RESULT invert(SkMatrix* inverse) const

#Line # returns inverse, if possible ##
Sets inverse to reciprocal matrix, returning true if Matrix can be inverted.
Geometrically, if Matrix maps from source to destination, inverse Matrix
maps from destination to source. If Matrix can not be inverted, inverse is
unchanged.

#Param inverse  storage for inverted Matrix; may be nullptr ##

#Return  true if Matrix can be inverted ##

#Example
#Height 128
    const SkPoint src[] = { { 10, 120}, {120, 120}, {120, 10}, {  10, 10} };
    const SkPoint dst[] = { {150, 120}, {200, 100}, {240, 30}, { 130, 40} };
    SkPaint paint;
    paint.setAntiAlias(true);
    SkMatrix matrix;
    matrix.setPolyToPoly(src, dst, 4);
    canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, src, paint);
    canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, dst, paint);
    paint.setColor(SK_ColorBLUE);
    paint.setStrokeWidth(3);
    paint.setStrokeCap(SkPaint::kRound_Cap);
    canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, dst, paint);
    matrix.invert(&matrix);
    canvas->concat(matrix);
    canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, dst, paint);
##

#SeeAlso Concat

##

# ------------------------------------------------------------------------------

#Method static void SetAffineIdentity(SkScalar affine[6])

#Line # sets 3x2 array to identity ##
Fills affine with identity values in column major order.
Sets affine to:

#Code
#Literal
| 1 0 0 |
| 0 1 0 |
##

Affine 3x2 matrices in column major order are used by OpenGL and XPS.

#Param affine  storage for 3x2 affine matrix ##

#Example
    SkScalar affine[6];
    SkMatrix::SetAffineIdentity(affine);
    const char* names[] = { "ScaleX", "SkewY", "SkewX", "ScaleY", "TransX", "TransY" };
    for (int i = 0; i < 6; ++i) {
        SkDebugf("%s: %g ", names[i], affine[i]);
    }
    SkDebugf("\n");
#StdOut
ScaleX: 1 SkewY: 0 SkewX: 0 ScaleY: 1 TransX: 0 TransY: 0
##
##

#SeeAlso setAffine asAffine

##

# ------------------------------------------------------------------------------

#Method bool SK_WARN_UNUSED_RESULT asAffine(SkScalar affine[6]) const

#Line # copies to 3x2 array ##
Fills affine in column major order. Sets affine to:

#Code
#Literal
| scale-x  skew-x translate-x |
| skew-y  scale-y translate-y |
##

If Matrix contains perspective, returns false and leaves affine unchanged.

#Param affine  storage for 3x2 affine matrix; may be nullptr ##

#Return  true if Matrix does not contain perspective ##

#Example
SkMatrix matrix;
matrix.setAll(2, 3, 4, 5, 6, 7, 0, 0, 1);
SkScalar affine[6];
matrix.asAffine(affine);
const char* names[] = { "ScaleX", "SkewY", "SkewX", "ScaleY", "TransX", "TransY" };
for (int i = 0; i < 6; ++i) {
    SkDebugf("%s: %g ", names[i], affine[i]);
}
SkDebugf("\n");
#StdOut
ScaleX: 2 SkewY: 5 SkewX: 3 ScaleY: 6 TransX: 4 TransY: 7 
##
##

#SeeAlso setAffine SetAffineIdentity

##

# ------------------------------------------------------------------------------

#Method void setAffine(const SkScalar affine[6])

#Line # sets left two columns ##
Sets Matrix to affine values, passed in column major order. Given affine,
column, then row, as:

#Code
#Literal
| scale-x  skew-x translate-x |
|  skew-y scale-y translate-y |
##

Matrix is set, row, then column, to:

#Code
#Literal
| scale-x  skew-x translate-x |
|  skew-y scale-y translate-y |
|       0       0           1 |
##

#Param affine  3x2 affine matrix ##

#Example
SkMatrix matrix;
matrix.setAll(2, 3, 4, 5, 6, 7, 0, 0, 1);
SkScalar affine[6];
matrix.asAffine(affine);
const char* names[] = { "ScaleX", "SkewY", "SkewX", "ScaleY", "TransX", "TransY" };
for (int i = 0; i < 6; ++i) {
    SkDebugf("%s: %g ", names[i], affine[i]);
}
SkDebugf("\n");
matrix.reset();
matrix.setAffine(affine);
matrix.dump();
#StdOut
ScaleX: 2 SkewY: 5 SkewX: 3 ScaleY: 6 TransX: 4 TransY: 7 
[  2.0000   3.0000   4.0000][  5.0000   6.0000   7.0000][  0.0000   0.0000   1.0000]
##
##

#SeeAlso asAffine SetAffineIdentity

##

# ------------------------------------------------------------------------------

#Method void mapPoints(SkPoint dst[], const SkPoint src[], int count) const

#Line # maps Point array ##
Maps src Point array of length count to dst Point array of equal or greater
length. Points are mapped by multiplying each Point by Matrix. Given:

#Code
#Literal
         | A B C |        | x |
Matrix = | D E F |,  pt = | y |
         | G H I |        | 1 |
##

where 

#Code
#Literal
for (i = 0; i < count; ++i) {
    x = src[i].fX
    y = src[i].fY
}
##

each dst Point is computed as:

#Code
#Literal
              |A B C| |x|                               Ax+By+C   Dx+Ey+F
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
              |G H I| |1|                               Gx+Hy+I   Gx+Hy+I
##

src and dst may point to the same storage.

#Param dst  storage for mapped Points ##
#Param src  Points to transform ##
#Param count  number of Points to transform ##

#Example
    SkMatrix matrix;
    matrix.reset();
    const int count = 4;
    SkPoint src[count];
    matrix.mapRectToQuad(src, {40, 70, 180, 220} );
    SkPaint paint;
    paint.setARGB(77, 23, 99, 154);
    for (int i = 0; i < 5; ++i) {
        SkPoint dst[count];
        matrix.mapPoints(dst, src, count);
        canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, dst, paint);
        matrix.preRotate(35, 128, 128);
    }
##

#SeeAlso mapXY mapHomogeneousPoints mapVectors

##

# ------------------------------------------------------------------------------

#Method void mapPoints(SkPoint pts[], int count) const

Maps pts Point array of length count in place. Points are mapped by multiplying
each Point by Matrix. Given:

#Code
#Literal
         | A B C |        | x |
Matrix = | D E F |,  pt = | y |
         | G H I |        | 1 |
##

where 

#Code
#Literal
for (i = 0; i < count; ++i) {
    x = pts[i].fX
    y = pts[i].fY
}
##

each resulting pts Point is computed as:

#Code
#Literal
              |A B C| |x|                               Ax+By+C   Dx+Ey+F
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
              |G H I| |1|                               Gx+Hy+I   Gx+Hy+I
##

#Param pts  storage for mapped Points ##
#Param count  number of Points to transform ##

#Example
    SkMatrix matrix;
    matrix.setRotate(35, 128, 128);
    const int count = 4;
    SkPoint pts[count];
    matrix.mapRectToQuad(pts, {40, 70, 180, 220} );
    SkPaint paint;
    paint.setARGB(77, 23, 99, 154);
    for (int i = 0; i < 5; ++i) {
        canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, pts, paint);
        matrix.mapPoints(pts, count);
    }
##

#SeeAlso mapXY mapHomogeneousPoints mapVectors

##

# ------------------------------------------------------------------------------

#Method void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const

#Line # maps Point3 array ##
Maps src Point3 array of length count to dst Point3 array, which must of length count or
greater. Point3 array is mapped by multiplying each Point3 by Matrix. Given:

#Code
#Literal
         | A B C |         | x |
Matrix = | D E F |,  src = | y |
         | G H I |         | z |
##

each resulting dst Point is computed as:

#Code
#Literal
               |A B C| |x|
Matrix * src = |D E F| |y| = |Ax+By+Cz Dx+Ey+Fz Gx+Hy+Iz|
               |G H I| |z|
##

#Param dst  storage for mapped Point3 array ##
#Param src  Point3 array to transform ##
#Param count  items in Point3 array to transform ##

#Example
    SkPoint3 src[] = {{3, 3, 1}, {8, 2, 2}, {5, 0, 4}, {0, 1, 3},
                      {3, 7, 1}, {8, 6, 2}, {5, 4, 4}, {0, 5, 3}};
    int lines[] = { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
    constexpr int count = SK_ARRAY_COUNT(src);
    auto debugster = [=](SkPoint3 src[]) -> void {
    for (size_t i = 0; i < SK_ARRAY_COUNT(lines); i += 2) {
        const SkPoint3& s = src[lines[i]];
        const SkPoint3& e = src[lines[i + 1]];
        SkPaint paint;
        paint.setARGB(77, 23, 99, 154);
        canvas->drawLine(s.fX / s.fZ, s.fY / s.fZ, e.fX / e.fZ, e.fY / e.fZ, paint);
    }
    };
    canvas->save();
    canvas->translate(5, 5);
    canvas->scale(15, 15);
    debugster(src);
    canvas->restore();
    canvas->translate(128, 128);
    SkMatrix matrix;
    matrix.setAll(15, 0, 0, 0, 15, 0, -0.08, 0.04, 1);
    matrix.mapHomogeneousPoints(src, src, count);
    debugster(src);
##

#SeeAlso mapPoints mapXY mapVectors

##

# ------------------------------------------------------------------------------

#Method void mapXY(SkScalar x, SkScalar y, SkPoint* result) const

#Line # maps Point ##
Maps Point (x, y) to result. Point is mapped by multiplying by Matrix. Given:

#Code
#Literal
         | A B C |        | x |
Matrix = | D E F |,  pt = | y |
         | G H I |        | 1 |
##

result is computed as:

#Code
#Literal
              |A B C| |x|                               Ax+By+C   Dx+Ey+F
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
              |G H I| |1|                               Gx+Hy+I   Gx+Hy+I
##

#Param x  x-coordinate of Point to map ##
#Param y  y-coordinate of Point to map ##
#Param result  storage for mapped Point ##

#Example
    SkPaint paint;
    paint.setAntiAlias(true);
    SkMatrix matrix;
    matrix.setRotate(60, 128, 128);
    SkPoint lines[] = {{50, 50}, {150, 50}, {150, 150}};
    for (size_t i = 0; i < SK_ARRAY_COUNT(lines); ++i) {
        SkPoint pt;
        matrix.mapXY(lines[i].fX, lines[i].fY, &pt);
        canvas->drawCircle(pt.fX, pt.fY, 3, paint);
    }
    canvas->concat(matrix);
    canvas->drawPoints(SkCanvas::kPolygon_PointMode, SK_ARRAY_COUNT(lines), lines, paint);
##

#SeeAlso mapPoints mapVectors

##

# ------------------------------------------------------------------------------

#Method SkPoint mapXY(SkScalar x, SkScalar y) const

Returns Point (x, y) multiplied by Matrix. Given:

#Code
#Literal
         | A B C |        | x |
Matrix = | D E F |,  pt = | y |
         | G H I |        | 1 |
##

result is computed as:

#Code
#Literal
              |A B C| |x|                               Ax+By+C   Dx+Ey+F
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
              |G H I| |1|                               Gx+Hy+I   Gx+Hy+I
##

#Param x  x-coordinate of Point to map ##
#Param y  y-coordinate of Point to map ##

#Return  mapped Point ##

#Example
#Image 4
SkMatrix matrix;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {30, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
SkPaint paint;
paint.setAntiAlias(true);
paint.setStrokeWidth(3);
for (int x : { 0, source.width() } ) {
    for (int y : { 0, source.height() } ) {
        canvas->drawPoint(matrix.mapXY(x, y), paint);
    }
}
canvas->concat(matrix);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso mapPoints mapVectors

##

# ------------------------------------------------------------------------------

#Method void mapVectors(SkVector dst[], const SkVector src[], int count) const

#Line # maps Vector array ##
Maps src Vector array of length count to Vector Point array of equal or greater
length. Vectors are mapped by multiplying each Vector by Matrix, treating
Matrix translation as zero. Given:

#Code
#Literal
         | A B 0 |         | x |
Matrix = | D E 0 |,  src = | y |
         | G H I |         | 1 |
##

where 

#Code
#Literal
for (i = 0; i < count; ++i) {
    x = src[i].fX
    y = src[i].fY
}
##

each dst Vector is computed as:

#Code
#Literal
               |A B 0| |x|                            Ax+By     Dx+Ey
Matrix * src = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , -------
               |G H I| |1|                           Gx+Hy+I   Gx+Hy+I
##

src and dst may point to the same storage.

#Param dst  storage for mapped Vectors ##
#Param src  Vectors to transform ##
#Param count  number of Vectors to transform ##

#Example
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setStyle(SkPaint::kStroke_Style);
    SkMatrix matrix;
    matrix.reset();
    const SkVector radii[] = {{8, 4}, {9, 1}, {6, 2}, {7, 3}};
    for (int i = 0; i < 4; ++i) {
        SkVector rScaled[4];
        matrix.preScale(1.5f, 2.f);
        matrix.mapVectors(rScaled, radii, SK_ARRAY_COUNT(radii));
        SkRRect rrect;
        rrect.setRectRadii({20, 20, 180, 70}, rScaled);
        canvas->drawRRect(rrect, paint);
        canvas->translate(0, 60);
    }
##

#SeeAlso mapVector mapPoints mapXY

##

# ------------------------------------------------------------------------------

#Method void mapVectors(SkVector vecs[], int count) const

Maps vecs Vector array of length count in place, multiplying each Vector by
Matrix, treating Matrix translation as zero. Given:

#Code
#Literal
         | A B 0 |         | x |
Matrix = | D E 0 |,  vec = | y |
         | G H I |         | 1 |
##

where 

#Code
#Literal
for (i = 0; i < count; ++i) {
    x = vecs[i].fX
    y = vecs[i].fY
}
##

each result Vector is computed as:

#Code
#Literal
               |A B 0| |x|                            Ax+By     Dx+Ey
Matrix * vec = |D E 0| |y| = |Ax+By Dx+Ey Gx+Hy+I| = ------- , -------
               |G H I| |1|                           Gx+Hy+I   Gx+Hy+I
##

#Param vecs  Vectors to transform, and storage for mapped Vectors ##
#Param count  number of Vectors to transform ##

#Example
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setStyle(SkPaint::kStroke_Style);
    SkMatrix matrix;
    matrix.setScale(2, 3);
    SkVector radii[] = {{7, 7}, {3, 3}, {2, 2}, {4, 0}};
    for (int i = 0; i < 4; ++i) {
        SkRRect rrect;
        rrect.setRectRadii({20, 20, 180, 70}, radii);
        canvas->drawRRect(rrect, paint);
        canvas->translate(0, 60);
        matrix.mapVectors(radii, SK_ARRAY_COUNT(radii));
    }
##

#SeeAlso mapVector mapPoints mapXY

##

# ------------------------------------------------------------------------------

#Method void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const

#Line # maps Vector ##
Maps Vector (x, y) to result. Vector is mapped by multiplying by Matrix,
treating Matrix translation as zero. Given:

#Code
#Literal
         | A B 0 |         | dx |
Matrix = | D E 0 |,  vec = | dy |
         | G H I |         |  1 |
##

each result Vector is computed as:

#Code
#Literal
#Outdent
               |A B 0| |dx|                                        A*dx+B*dy     D*dx+E*dy
Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , -----------
               |G H I| | 1|                                       G*dx+H*dy+I   G*dx+*dHy+I
##

#Param dx  x-coordinate of Vector to map ##
#Param dy  y-coordinate of Vector to map ##
#Param result  storage for mapped Vector ##

#Example
    SkPaint paint;
    paint.setColor(SK_ColorGREEN);
    paint.setAntiAlias(true);
    paint.setTextSize(48);
    SkMatrix matrix;
    matrix.setRotate(90);
    SkVector offset = { 7, 7 };
    for (int i = 0; i < 4; ++i) {
        paint.setImageFilter(SkDropShadowImageFilter::Make(offset.fX, offset.fY, 3, 3,
              SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, nullptr));
        matrix.mapVector(offset.fX, offset.fY, &offset);
        canvas->translate(0, 60);
        canvas->drawString("Text", 50, 0, paint);
    }
##

#SeeAlso mapVectors mapPoints mapXY

##

# ------------------------------------------------------------------------------

#Method SkVector mapVector(SkScalar dx, SkScalar dy) const

Returns Vector (x, y) multiplied by Matrix, treating Matrix translation as zero.
Given:

#Code
#Literal
         | A B 0 |         | dx |
Matrix = | D E 0 |,  vec = | dy |
         | G H I |         |  1 |
##

each result Vector is computed as:

#Code
#Literal
#Outdent
               |A B 0| |dx|                                        A*dx+B*dy     D*dx+E*dy
Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , -----------
               |G H I| | 1|                                       G*dx+H*dy+I   G*dx+*dHy+I
##

#Param dx  x-coordinate of Vector to map ##
#Param dy  y-coordinate of Vector to map ##

#Return  mapped Vector ##

#Example
    SkPaint paint;
    paint.setColor(SK_ColorGREEN);
    paint.setAntiAlias(true);
    paint.setTextSize(48);
    SkMatrix matrix;
    matrix.setRotate(90);
    SkVector offset = { 7, 7 };
    for (int i = 0; i < 4; ++i) {
        paint.setImageFilter(SkDropShadowImageFilter::Make(offset.fX, offset.fY, 3, 3,
              SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, nullptr));
        offset = matrix.mapVector(offset.fX, offset.fY);
        canvas->translate(0, 60);
        canvas->drawString("Text", 50, 0, paint);
    }
##

#SeeAlso mapVectors mapPoints mapXY

##

# ------------------------------------------------------------------------------

#Method bool mapRect(SkRect* dst, const SkRect& src) const

#Line # returns bounds of mapped Rect ##
Sets dst to bounds of src corners mapped by Matrix.
Returns true if mapped corners are dst corners.

Returned value is the same as calling rectStaysRect.

#Param dst  storage for bounds of mapped Points ##
#Param src  Rect to map ##

#Return  true if dst is equivalent to mapped src ##

#Example
    SkPaint paint;
    paint.setAntiAlias(true);
    SkMatrix matrix;
    matrix.setRotate(45, 128, 128);
    SkRect rotatedBounds, bounds = {40, 50, 190, 200};
    matrix.mapRect(&rotatedBounds, bounds );
    paint.setColor(SK_ColorGRAY);
    canvas->drawRect(rotatedBounds, paint);
    canvas->concat(matrix);
    paint.setColor(SK_ColorRED);
    canvas->drawRect(bounds, paint);
##

#SeeAlso mapPoints rectStaysRect

##

# ------------------------------------------------------------------------------

#Method bool mapRect(SkRect* rect) const

Sets rect to bounds of rect corners mapped by Matrix.
Returns true if mapped corners are computed rect corners.

Returned value is the same as calling rectStaysRect.

#Param rect  rectangle to map, and storage for bounds of mapped corners ##

#Return  true if result is equivalent to mapped src ##

#Example
    SkPaint paint;
    paint.setAntiAlias(true);
    SkMatrix matrix;
    matrix.setRotate(45, 128, 128);
    SkRect bounds = {40, 50, 190, 200};
    matrix.mapRect(&bounds);
    paint.setColor(SK_ColorGRAY);
    canvas->drawRect(bounds, paint);
    canvas->concat(matrix);
    paint.setColor(SK_ColorRED);
    canvas->drawRect({40, 50, 190, 200}, paint);
##

#SeeAlso mapRectScaleTranslate mapPoints rectStaysRect

##

# ------------------------------------------------------------------------------

#Method void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const

#Line # maps Rect to Point array ##
Maps four corners of rect to dst. Points are mapped by multiplying each
rect corner by Matrix. rect corner is processed in this order:
(rect.fLeft, rect.fTop), (rect.fRight, rect.fTop), (rect.fRight, rect.fBottom),
(rect.fLeft, rect.fBottom). 

rect may be empty: rect.fLeft may be greater than or equal to rect.fRight;
rect.fTop may be greater than or equal to rect.fBottom.

Given:

#Code
#Literal
         | A B C |        | x |
Matrix = | D E F |,  pt = | y |
         | G H I |        | 1 |
##

where pt is initialized from each of (rect.fLeft, rect.fTop),
(rect.fRight, rect.fTop), (rect.fRight, rect.fBottom), (rect.fLeft, rect.fBottom),
each dst Point is computed as:

#Code
#Literal
              |A B C| |x|                               Ax+By+C   Dx+Ey+F
Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , -------
              |G H I| |1|                               Gx+Hy+I   Gx+Hy+I
##

#Param dst  storage for mapped corner Points ##
#Param rect  Rect to map ##

#Example
#Height 192
    SkPaint paint;
    paint.setAntiAlias(true);
    SkMatrix matrix;
    matrix.setRotate(60, 128, 128);
    SkRect rect = {50, 50, 150, 150};
    SkPoint pts[4];
    matrix.mapRectToQuad(pts, rect);
    for (int i = 0; i < 4; ++i) {
        canvas->drawCircle(pts[i].fX, pts[i].fY, 3, paint);
    }
    canvas->concat(matrix);
    paint.setStyle(SkPaint::kStroke_Style);
    canvas->drawRect(rect, paint);
##

#SeeAlso mapRect mapRectScaleTranslate

##

# ------------------------------------------------------------------------------

#Method void mapRectScaleTranslate(SkRect* dst, const SkRect& src) const

#Line # returns bounds of mapped Rect ##
Sets dst to bounds of src corners mapped by Matrix. If matrix contains
elements other than scale or translate: asserts if SK_DEBUG is defined;
otherwise, results are undefined.

#Param dst  storage for bounds of mapped Points ##
#Param src  Rect to map ##

#Example
    SkPaint paint;
    SkMatrix matrix;
    SkRect rect = {100, 50, 150, 180};
    matrix.setScale(2, .5f, rect.centerX(), rect.centerY());
    SkRect rotated;
    matrix.mapRectScaleTranslate(&rotated, rect);
    paint.setStyle(SkPaint::kStroke_Style);
    canvas->drawRect(rect, paint);
    paint.setColor(SK_ColorRED);
    canvas->drawRect(rotated, paint);
##

#SeeAlso mapRect mapRectToQuad isScaleTranslate rectStaysRect

##

# ------------------------------------------------------------------------------

#Method SkScalar mapRadius(SkScalar radius) const

#Line # returns mean radius of mapped Circle ##
Returns geometric mean radius of ellipse formed by constructing Circle of
size radius, and mapping constructed Circle with Matrix. The result squared is
equal to the major axis length times the minor axis length.
Result is not meaningful if Matrix contains perspective elements.

#Param radius  Circle size to map ##

#Return  average mapped radius ##

#Example
#Description
The area enclosed by a square with sides equal to mappedRadius is the same as
the area enclosed by the ellipse major and minor axes.
##
  SkPaint paint;
  paint.setAntiAlias(true);
  SkMatrix matrix;
  const SkPoint center = {108, 93};
  matrix.setScale(2, .5f, center.fX, center.fY);
  matrix.postRotate(45, center.fX, center.fY);
  const SkScalar circleRadius = 50;
  SkScalar mappedRadius = matrix.mapRadius(circleRadius);
  SkVector minorAxis, majorAxis;
  matrix.mapVector(0, circleRadius, &minorAxis);
  matrix.mapVector(circleRadius, 0, &majorAxis);
  SkString mappedArea;
  mappedArea.printf("area = %g", mappedRadius * mappedRadius);
  canvas->drawString(mappedArea, 145, 250, paint);
  canvas->drawString("mappedRadius", center.fX + mappedRadius + 3, center.fY, paint);
  paint.setColor(SK_ColorRED);
  SkString axArea;
  axArea.printf("area = %g", majorAxis.length() * minorAxis.length());
  paint.setStyle(SkPaint::kFill_Style);
  canvas->drawString(axArea, 15, 250, paint);
  paint.setStyle(SkPaint::kStroke_Style);
  canvas->drawRect({10, 200, 10 + majorAxis.length(), 200 + minorAxis.length()}, paint);
  paint.setColor(SK_ColorBLACK);
  canvas->drawLine(center.fX, center.fY, center.fX + mappedRadius, center.fY, paint);
  canvas->drawLine(center.fX, center.fY, center.fX, center.fY + mappedRadius, paint);
  canvas->drawRect({140, 180, 140 + mappedRadius, 180 + mappedRadius}, paint);
  canvas->concat(matrix);
  canvas->drawCircle(center.fX, center.fY, circleRadius, paint);
  paint.setColor(SK_ColorRED);
  canvas->drawLine(center.fX, center.fY, center.fX + circleRadius, center.fY, paint);
  canvas->drawLine(center.fX, center.fY, center.fX, center.fY + circleRadius, paint);
##

#SeeAlso mapVector

##

# ------------------------------------------------------------------------------

#Method bool isFixedStepInX() const

#Line # returns if transformation supports fixed step in x ##
Returns true if a unit step in x at some y mapped through Matrix can be
represented by a constant Vector. Returns true if getType returns kIdentity_Mask,
or combinations of: kTranslate_Mask, kScale_Mask, and kAffine_Mask.

May return true if getType returns kPerspective_Mask, but only when Matrix
does not include rotation or skewing along the y-axis.

#Return  true if Matrix does not have complex perspective ##

#Example
    SkMatrix matrix;
    for (SkScalar px : { 0.0f, 0.1f } ) {
        for (SkScalar py : { 0.0f, 0.1f } ) {
            for (SkScalar sy : { 1, 2 } ) {
                matrix.setAll(1, 0, 0,   0, sy, 0,   px, py, 1);
                matrix.dump();
                SkDebugf("isFixedStepInX: %s\n", matrix.isFixedStepInX() ? "true" : "false");
            }
        }
    }
#StdOut
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
isFixedStepInX: true
[  1.0000   0.0000   0.0000][  0.0000   2.0000   0.0000][  0.0000   0.0000   1.0000]
isFixedStepInX: true
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.0000   0.1000   1.0000]
isFixedStepInX: true
[  1.0000   0.0000   0.0000][  0.0000   2.0000   0.0000][  0.0000   0.1000   1.0000]
isFixedStepInX: true
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.1000   0.0000   1.0000]
isFixedStepInX: false
[  1.0000   0.0000   0.0000][  0.0000   2.0000   0.0000][  0.1000   0.0000   1.0000]
isFixedStepInX: false
[  1.0000   0.0000   0.0000][  0.0000   1.0000   0.0000][  0.1000   0.1000   1.0000]
isFixedStepInX: false
[  1.0000   0.0000   0.0000][  0.0000   2.0000   0.0000][  0.1000   0.1000   1.0000]
isFixedStepInX: false
##
##

#SeeAlso fixedStepInX getType

##

# ------------------------------------------------------------------------------

#Method SkVector fixedStepInX(SkScalar y) const

#Line # returns step in x for a position in y ##
Returns Vector representing a unit step in x at y mapped through Matrix.
If isFixedStepInX is false, returned value is undefined.

#Param y  position of line parallel to x-axis ##

#Return  Vector advance of mapped unit step in x ##

#Example
#Image 3
    SkMatrix matrix;
    const SkPoint center = { 128, 128 };
    matrix.setScale(20, 25, center.fX, center.fY);
    matrix.postRotate(75, center.fX, center.fY);
    {
       SkAutoCanvasRestore acr(canvas, true);
       canvas->concat(matrix);
       canvas->drawBitmap(source, 0, 0);
    }
    if (matrix.isFixedStepInX()) {
       SkPaint paint;
       paint.setAntiAlias(true);
       SkVector step = matrix.fixedStepInX(128);
       SkVector end = center + step;
       canvas->drawLine(center, end, paint);
       SkVector arrow = { step.fX + step.fY, step.fY - step.fX};
       arrow = arrow * .25f;
       canvas->drawLine(end, end - arrow, paint);
       canvas->drawLine(end, {end.fX + arrow.fY, end.fY - arrow.fX}, paint);
    }
##

#SeeAlso isFixedStepInX getType

##

# ------------------------------------------------------------------------------

#Method bool cheapEqualTo(const SkMatrix& m) const

#Line # compares Matrix pair using memcmp() ##
Returns true if Matrix equals m, using an efficient comparison.

Returns false when the sign of zero values is the different; when one
matrix has positive zero value and the other has negative zero value.

Returns true even when both Matrices contain NaN.

NaN never equals any value, including itself. To improve performance, NaN values
are treated as bit patterns that are equal if their bit patterns are equal.

#Param m  Matrix to compare ##

#Return  true if m and Matrix are represented by identical bit patterns ##

#Example
    auto debugster = [](const char* prefix, const SkMatrix& a, const SkMatrix& b) -> void {
        SkDebugf("%s: a %c= b a.cheapEqualTo(b): %s\n", prefix,
                 a == b ? '=' : '!', a.cheapEqualTo(b) ? "true" : "false");
    };
    SkMatrix a, b;
    a.setAll(1, 0, 0,   0, 1, 0,  0, 0, 1);
    b.setIdentity();
    debugster("identity", a, b);
    a.setAll(1, -0.0f, 0,   0, 1, 0,  0, 0, 1);
    debugster("neg zero", a, b);
    a.setAll(1, SK_ScalarNaN, 0,   0, 1, 0,  0, 0, 1);
    debugster(" one NaN", a, b);
    b.setAll(1, SK_ScalarNaN, 0,   0, 1, 0,  0, 0, 1);
    debugster("both NaN", a, b);
#StdOut
identity: a == b a.cheapEqualTo(b): true
neg zero: a == b a.cheapEqualTo(b): false
 one NaN: a != b a.cheapEqualTo(b): false
both NaN: a != b a.cheapEqualTo(b): true
##
##

#SeeAlso operator==(const SkMatrix& a, const SkMatrix& b)

##

# ------------------------------------------------------------------------------

#Method bool operator==(const SkMatrix& a, const SkMatrix& b)

#Line # returns true if members are equal ##
Compares a and b; returns true if a and b are numerically equal. Returns true
even if sign of zero values are different. Returns false if either Matrix
contains NaN, even if the other Matrix also contains NaN.

#Param a  Matrix to compare ##
#Param b  Matrix to compare ##

#Return  true if m and Matrix are numerically equal ##

#Example
    auto debugster = [](const char* prefix, const SkMatrix& a, const SkMatrix& b) -> void {
        SkDebugf("%s: a %c= b a.cheapEqualTo(b): %s\n", prefix,
                 a == b ? '=' : '!', a.cheapEqualTo(b) ? "true" : "false");
    };
    SkMatrix a, b;
    a.setAll(1, 0, 0,   0, 1, 0,  0, 0, 1);
    b.setScale(2, 4);
    b.postScale(0.5f, 0.25f);
    debugster("identity", a, b);
#StdOut
identity: a == b a.cheapEqualTo(b): true
##
##

#SeeAlso  cheapEqualTo operator!=(const SkMatrix& a, const SkMatrix& b)

##

# ------------------------------------------------------------------------------

#Method bool operator!=(const SkMatrix& a, const SkMatrix& b)

#Line # returns true if members are unequal ##
Compares a and b; returns true if a and b are not numerically equal. Returns false
even if sign of zero values are different. Returns true if either Matrix
contains NaN, even if the other Matrix also contains NaN.

#Param a  Matrix to compare ##
#Param b  Matrix to compare ##

#Return  true if m and Matrix are numerically not equal ##

#Example
    auto debugster = [](const char* prefix, const SkMatrix& a, const SkMatrix& b) -> void {
        SkDebugf("%s: a %c= b a.cheapEqualTo(b): %s\n", prefix,
                 a != b ? '!' : '=', a.cheapEqualTo(b) ? "true" : "false");
    };
    SkMatrix a, b;
    a.setAll(1, 0, 0,   0, 1, 0,  1, 0, 1);
    a.invert(&b);
    debugster("identity", a, b);
##

#SeeAlso cheapEqualTo operator==(const SkMatrix& a, const SkMatrix& b)

##

# ------------------------------------------------------------------------------

#Method void dump() const

#Line # sends text representation using floats to standard output ##
Writes text representation of Matrix to standard output. Floating point values
are written with limited precision; it may not be possible to reconstruct
original Matrix from output.

#Example
    SkMatrix matrix;
    matrix.setRotate(45);
    matrix.dump();
    SkMatrix nearlyEqual;
    nearlyEqual.setAll(0.7071f, -0.7071f, 0,   0.7071f, 0.7071f, 0,   0, 0, 1);
    nearlyEqual.dump();
    SkDebugf("matrix %c= nearlyEqual\n", matrix == nearlyEqual ? '=' : '!');
#StdOut
[  0.7071  -0.7071   0.0000][  0.7071   0.7071   0.0000][  0.0000   0.0000   1.0000]
[  0.7071  -0.7071   0.0000][  0.7071   0.7071   0.0000][  0.0000   0.0000   1.0000]
matrix != nearlyEqual
##
##

#SeeAlso toString

##

# ------------------------------------------------------------------------------

#Method void toString(SkString* str) const

#Line # converts Matrix to machine readable form ##
Creates string representation of Matrix. Floating point values
are written with limited precision; it may not be possible to reconstruct
original Matrix from output.

#Param str  storage for string representation of Matrix ##

#Example
    SkMatrix matrix;
    matrix.setRotate(45);
    SkString mStr, neStr;
    matrix.toString(&mStr);
    SkMatrix nearlyEqual;
    nearlyEqual.setAll(0.7071f, -0.7071f, 0,   0.7071f, 0.7071f, 0,   0, 0, 1);
    nearlyEqual.toString(&neStr);
    SkDebugf("mStr  %s\n", mStr.c_str());
    SkDebugf("neStr %s\n", neStr.c_str());
    SkDebugf("matrix %c= nearlyEqual\n", matrix == nearlyEqual ? '=' : '!');
#StdOut
mStr  [  0.7071  -0.7071   0.0000][  0.7071   0.7071   0.0000][  0.0000   0.0000   1.0000]
neStr [  0.7071  -0.7071   0.0000][  0.7071   0.7071   0.0000][  0.0000   0.0000   1.0000]
matrix != nearlyEqual
##
##

#SeeAlso dump

##

# ------------------------------------------------------------------------------

#Method SkScalar getMinScale() const

#Line # returns minimum scaling, if possible ##
Returns the minimum scaling factor of Matrix by decomposing the scaling and
skewing elements.
Returns -1 if scale factor overflows or Matrix contains perspective.

#Return  minimum scale factor
##

#Example
    SkMatrix matrix;
    matrix.setScale(42, 24);
    SkDebugf("matrix.getMinScale() %g\n", matrix.getMinScale());
#StdOut
matrix.getMinScale() 24
##
##

#SeeAlso getMaxScale getMinMaxScales

##

# ------------------------------------------------------------------------------

#Method SkScalar getMaxScale() const

#Line # returns maximum scaling, if possible ##
Returns the maximum scaling factor of Matrix by decomposing the scaling and
skewing elements.
Returns -1 if scale factor overflows or Matrix contains perspective.

#Return  maximum scale factor
##

#Example
    SkMatrix matrix;
    matrix.setScale(42, 24);
    SkDebugf("matrix.getMaxScale() %g\n", matrix.getMaxScale());
#StdOut
matrix.getMaxScale() 42
##
##

#SeeAlso getMinScale getMinMaxScales

##

# ------------------------------------------------------------------------------

#Method bool SK_WARN_UNUSED_RESULT getMinMaxScales(SkScalar scaleFactors[2]) const

#Line # returns minimum and maximum scaling, if possible ##
Sets scaleFactors[0] to the minimum scaling factor, and scaleFactors[1] to the 
maximum scaling factor. Scaling factors are computed by decomposing
the Matrix scaling and skewing elements.

Returns true if scaleFactors are found; otherwise, returns false and sets
scaleFactors to undefined values.

#Param scaleFactors  storage for minimum and maximum scale factors ##

#Return  true if scale factors were computed correctly ##

#Example
    SkMatrix matrix;
    matrix.setAll(1, 0, 0,  0, 1, 0,   0, 0, 0);
    matrix.invert(&matrix); 
    SkScalar factor[2] = {2, 2};
    bool result = matrix.getMinMaxScales(factor);
    SkDebugf("matrix.getMinMaxScales() %s %g %g\n", result ? "true" : "false", factor[0], factor[1]);
#StdOut
matrix.getMinMaxScales() false 2 2
##
##

#SeeAlso getMinScale getMaxScale

##

# ------------------------------------------------------------------------------

#Method bool decomposeScale(SkSize* scale, SkMatrix* remaining = nullptr) const

#Line # separates scale if possible ##
Decomposes Matrix into scale components and whatever remains. Returns false if
Matrix could not be decomposed.

Sets scale to portion of Matrix that scales in x and y. Sets remaining to Matrix
with x and y scaling factored out. remaining may be passed as nullptr
to determine if Matrix can be decomposed without computing remainder.

Returns true if scale components are found. scale and remaining are
unchanged if Matrix contains perspective; scale factors are not finite, or
are nearly zero. 

On success

#Formula
Matrix = scale * Remaining
##

#Param scale  x and y scaling factors; may be nullptr  ##
#Param remaining  Matrix without scaling; may be nullptr ##

#Return  true if scale can be computed ##

#Example
    SkMatrix matrix;
    matrix.setRotate(90 * SK_Scalar1);
    matrix.postScale(1.f / 4, 1.f / 2);
    matrix.dump();
    SkSize scale = {SK_ScalarNaN, SK_ScalarNaN};
    SkMatrix remaining;
    remaining.reset();
    bool success = matrix.decomposeScale(&scale, &remaining);
    SkDebugf("success: %s  ", success ? "true" : "false");
    SkDebugf("scale: %g, %g\n", scale.width(), scale.height());
    remaining.dump();
    SkMatrix scaleMatrix = SkMatrix::MakeScale(scale.width(), scale.height());
    SkMatrix combined = SkMatrix::Concat(scaleMatrix, remaining);
    combined.dump();
#StdOut
[  0.0000  -0.2500   0.0000][  0.5000   0.0000   0.0000][  0.0000   0.0000   1.0000]
success: true  scale: 0.5, 0.25
[  0.0000  -0.5000   0.0000][  2.0000   0.0000   0.0000][  0.0000   0.0000   1.0000]
[  0.0000  -0.2500   0.0000][  0.5000   0.0000   0.0000][  0.0000   0.0000   1.0000]
##
##

#SeeAlso setScale MakeScale

##

# ------------------------------------------------------------------------------

#Method static const SkMatrix& I()

#Line # returns a reference to a const identity Matrix ##
Returns reference to const identity Matrix. Returned Matrix is set to:

#Code
#Literal
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
##

#Return  const identity Matrix ##

#Example
    SkMatrix m1, m2, m3;
    m1.reset();
    m2.setIdentity();
    m3 = SkMatrix::I();
    SkDebugf("m1 %c= m2\n", m1 == m2 ? '=' : '!');
    SkDebugf("m2 %c= m3\n", m1 == m2 ? '=' : '!');
#StdOut
m1 == m2
m2 == m3
##
##

#SeeAlso reset() setIdentity

##

# ------------------------------------------------------------------------------

#Method static const SkMatrix& InvalidMatrix()

#Line # returns a reference to a const invalid Matrix ##
Returns reference to a const Matrix with invalid values. Returned Matrix is set
to:

#Code
#Literal
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
| SK_ScalarMax SK_ScalarMax SK_ScalarMax |
##

#Return  const invalid Matrix ##

#Example
    SkDebugf("scaleX %g\n", SkMatrix::InvalidMatrix().getScaleX());
#StdOut
scaleX 3.40282e+38
##
##

#SeeAlso SeeAlso getType

##

# ------------------------------------------------------------------------------

#Method static SkMatrix Concat(const SkMatrix& a, const SkMatrix& b)

#Line # returns the concatenation of Matrix pair ##
Returns Matrix a multiplied by Matrix b.

Given:

#Code
#Literal
    | A B C |      | J K L |
a = | D E F |, b = | M N O |
    | G H I |      | P Q R |
##

sets Matrix to:

#Code
#Literal
        | A B C |   | J K L |   | AJ+BM+CP AK+BN+CQ AL+BO+CR |
a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR |
        | G H I |   | P Q R |   | GJ+HM+IP GK+HN+IQ GL+HO+IR |
##

#Param a  Matrix on left side of multiply expression ##
#Param b  Matrix on right side of multiply expression ##

#Return  Matrix computed from a times b ##

#Example
#Height 64
#Image 4
#Description
setPolyToPoly creates perspective matrices, one the inverse of the other.
Multiplying the matrix by its inverse turns into an identity matrix.
##
SkMatrix matrix, matrix2;
SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}};
SkRect::Make(source.bounds()).toQuad(bitmapBounds);
matrix.setPolyToPoly(bitmapBounds, perspect, 4);
matrix2.setPolyToPoly(perspect, bitmapBounds, 4);
SkMatrix concat = SkMatrix::Concat(matrix, matrix2);
canvas->concat(concat);
canvas->drawBitmap(source, 0, 0);
##

#SeeAlso preConcat postConcat

##

# ------------------------------------------------------------------------------

#Method void dirtyMatrixTypeCache()

#Line # sets internal cache to unknown state ##
Sets internal cache to unknown state. Use to force update after repeated 
modifications to Matrix element reference returned by operator[](int index).

#Example
SkMatrix matrix;
matrix.setIdentity();
SkDebugf("with identity matrix: x = %g\n", matrix.mapXY(24, 42).fX);
SkScalar& skewRef = matrix[SkMatrix::kMSkewX];
skewRef = 0;
SkDebugf("after skew x mod:     x = %g\n", matrix.mapXY(24, 42).fX);
skewRef = 1;
SkDebugf("after 2nd skew x mod: x = %g\n", matrix.mapXY(24, 42).fX);
matrix.dirtyMatrixTypeCache();
SkDebugf("after dirty cache:    x = %g\n", matrix.mapXY(24, 42).fX);
#StdOut
with identity matrix: x = 24
after skew x mod:     x = 24
after 2nd skew x mod: x = 24
after dirty cache:    x = 66
##
##

#SeeAlso operator[](int index) getType

##

# ------------------------------------------------------------------------------

#Method void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty)

#Line # sets to scale and translate ##
Initializes Matrix with scale and translate elements.

#Code
#Literal
| sx  0 tx |
|  0 sy ty |
|  0  0  1 |
##

#Param sx  horizontal scale factor to store ##
#Param sy  vertical scale factor to store ##
#Param tx  horizontal translation to store ##
#Param ty  vertical translation to store ##

#Example
SkMatrix matrix;
matrix.setScaleTranslate(1, 2, 3, 4);
matrix.dump();
#StdOut
[  1.0000   0.0000   3.0000][  0.0000   2.0000   4.0000][  0.0000   0.0000   1.0000]
##
##

#SeeAlso setScale preTranslate postTranslate

##

# ------------------------------------------------------------------------------

#Method bool isFinite() const

#Line # returns if all Matrix values are not infinity, NaN ##
Returns true if all elements of the matrix are finite. Returns false if any
element is infinity, or NaN.

#Return  true if matrix has only finite elements ##

#Example
SkMatrix matrix = SkMatrix::MakeTrans(SK_ScalarNaN, 0);
matrix.dump();
SkDebugf("matrix is finite: %s\n", matrix.isFinite() ? "true" : "false");
SkDebugf("matrix %c= matrix\n", matrix == matrix ? '=' : '!');
#StdOut
[  1.0000   0.0000      nan][  0.0000   1.0000   0.0000][  0.0000   0.0000   1.0000]
matrix is finite: false
matrix != matrix
##
##

#SeeAlso operator==

##

#Class SkMatrix ##

#Topic Matrix ##