cc4dac3dac
Review URL: http://codereview.appspot.com/4538043/ Checked in on behalf of reed@ with some additional work (remove the do-nother sk->gr matrix converter). git-svn-id: http://skia.googlecode.com/svn/trunk@1289 2bbb7eff-a529-9590-31e7-b0007b416f81
547 lines
20 KiB
C++
547 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef SkMatrix_DEFINED
|
|
#define SkMatrix_DEFINED
|
|
|
|
#include "SkRect.h"
|
|
|
|
class SkString;
|
|
|
|
/** \class SkMatrix
|
|
|
|
The SkMatrix class holds a 3x3 matrix for transforming coordinates.
|
|
SkMatrix does not have a constructor, so it must be explicitly initialized
|
|
using either reset() - to construct an identity matrix, or one of the set
|
|
functions (e.g. setTranslate, setRotate, etc.).
|
|
*/
|
|
class SK_API SkMatrix {
|
|
public:
|
|
/** Enum of bit fields for the mask return by getType().
|
|
Use this to identify the complexity of the matrix.
|
|
*/
|
|
enum TypeMask {
|
|
kIdentity_Mask = 0,
|
|
kTranslate_Mask = 0x01, //!< set if the matrix has translation
|
|
kScale_Mask = 0x02, //!< set if the matrix has X or Y scale
|
|
kAffine_Mask = 0x04, //!< set if the matrix skews or rotates
|
|
kPerspective_Mask = 0x08 //!< set if the matrix is in perspective
|
|
};
|
|
|
|
/** Returns a mask bitfield describing the types of transformations
|
|
that the matrix will perform. This information is used by routines
|
|
like mapPoints, to optimize its inner loops to only perform as much
|
|
arithmetic as is necessary.
|
|
*/
|
|
TypeMask getType() const {
|
|
if (fTypeMask & kUnknown_Mask) {
|
|
fTypeMask = this->computeTypeMask();
|
|
}
|
|
// only return the public masks
|
|
return (TypeMask)(fTypeMask & 0xF);
|
|
}
|
|
|
|
/** Returns true if the matrix is identity.
|
|
*/
|
|
bool isIdentity() const {
|
|
return this->getType() == 0;
|
|
}
|
|
|
|
/** Returns true if will map a rectangle to another rectangle. This can be
|
|
true if the matrix is identity, scale-only, or rotates a multiple of
|
|
90 degrees.
|
|
*/
|
|
bool rectStaysRect() const {
|
|
if (fTypeMask & kUnknown_Mask) {
|
|
fTypeMask = this->computeTypeMask();
|
|
}
|
|
return (fTypeMask & kRectStaysRect_Mask) != 0;
|
|
}
|
|
// alias for rectStaysRect()
|
|
bool preservesAxisAlignment() const { return this->rectStaysRect(); }
|
|
|
|
/**
|
|
* Returns true if the perspective contains perspective elements.
|
|
*/
|
|
bool hasPerspective() const {
|
|
return SkToBool(this->getType() & kPerspective_Mask);
|
|
}
|
|
|
|
enum {
|
|
kMScaleX,
|
|
kMSkewX,
|
|
kMTransX,
|
|
kMSkewY,
|
|
kMScaleY,
|
|
kMTransY,
|
|
kMPersp0,
|
|
kMPersp1,
|
|
kMPersp2
|
|
};
|
|
|
|
SkScalar operator[](int index) const {
|
|
SkASSERT((unsigned)index < 9);
|
|
return fMat[index];
|
|
}
|
|
|
|
SkScalar get(int index) const {
|
|
SkASSERT((unsigned)index < 9);
|
|
return fMat[index];
|
|
}
|
|
|
|
SkScalar getScaleX() const { return fMat[kMScaleX]; }
|
|
SkScalar getScaleY() const { return fMat[kMScaleY]; }
|
|
SkScalar getSkewY() const { return fMat[kMSkewY]; }
|
|
SkScalar getSkewX() const { return fMat[kMSkewX]; }
|
|
SkScalar getTranslateX() const { return fMat[kMTransX]; }
|
|
SkScalar getTranslateY() const { return fMat[kMTransY]; }
|
|
SkScalar getPerspX() const { return fMat[kMPersp0]; }
|
|
SkScalar getPerspY() const { return fMat[kMPersp1]; }
|
|
|
|
SkScalar& operator[](int index) {
|
|
SkASSERT((unsigned)index < 9);
|
|
this->setTypeMask(kUnknown_Mask);
|
|
return fMat[index];
|
|
}
|
|
|
|
void set(int index, SkScalar value) {
|
|
SkASSERT((unsigned)index < 9);
|
|
fMat[index] = value;
|
|
this->setTypeMask(kUnknown_Mask);
|
|
}
|
|
|
|
void setScaleX(SkScalar v) { this->set(kMScaleX, v); }
|
|
void setScaleY(SkScalar v) { this->set(kMScaleY, v); }
|
|
void setSkewY(SkScalar v) { this->set(kMSkewY, v); }
|
|
void setSkewX(SkScalar v) { this->set(kMSkewX, v); }
|
|
void setTranslateX(SkScalar v) { this->set(kMTransX, v); }
|
|
void setTranslateY(SkScalar v) { this->set(kMTransY, v); }
|
|
void setPerspX(SkScalar v) { this->set(kMPersp0, v); }
|
|
void setPerspY(SkScalar v) { this->set(kMPersp1, v); }
|
|
|
|
void setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
|
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
|
SkScalar persp0, SkScalar persp1, SkScalar persp2) {
|
|
fMat[kMScaleX] = scaleX;
|
|
fMat[kMSkewX] = skewX;
|
|
fMat[kMTransX] = transX;
|
|
fMat[kMSkewY] = skewY;
|
|
fMat[kMScaleY] = scaleY;
|
|
fMat[kMTransY] = transY;
|
|
fMat[kMPersp0] = persp0;
|
|
fMat[kMPersp1] = persp1;
|
|
fMat[kMPersp2] = persp2;
|
|
this->setTypeMask(kUnknown_Mask);
|
|
}
|
|
|
|
/** Set the matrix to identity
|
|
*/
|
|
void reset();
|
|
// alias for reset()
|
|
void setIdentity() { this->reset(); }
|
|
|
|
/** Set the matrix to translate by (dx, dy).
|
|
*/
|
|
void setTranslate(SkScalar dx, SkScalar dy);
|
|
/** Set the matrix to scale by sx and sy, with a pivot point at (px, py).
|
|
The pivot point is the coordinate that should remain unchanged by the
|
|
specified transformation.
|
|
*/
|
|
void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
/** Set the matrix to scale by sx and sy.
|
|
*/
|
|
void setScale(SkScalar sx, SkScalar sy);
|
|
/** Set the matrix to rotate by the specified number of degrees, with a
|
|
pivot point at (px, py). The pivot point is the coordinate that should
|
|
remain unchanged by the specified transformation.
|
|
*/
|
|
void setRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
/** Set the matrix to rotate about (0,0) by the specified number of degrees.
|
|
*/
|
|
void setRotate(SkScalar degrees);
|
|
/** Set the matrix to rotate by the specified sine and cosine values, with
|
|
a pivot point at (px, py). The pivot point is the coordinate that
|
|
should remain unchanged by the specified transformation.
|
|
*/
|
|
void setSinCos(SkScalar sinValue, SkScalar cosValue,
|
|
SkScalar px, SkScalar py);
|
|
/** Set the matrix to rotate by the specified sine and cosine values.
|
|
*/
|
|
void setSinCos(SkScalar sinValue, SkScalar cosValue);
|
|
/** Set the matrix to skew by sx and sy, with a pivot point at (px, py).
|
|
The pivot point is the coordinate that should remain unchanged by the
|
|
specified transformation.
|
|
*/
|
|
void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
/** Set the matrix to skew by sx and sy.
|
|
*/
|
|
void setSkew(SkScalar kx, SkScalar ky);
|
|
/** Set the matrix to the concatenation of the two specified matrices,
|
|
returning true if the the result can be represented. Either of the
|
|
two matrices may also be the target matrix. *this = a * b;
|
|
*/
|
|
bool setConcat(const SkMatrix& a, const SkMatrix& b);
|
|
|
|
/** Preconcats the matrix with the specified translation.
|
|
M' = M * T(dx, dy)
|
|
*/
|
|
bool preTranslate(SkScalar dx, SkScalar dy);
|
|
/** Preconcats the matrix with the specified scale.
|
|
M' = M * S(sx, sy, px, py)
|
|
*/
|
|
bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
/** Preconcats the matrix with the specified scale.
|
|
M' = M * S(sx, sy)
|
|
*/
|
|
bool preScale(SkScalar sx, SkScalar sy);
|
|
/** Preconcats the matrix with the specified rotation.
|
|
M' = M * R(degrees, px, py)
|
|
*/
|
|
bool preRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
/** Preconcats the matrix with the specified rotation.
|
|
M' = M * R(degrees)
|
|
*/
|
|
bool preRotate(SkScalar degrees);
|
|
/** Preconcats the matrix with the specified skew.
|
|
M' = M * K(kx, ky, px, py)
|
|
*/
|
|
bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
/** Preconcats the matrix with the specified skew.
|
|
M' = M * K(kx, ky)
|
|
*/
|
|
bool preSkew(SkScalar kx, SkScalar ky);
|
|
/** Preconcats the matrix with the specified matrix.
|
|
M' = M * other
|
|
*/
|
|
bool preConcat(const SkMatrix& other);
|
|
|
|
/** Postconcats the matrix with the specified translation.
|
|
M' = T(dx, dy) * M
|
|
*/
|
|
bool postTranslate(SkScalar dx, SkScalar dy);
|
|
/** Postconcats the matrix with the specified scale.
|
|
M' = S(sx, sy, px, py) * M
|
|
*/
|
|
bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
|
|
/** Postconcats the matrix with the specified scale.
|
|
M' = S(sx, sy) * M
|
|
*/
|
|
bool postScale(SkScalar sx, SkScalar sy);
|
|
/** Postconcats the matrix by dividing it by the specified integers.
|
|
M' = S(1/divx, 1/divy, 0, 0) * M
|
|
*/
|
|
bool postIDiv(int divx, int divy);
|
|
/** Postconcats the matrix with the specified rotation.
|
|
M' = R(degrees, px, py) * M
|
|
*/
|
|
bool postRotate(SkScalar degrees, SkScalar px, SkScalar py);
|
|
/** Postconcats the matrix with the specified rotation.
|
|
M' = R(degrees) * M
|
|
*/
|
|
bool postRotate(SkScalar degrees);
|
|
/** Postconcats the matrix with the specified skew.
|
|
M' = K(kx, ky, px, py) * M
|
|
*/
|
|
bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
|
|
/** Postconcats the matrix with the specified skew.
|
|
M' = K(kx, ky) * M
|
|
*/
|
|
bool postSkew(SkScalar kx, SkScalar ky);
|
|
/** Postconcats the matrix with the specified matrix.
|
|
M' = other * M
|
|
*/
|
|
bool postConcat(const SkMatrix& other);
|
|
|
|
enum ScaleToFit {
|
|
/**
|
|
* Scale in X and Y independently, so that src matches dst exactly.
|
|
* This may change the aspect ratio of the src.
|
|
*/
|
|
kFill_ScaleToFit,
|
|
/**
|
|
* Compute a scale that will maintain the original src aspect ratio,
|
|
* but will also ensure that src fits entirely inside dst. At least one
|
|
* axis (X or Y) will fit exactly. kStart aligns the result to the
|
|
* left and top edges of dst.
|
|
*/
|
|
kStart_ScaleToFit,
|
|
/**
|
|
* Compute a scale that will maintain the original src aspect ratio,
|
|
* but will also ensure that src fits entirely inside dst. At least one
|
|
* axis (X or Y) will fit exactly. The result is centered inside dst.
|
|
*/
|
|
kCenter_ScaleToFit,
|
|
/**
|
|
* Compute a scale that will maintain the original src aspect ratio,
|
|
* but will also ensure that src fits entirely inside dst. At least one
|
|
* axis (X or Y) will fit exactly. kEnd aligns the result to the
|
|
* right and bottom edges of dst.
|
|
*/
|
|
kEnd_ScaleToFit
|
|
};
|
|
|
|
/** Set the matrix to the scale and translate values that map the source
|
|
rectangle to the destination rectangle, returning true if the the result
|
|
can be represented.
|
|
@param src the source rectangle to map from.
|
|
@param dst the destination rectangle to map to.
|
|
@param stf the ScaleToFit option
|
|
@return true if the matrix can be represented by the rectangle mapping.
|
|
*/
|
|
bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf);
|
|
|
|
/** Set the matrix such that the specified src points would map to the
|
|
specified dst points. count must be within [0..4].
|
|
@param src The array of src points
|
|
@param dst The array of dst points
|
|
@param count The number of points to use for the transformation
|
|
@return true if the matrix was set to the specified transformation
|
|
*/
|
|
bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count);
|
|
|
|
/** If this matrix can be inverted, return true and if inverse is not null,
|
|
set inverse to be the inverse of this matrix. If this matrix cannot be
|
|
inverted, ignore inverse and return false
|
|
*/
|
|
bool invert(SkMatrix* inverse) const;
|
|
|
|
/** Fills the passed array with the tranform values in the right order
|
|
for PDFs. If the matrix is a perspective transform, returns false
|
|
and fills the array with an identity transform.
|
|
@param transform The array to fill in.
|
|
*/
|
|
bool pdfTransform(SkScalar transform[6]) const;
|
|
|
|
/** Apply this matrix to the array of points specified by src, and write
|
|
the transformed points into the array of points specified by dst.
|
|
dst[] = M * src[]
|
|
@param dst Where the transformed coordinates are written. It must
|
|
contain at least count entries
|
|
@param src The original coordinates that are to be transformed. It
|
|
must contain at least count entries
|
|
@param count The number of points in src to read, and then transform
|
|
into dst.
|
|
*/
|
|
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
|
|
|
|
/** Apply this matrix to the array of points, overwriting it with the
|
|
transformed values.
|
|
dst[] = M * pts[]
|
|
@param pts The points to be transformed. It must contain at least
|
|
count entries
|
|
@param count The number of points in pts.
|
|
*/
|
|
void mapPoints(SkPoint pts[], int count) const {
|
|
this->mapPoints(pts, pts, count);
|
|
}
|
|
|
|
void mapXY(SkScalar x, SkScalar y, SkPoint* result) const {
|
|
SkASSERT(result);
|
|
this->getMapXYProc()(*this, x, y, result);
|
|
}
|
|
|
|
/** Apply this matrix to the array of vectors specified by src, and write
|
|
the transformed vectors into the array of vectors specified by dst.
|
|
This is similar to mapPoints, but ignores any translation in the matrix.
|
|
@param dst Where the transformed coordinates are written. It must
|
|
contain at least count entries
|
|
@param src The original coordinates that are to be transformed. It
|
|
must contain at least count entries
|
|
@param count The number of vectors in src to read, and then transform
|
|
into dst.
|
|
*/
|
|
void mapVectors(SkVector dst[], const SkVector src[], int count) const;
|
|
|
|
/** Apply this matrix to the array of vectors specified by src, and write
|
|
the transformed vectors into the array of vectors specified by dst.
|
|
This is similar to mapPoints, but ignores any translation in the matrix.
|
|
@param vecs The vectors to be transformed. It must contain at least
|
|
count entries
|
|
@param count The number of vectors in vecs.
|
|
*/
|
|
void mapVectors(SkVector vecs[], int count) const {
|
|
this->mapVectors(vecs, vecs, count);
|
|
}
|
|
|
|
/** Apply this matrix to the src rectangle, and write the transformed
|
|
rectangle into dst. This is accomplished by transforming the 4 corners
|
|
of src, and then setting dst to the bounds of those points.
|
|
@param dst Where the transformed rectangle is written.
|
|
@param src The original rectangle to be transformed.
|
|
@return the result of calling rectStaysRect()
|
|
*/
|
|
bool mapRect(SkRect* dst, const SkRect& src) const;
|
|
|
|
/** Apply this matrix to the rectangle, and write the transformed rectangle
|
|
back into it. This is accomplished by transforming the 4 corners of
|
|
rect, and then setting it to the bounds of those points
|
|
@param rect The rectangle to transform.
|
|
@return the result of calling rectStaysRect()
|
|
*/
|
|
bool mapRect(SkRect* rect) const {
|
|
return this->mapRect(rect, *rect);
|
|
}
|
|
|
|
void mapPointsWithStride(SkPoint pts[], size_t stride, int count) const {
|
|
for (int i = 0; i < count; ++i) {
|
|
this->mapPoints(pts, pts, 1);
|
|
pts = (SkPoint*)((intptr_t)pts + stride);
|
|
}
|
|
}
|
|
|
|
/** Return the mean radius of a circle after it has been mapped by
|
|
this matrix. NOTE: in perspective this value assumes the circle
|
|
has its center at the origin.
|
|
*/
|
|
SkScalar mapRadius(SkScalar radius) const;
|
|
|
|
typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y,
|
|
SkPoint* result);
|
|
|
|
static MapXYProc GetMapXYProc(TypeMask mask) {
|
|
SkASSERT((mask & ~kAllMasks) == 0);
|
|
return gMapXYProcs[mask & kAllMasks];
|
|
}
|
|
|
|
MapXYProc getMapXYProc() const {
|
|
return GetMapXYProc(this->getType());
|
|
}
|
|
|
|
typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[],
|
|
const SkPoint src[], int count);
|
|
|
|
static MapPtsProc GetMapPtsProc(TypeMask mask) {
|
|
SkASSERT((mask & ~kAllMasks) == 0);
|
|
return gMapPtsProcs[mask & kAllMasks];
|
|
}
|
|
|
|
MapPtsProc getMapPtsProc() const {
|
|
return GetMapPtsProc(this->getType());
|
|
}
|
|
|
|
/** If the matrix can be stepped in X (not complex perspective)
|
|
then return true and if step[XY] is not null, return the step[XY] value.
|
|
If it cannot, return false and ignore step.
|
|
*/
|
|
bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
|
|
|
|
friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
|
|
return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
|
|
}
|
|
|
|
friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
|
|
return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
|
|
}
|
|
|
|
enum {
|
|
// flatten/unflatten will never return a value larger than this
|
|
kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t)
|
|
};
|
|
// return the number of bytes written, whether or not buffer is null
|
|
uint32_t flatten(void* buffer) const;
|
|
// return the number of bytes read
|
|
uint32_t unflatten(const void* buffer);
|
|
|
|
void dump() const;
|
|
void toDumpString(SkString*) const;
|
|
|
|
/**
|
|
* Calculates the maximum stretching factor of the matrix. Only defined if
|
|
* the matrix does not have perspective.
|
|
*
|
|
* @return maximum strecthing factor or negative if matrix has perspective.
|
|
*/
|
|
SkScalar getMaxStretch() const;
|
|
|
|
/**
|
|
* Return a reference to a const identity matrix
|
|
*/
|
|
static const SkMatrix& I();
|
|
|
|
/**
|
|
* Return a reference to a const matrix that is "invalid", one that could
|
|
* never be used.
|
|
*/
|
|
static const SkMatrix& InvalidMatrix();
|
|
|
|
private:
|
|
enum {
|
|
/** Set if the matrix will map a rectangle to another rectangle. This
|
|
can be true if the matrix is scale-only, or rotates a multiple of
|
|
90 degrees. This bit is not set if the matrix is identity.
|
|
|
|
This bit will be set on identity matrices
|
|
*/
|
|
kRectStaysRect_Mask = 0x10,
|
|
|
|
kUnknown_Mask = 0x80,
|
|
|
|
kAllMasks = kTranslate_Mask |
|
|
kScale_Mask |
|
|
kAffine_Mask |
|
|
kPerspective_Mask |
|
|
kRectStaysRect_Mask
|
|
};
|
|
|
|
SkScalar fMat[9];
|
|
mutable uint8_t fTypeMask;
|
|
|
|
uint8_t computeTypeMask() const;
|
|
|
|
void setTypeMask(int mask) {
|
|
// allow kUnknown or a valid mask
|
|
SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask);
|
|
fTypeMask = SkToU8(mask);
|
|
}
|
|
|
|
void clearTypeMask(int mask) {
|
|
// only allow a valid mask
|
|
SkASSERT((mask & kAllMasks) == mask);
|
|
fTypeMask &= ~mask;
|
|
}
|
|
|
|
static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
|
|
static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
|
|
static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
|
|
|
|
static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
|
|
|
|
static const MapXYProc gMapXYProcs[];
|
|
|
|
static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int);
|
|
static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
|
|
int count);
|
|
static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
|
|
int count);
|
|
static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
|
|
|
|
static const MapPtsProc gMapPtsProcs[];
|
|
|
|
friend class SkPerspIter;
|
|
};
|
|
|
|
#endif
|
|
|