michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #ifndef SkMatrix_DEFINED michael@0: #define SkMatrix_DEFINED michael@0: michael@0: #include "SkRect.h" michael@0: michael@0: class SkString; michael@0: michael@0: // TODO: can we remove these 3 (need to check chrome/android) michael@0: typedef SkScalar SkPersp; michael@0: #define SkScalarToPersp(x) (x) michael@0: #define SkPerspToScalar(x) (x) michael@0: michael@0: /** \class SkMatrix michael@0: michael@0: The SkMatrix class holds a 3x3 matrix for transforming coordinates. michael@0: SkMatrix does not have a constructor, so it must be explicitly initialized michael@0: using either reset() - to construct an identity matrix, or one of the set michael@0: functions (e.g. setTranslate, setRotate, etc.). michael@0: */ michael@0: class SK_API SkMatrix { michael@0: public: michael@0: /** Enum of bit fields for the mask return by getType(). michael@0: Use this to identify the complexity of the matrix. michael@0: */ michael@0: enum TypeMask { michael@0: kIdentity_Mask = 0, michael@0: kTranslate_Mask = 0x01, //!< set if the matrix has translation michael@0: kScale_Mask = 0x02, //!< set if the matrix has X or Y scale michael@0: kAffine_Mask = 0x04, //!< set if the matrix skews or rotates michael@0: kPerspective_Mask = 0x08 //!< set if the matrix is in perspective michael@0: }; michael@0: michael@0: /** Returns a bitfield describing the transformations the matrix may michael@0: perform. The bitfield is computed conservatively, so it may include michael@0: false positives. For example, when kPerspective_Mask is true, all michael@0: other bits may be set to true even in the case of a pure perspective michael@0: transform. michael@0: */ michael@0: TypeMask getType() const { michael@0: if (fTypeMask & kUnknown_Mask) { michael@0: fTypeMask = this->computeTypeMask(); michael@0: } michael@0: // only return the public masks michael@0: return (TypeMask)(fTypeMask & 0xF); michael@0: } michael@0: michael@0: /** Returns true if the matrix is identity. michael@0: */ michael@0: bool isIdentity() const { michael@0: return this->getType() == 0; michael@0: } michael@0: michael@0: /** Returns true if will map a rectangle to another rectangle. This can be michael@0: true if the matrix is identity, scale-only, or rotates a multiple of michael@0: 90 degrees. michael@0: */ michael@0: bool rectStaysRect() const { michael@0: if (fTypeMask & kUnknown_Mask) { michael@0: fTypeMask = this->computeTypeMask(); michael@0: } michael@0: return (fTypeMask & kRectStaysRect_Mask) != 0; michael@0: } michael@0: // alias for rectStaysRect() michael@0: bool preservesAxisAlignment() const { return this->rectStaysRect(); } michael@0: michael@0: /** michael@0: * Returns true if the matrix contains perspective elements. michael@0: */ michael@0: bool hasPerspective() const { michael@0: return SkToBool(this->getPerspectiveTypeMaskOnly() & michael@0: kPerspective_Mask); michael@0: } michael@0: michael@0: /** Returns true if the matrix contains only translation, rotation or uniform scale michael@0: Returns false if other transformation types are included or is degenerate michael@0: */ michael@0: bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const; michael@0: michael@0: /** Returns true if the matrix contains only translation, rotation or scale michael@0: (non-uniform scale is allowed). michael@0: Returns false if other transformation types are included or is degenerate michael@0: */ michael@0: bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const; michael@0: michael@0: enum { michael@0: kMScaleX, michael@0: kMSkewX, michael@0: kMTransX, michael@0: kMSkewY, michael@0: kMScaleY, michael@0: kMTransY, michael@0: kMPersp0, michael@0: kMPersp1, michael@0: kMPersp2 michael@0: }; michael@0: michael@0: /** Affine arrays are in column major order michael@0: because that's how PDF and XPS like it. michael@0: */ michael@0: enum { michael@0: kAScaleX, michael@0: kASkewY, michael@0: kASkewX, michael@0: kAScaleY, michael@0: kATransX, michael@0: kATransY michael@0: }; michael@0: michael@0: SkScalar operator[](int index) const { michael@0: SkASSERT((unsigned)index < 9); michael@0: return fMat[index]; michael@0: } michael@0: michael@0: SkScalar get(int index) const { michael@0: SkASSERT((unsigned)index < 9); michael@0: return fMat[index]; michael@0: } michael@0: michael@0: SkScalar getScaleX() const { return fMat[kMScaleX]; } michael@0: SkScalar getScaleY() const { return fMat[kMScaleY]; } michael@0: SkScalar getSkewY() const { return fMat[kMSkewY]; } michael@0: SkScalar getSkewX() const { return fMat[kMSkewX]; } michael@0: SkScalar getTranslateX() const { return fMat[kMTransX]; } michael@0: SkScalar getTranslateY() const { return fMat[kMTransY]; } michael@0: SkPersp getPerspX() const { return fMat[kMPersp0]; } michael@0: SkPersp getPerspY() const { return fMat[kMPersp1]; } michael@0: michael@0: SkScalar& operator[](int index) { michael@0: SkASSERT((unsigned)index < 9); michael@0: this->setTypeMask(kUnknown_Mask); michael@0: return fMat[index]; michael@0: } michael@0: michael@0: void set(int index, SkScalar value) { michael@0: SkASSERT((unsigned)index < 9); michael@0: fMat[index] = value; michael@0: this->setTypeMask(kUnknown_Mask); michael@0: } michael@0: michael@0: void setScaleX(SkScalar v) { this->set(kMScaleX, v); } michael@0: void setScaleY(SkScalar v) { this->set(kMScaleY, v); } michael@0: void setSkewY(SkScalar v) { this->set(kMSkewY, v); } michael@0: void setSkewX(SkScalar v) { this->set(kMSkewX, v); } michael@0: void setTranslateX(SkScalar v) { this->set(kMTransX, v); } michael@0: void setTranslateY(SkScalar v) { this->set(kMTransY, v); } michael@0: void setPerspX(SkPersp v) { this->set(kMPersp0, v); } michael@0: void setPerspY(SkPersp v) { this->set(kMPersp1, v); } michael@0: michael@0: void setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, michael@0: SkScalar skewY, SkScalar scaleY, SkScalar transY, michael@0: SkPersp persp0, SkPersp persp1, SkPersp persp2) { michael@0: fMat[kMScaleX] = scaleX; michael@0: fMat[kMSkewX] = skewX; michael@0: fMat[kMTransX] = transX; michael@0: fMat[kMSkewY] = skewY; michael@0: fMat[kMScaleY] = scaleY; michael@0: fMat[kMTransY] = transY; michael@0: fMat[kMPersp0] = persp0; michael@0: fMat[kMPersp1] = persp1; michael@0: fMat[kMPersp2] = persp2; michael@0: this->setTypeMask(kUnknown_Mask); michael@0: } michael@0: michael@0: /** Set the matrix to identity michael@0: */ michael@0: void reset(); michael@0: // alias for reset() michael@0: void setIdentity() { this->reset(); } michael@0: michael@0: /** Set the matrix to translate by (dx, dy). michael@0: */ michael@0: void setTranslate(SkScalar dx, SkScalar dy); michael@0: void setTranslate(const SkVector& v) { this->setTranslate(v.fX, v.fY); } michael@0: michael@0: /** Set the matrix to scale by sx and sy, with a pivot point at (px, py). michael@0: The pivot point is the coordinate that should remain unchanged by the michael@0: specified transformation. michael@0: */ michael@0: void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); michael@0: /** Set the matrix to scale by sx and sy. michael@0: */ michael@0: void setScale(SkScalar sx, SkScalar sy); michael@0: /** Set the matrix to scale by 1/divx and 1/divy. Returns false and doesn't michael@0: touch the matrix if either divx or divy is zero. michael@0: */ michael@0: bool setIDiv(int divx, int divy); michael@0: /** Set the matrix to rotate by the specified number of degrees, with a michael@0: pivot point at (px, py). The pivot point is the coordinate that should michael@0: remain unchanged by the specified transformation. michael@0: */ michael@0: void setRotate(SkScalar degrees, SkScalar px, SkScalar py); michael@0: /** Set the matrix to rotate about (0,0) by the specified number of degrees. michael@0: */ michael@0: void setRotate(SkScalar degrees); michael@0: /** Set the matrix to rotate by the specified sine and cosine values, with michael@0: a pivot point at (px, py). The pivot point is the coordinate that michael@0: should remain unchanged by the specified transformation. michael@0: */ michael@0: void setSinCos(SkScalar sinValue, SkScalar cosValue, michael@0: SkScalar px, SkScalar py); michael@0: /** Set the matrix to rotate by the specified sine and cosine values. michael@0: */ michael@0: void setSinCos(SkScalar sinValue, SkScalar cosValue); michael@0: /** Set the matrix to skew by sx and sy, with a pivot point at (px, py). michael@0: The pivot point is the coordinate that should remain unchanged by the michael@0: specified transformation. michael@0: */ michael@0: void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); michael@0: /** Set the matrix to skew by sx and sy. michael@0: */ michael@0: void setSkew(SkScalar kx, SkScalar ky); michael@0: /** Set the matrix to the concatenation of the two specified matrices, michael@0: returning true if the the result can be represented. Either of the michael@0: two matrices may also be the target matrix. *this = a * b; michael@0: */ michael@0: bool setConcat(const SkMatrix& a, const SkMatrix& b); michael@0: michael@0: /** Preconcats the matrix with the specified translation. michael@0: M' = M * T(dx, dy) michael@0: */ michael@0: bool preTranslate(SkScalar dx, SkScalar dy); michael@0: /** Preconcats the matrix with the specified scale. michael@0: M' = M * S(sx, sy, px, py) michael@0: */ michael@0: bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); michael@0: /** Preconcats the matrix with the specified scale. michael@0: M' = M * S(sx, sy) michael@0: */ michael@0: bool preScale(SkScalar sx, SkScalar sy); michael@0: /** Preconcats the matrix with the specified rotation. michael@0: M' = M * R(degrees, px, py) michael@0: */ michael@0: bool preRotate(SkScalar degrees, SkScalar px, SkScalar py); michael@0: /** Preconcats the matrix with the specified rotation. michael@0: M' = M * R(degrees) michael@0: */ michael@0: bool preRotate(SkScalar degrees); michael@0: /** Preconcats the matrix with the specified skew. michael@0: M' = M * K(kx, ky, px, py) michael@0: */ michael@0: bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); michael@0: /** Preconcats the matrix with the specified skew. michael@0: M' = M * K(kx, ky) michael@0: */ michael@0: bool preSkew(SkScalar kx, SkScalar ky); michael@0: /** Preconcats the matrix with the specified matrix. michael@0: M' = M * other michael@0: */ michael@0: bool preConcat(const SkMatrix& other); michael@0: michael@0: /** Postconcats the matrix with the specified translation. michael@0: M' = T(dx, dy) * M michael@0: */ michael@0: bool postTranslate(SkScalar dx, SkScalar dy); michael@0: /** Postconcats the matrix with the specified scale. michael@0: M' = S(sx, sy, px, py) * M michael@0: */ michael@0: bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); michael@0: /** Postconcats the matrix with the specified scale. michael@0: M' = S(sx, sy) * M michael@0: */ michael@0: bool postScale(SkScalar sx, SkScalar sy); michael@0: /** Postconcats the matrix by dividing it by the specified integers. michael@0: M' = S(1/divx, 1/divy, 0, 0) * M michael@0: */ michael@0: bool postIDiv(int divx, int divy); michael@0: /** Postconcats the matrix with the specified rotation. michael@0: M' = R(degrees, px, py) * M michael@0: */ michael@0: bool postRotate(SkScalar degrees, SkScalar px, SkScalar py); michael@0: /** Postconcats the matrix with the specified rotation. michael@0: M' = R(degrees) * M michael@0: */ michael@0: bool postRotate(SkScalar degrees); michael@0: /** Postconcats the matrix with the specified skew. michael@0: M' = K(kx, ky, px, py) * M michael@0: */ michael@0: bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); michael@0: /** Postconcats the matrix with the specified skew. michael@0: M' = K(kx, ky) * M michael@0: */ michael@0: bool postSkew(SkScalar kx, SkScalar ky); michael@0: /** Postconcats the matrix with the specified matrix. michael@0: M' = other * M michael@0: */ michael@0: bool postConcat(const SkMatrix& other); michael@0: michael@0: enum ScaleToFit { michael@0: /** michael@0: * Scale in X and Y independently, so that src matches dst exactly. michael@0: * This may change the aspect ratio of the src. michael@0: */ michael@0: kFill_ScaleToFit, michael@0: /** michael@0: * Compute a scale that will maintain the original src aspect ratio, michael@0: * but will also ensure that src fits entirely inside dst. At least one michael@0: * axis (X or Y) will fit exactly. kStart aligns the result to the michael@0: * left and top edges of dst. michael@0: */ michael@0: kStart_ScaleToFit, michael@0: /** michael@0: * Compute a scale that will maintain the original src aspect ratio, michael@0: * but will also ensure that src fits entirely inside dst. At least one michael@0: * axis (X or Y) will fit exactly. The result is centered inside dst. michael@0: */ michael@0: kCenter_ScaleToFit, michael@0: /** michael@0: * Compute a scale that will maintain the original src aspect ratio, michael@0: * but will also ensure that src fits entirely inside dst. At least one michael@0: * axis (X or Y) will fit exactly. kEnd aligns the result to the michael@0: * right and bottom edges of dst. michael@0: */ michael@0: kEnd_ScaleToFit michael@0: }; michael@0: michael@0: /** Set the matrix to the scale and translate values that map the source michael@0: rectangle to the destination rectangle, returning true if the the result michael@0: can be represented. michael@0: @param src the source rectangle to map from. michael@0: @param dst the destination rectangle to map to. michael@0: @param stf the ScaleToFit option michael@0: @return true if the matrix can be represented by the rectangle mapping. michael@0: */ michael@0: bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); michael@0: michael@0: /** Set the matrix such that the specified src points would map to the michael@0: specified dst points. count must be within [0..4]. michael@0: @param src The array of src points michael@0: @param dst The array of dst points michael@0: @param count The number of points to use for the transformation michael@0: @return true if the matrix was set to the specified transformation michael@0: */ michael@0: bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); michael@0: michael@0: /** If this matrix can be inverted, return true and if inverse is not null, michael@0: set inverse to be the inverse of this matrix. If this matrix cannot be michael@0: inverted, ignore inverse and return false michael@0: */ michael@0: bool SK_WARN_UNUSED_RESULT invert(SkMatrix* inverse) const { michael@0: // Allow the trivial case to be inlined. michael@0: if (this->isIdentity()) { michael@0: if (NULL != inverse) { michael@0: inverse->reset(); michael@0: } michael@0: return true; michael@0: } michael@0: return this->invertNonIdentity(inverse); michael@0: } michael@0: michael@0: /** Fills the passed array with affine identity values michael@0: in column major order. michael@0: @param affine The array to fill with affine identity values. michael@0: Must not be NULL. michael@0: */ michael@0: static void SetAffineIdentity(SkScalar affine[6]); michael@0: michael@0: /** Fills the passed array with the affine values in column major order. michael@0: If the matrix is a perspective transform, returns false michael@0: and does not change the passed array. michael@0: @param affine The array to fill with affine values. Ignored if NULL. michael@0: */ michael@0: bool asAffine(SkScalar affine[6]) const; michael@0: michael@0: /** Apply this matrix to the array of points specified by src, and write michael@0: the transformed points into the array of points specified by dst. michael@0: dst[] = M * src[] michael@0: @param dst Where the transformed coordinates are written. It must michael@0: contain at least count entries michael@0: @param src The original coordinates that are to be transformed. It michael@0: must contain at least count entries michael@0: @param count The number of points in src to read, and then transform michael@0: into dst. michael@0: */ michael@0: void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; michael@0: michael@0: /** Apply this matrix to the array of points, overwriting it with the michael@0: transformed values. michael@0: dst[] = M * pts[] michael@0: @param pts The points to be transformed. It must contain at least michael@0: count entries michael@0: @param count The number of points in pts. michael@0: */ michael@0: void mapPoints(SkPoint pts[], int count) const { michael@0: this->mapPoints(pts, pts, count); michael@0: } michael@0: michael@0: /** Like mapPoints but with custom byte stride between the points. Stride michael@0: * should be a multiple of sizeof(SkScalar). michael@0: */ michael@0: void mapPointsWithStride(SkPoint pts[], size_t stride, int count) const { michael@0: SkASSERT(stride >= sizeof(SkPoint)); michael@0: SkASSERT(0 == stride % sizeof(SkScalar)); michael@0: for (int i = 0; i < count; ++i) { michael@0: this->mapPoints(pts, pts, 1); michael@0: pts = (SkPoint*)((intptr_t)pts + stride); michael@0: } michael@0: } michael@0: michael@0: /** Like mapPoints but with custom byte stride between the points. michael@0: */ michael@0: void mapPointsWithStride(SkPoint dst[], SkPoint src[], michael@0: size_t stride, int count) const { michael@0: SkASSERT(stride >= sizeof(SkPoint)); michael@0: SkASSERT(0 == stride % sizeof(SkScalar)); michael@0: for (int i = 0; i < count; ++i) { michael@0: this->mapPoints(dst, src, 1); michael@0: src = (SkPoint*)((intptr_t)src + stride); michael@0: dst = (SkPoint*)((intptr_t)dst + stride); michael@0: } michael@0: } michael@0: michael@0: /** Apply this matrix to the array of homogeneous points, specified by src, michael@0: where a homogeneous point is defined by 3 contiguous scalar values, michael@0: and write the transformed points into the array of scalars specified by dst. michael@0: dst[] = M * src[] michael@0: @param dst Where the transformed coordinates are written. It must michael@0: contain at least 3 * count entries michael@0: @param src The original coordinates that are to be transformed. It michael@0: must contain at least 3 * count entries michael@0: @param count The number of triples (homogeneous points) in src to read, michael@0: and then transform into dst. michael@0: */ michael@0: void mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const; michael@0: michael@0: void mapXY(SkScalar x, SkScalar y, SkPoint* result) const { michael@0: SkASSERT(result); michael@0: this->getMapXYProc()(*this, x, y, result); michael@0: } michael@0: michael@0: /** Apply this matrix to the array of vectors specified by src, and write michael@0: the transformed vectors into the array of vectors specified by dst. michael@0: This is similar to mapPoints, but ignores any translation in the matrix. michael@0: @param dst Where the transformed coordinates are written. It must michael@0: contain at least count entries michael@0: @param src The original coordinates that are to be transformed. It michael@0: must contain at least count entries michael@0: @param count The number of vectors in src to read, and then transform michael@0: into dst. michael@0: */ michael@0: void mapVectors(SkVector dst[], const SkVector src[], int count) const; michael@0: michael@0: /** Apply this matrix to the array of vectors specified by src, and write michael@0: the transformed vectors into the array of vectors specified by dst. michael@0: This is similar to mapPoints, but ignores any translation in the matrix. michael@0: @param vecs The vectors to be transformed. It must contain at least michael@0: count entries michael@0: @param count The number of vectors in vecs. michael@0: */ michael@0: void mapVectors(SkVector vecs[], int count) const { michael@0: this->mapVectors(vecs, vecs, count); michael@0: } michael@0: michael@0: /** Apply this matrix to the src rectangle, and write the transformed michael@0: rectangle into dst. This is accomplished by transforming the 4 corners michael@0: of src, and then setting dst to the bounds of those points. michael@0: @param dst Where the transformed rectangle is written. michael@0: @param src The original rectangle to be transformed. michael@0: @return the result of calling rectStaysRect() michael@0: */ michael@0: bool mapRect(SkRect* dst, const SkRect& src) const; michael@0: michael@0: /** Apply this matrix to the rectangle, and write the transformed rectangle michael@0: back into it. This is accomplished by transforming the 4 corners of michael@0: rect, and then setting it to the bounds of those points michael@0: @param rect The rectangle to transform. michael@0: @return the result of calling rectStaysRect() michael@0: */ michael@0: bool mapRect(SkRect* rect) const { michael@0: return this->mapRect(rect, *rect); michael@0: } michael@0: michael@0: /** Apply this matrix to the src rectangle, and write the four transformed michael@0: points into dst. The points written to dst will be the original top-left, top-right, michael@0: bottom-right, and bottom-left points transformed by the matrix. michael@0: @param dst Where the transformed quad is written. michael@0: @param rect The original rectangle to be transformed. michael@0: */ michael@0: void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const { michael@0: // This could potentially be faster if we only transformed each x and y of the rect once. michael@0: rect.toQuad(dst); michael@0: this->mapPoints(dst, 4); michael@0: } michael@0: michael@0: /** Return the mean radius of a circle after it has been mapped by michael@0: this matrix. NOTE: in perspective this value assumes the circle michael@0: has its center at the origin. michael@0: */ michael@0: SkScalar mapRadius(SkScalar radius) const; michael@0: michael@0: typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, michael@0: SkPoint* result); michael@0: michael@0: static MapXYProc GetMapXYProc(TypeMask mask) { michael@0: SkASSERT((mask & ~kAllMasks) == 0); michael@0: return gMapXYProcs[mask & kAllMasks]; michael@0: } michael@0: michael@0: MapXYProc getMapXYProc() const { michael@0: return GetMapXYProc(this->getType()); michael@0: } michael@0: michael@0: typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], michael@0: const SkPoint src[], int count); michael@0: michael@0: static MapPtsProc GetMapPtsProc(TypeMask mask) { michael@0: SkASSERT((mask & ~kAllMasks) == 0); michael@0: return gMapPtsProcs[mask & kAllMasks]; michael@0: } michael@0: michael@0: MapPtsProc getMapPtsProc() const { michael@0: return GetMapPtsProc(this->getType()); michael@0: } michael@0: michael@0: /** If the matrix can be stepped in X (not complex perspective) michael@0: then return true and if step[XY] is not null, return the step[XY] value. michael@0: If it cannot, return false and ignore step. michael@0: */ michael@0: bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; michael@0: michael@0: /** Efficient comparison of two matrices. It distinguishes between zero and michael@0: * negative zero. It will return false when the sign of zero values is the michael@0: * only difference between the two matrices. It considers NaN values to be michael@0: * equal to themselves. So a matrix full of NaNs is "cheap equal" to michael@0: * another matrix full of NaNs iff the NaN values are bitwise identical michael@0: * while according to strict the strict == test a matrix with a NaN value michael@0: * is equal to nothing, including itself. michael@0: */ michael@0: bool cheapEqualTo(const SkMatrix& m) const { michael@0: return 0 == memcmp(fMat, m.fMat, sizeof(fMat)); michael@0: } michael@0: michael@0: friend bool operator==(const SkMatrix& a, const SkMatrix& b); michael@0: friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { michael@0: return !(a == b); michael@0: } michael@0: michael@0: enum { michael@0: // writeTo/readFromMemory will never return a value larger than this michael@0: kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t) michael@0: }; michael@0: // return the number of bytes written, whether or not buffer is null michael@0: size_t writeToMemory(void* buffer) const; michael@0: /** michael@0: * Reads data from the buffer parameter michael@0: * michael@0: * @param buffer Memory to read from michael@0: * @param length Amount of memory available in the buffer michael@0: * @return number of bytes read (must be a multiple of 4) or michael@0: * 0 if there was not enough memory available michael@0: */ michael@0: size_t readFromMemory(const void* buffer, size_t length); michael@0: michael@0: SkDEVCODE(void dump() const;) michael@0: SK_TO_STRING_NONVIRT() michael@0: michael@0: /** michael@0: * Calculates the minimum stretching factor of the matrix. If the matrix has michael@0: * perspective -1 is returned. michael@0: * michael@0: * @return minumum strecthing factor michael@0: */ michael@0: SkScalar getMinStretch() const; michael@0: michael@0: /** michael@0: * Calculates the maximum stretching factor of the matrix. If the matrix has michael@0: * perspective -1 is returned. michael@0: * michael@0: * @return maximum strecthing factor michael@0: */ michael@0: SkScalar getMaxStretch() const; michael@0: michael@0: /** michael@0: * Return a reference to a const identity matrix michael@0: */ michael@0: static const SkMatrix& I(); michael@0: michael@0: /** michael@0: * Return a reference to a const matrix that is "invalid", one that could michael@0: * never be used. michael@0: */ michael@0: static const SkMatrix& InvalidMatrix(); michael@0: michael@0: /** michael@0: * Testing routine; the matrix's type cache should never need to be michael@0: * manually invalidated during normal use. michael@0: */ michael@0: void dirtyMatrixTypeCache() { michael@0: this->setTypeMask(kUnknown_Mask); michael@0: } michael@0: michael@0: private: michael@0: enum { michael@0: /** Set if the matrix will map a rectangle to another rectangle. This michael@0: can be true if the matrix is scale-only, or rotates a multiple of michael@0: 90 degrees. michael@0: michael@0: This bit will be set on identity matrices michael@0: */ michael@0: kRectStaysRect_Mask = 0x10, michael@0: michael@0: /** Set if the perspective bit is valid even though the rest of michael@0: the matrix is Unknown. michael@0: */ michael@0: kOnlyPerspectiveValid_Mask = 0x40, michael@0: michael@0: kUnknown_Mask = 0x80, michael@0: michael@0: kORableMasks = kTranslate_Mask | michael@0: kScale_Mask | michael@0: kAffine_Mask | michael@0: kPerspective_Mask, michael@0: michael@0: kAllMasks = kTranslate_Mask | michael@0: kScale_Mask | michael@0: kAffine_Mask | michael@0: kPerspective_Mask | michael@0: kRectStaysRect_Mask michael@0: }; michael@0: michael@0: SkScalar fMat[9]; michael@0: mutable uint32_t fTypeMask; michael@0: michael@0: uint8_t computeTypeMask() const; michael@0: uint8_t computePerspectiveTypeMask() const; michael@0: michael@0: void setTypeMask(int mask) { michael@0: // allow kUnknown or a valid mask michael@0: SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask || michael@0: ((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask) michael@0: == (kUnknown_Mask | kOnlyPerspectiveValid_Mask)); michael@0: fTypeMask = SkToU8(mask); michael@0: } michael@0: michael@0: void orTypeMask(int mask) { michael@0: SkASSERT((mask & kORableMasks) == mask); michael@0: fTypeMask = SkToU8(fTypeMask | mask); michael@0: } michael@0: michael@0: void clearTypeMask(int mask) { michael@0: // only allow a valid mask michael@0: SkASSERT((mask & kAllMasks) == mask); michael@0: fTypeMask &= ~mask; michael@0: } michael@0: michael@0: TypeMask getPerspectiveTypeMaskOnly() const { michael@0: if ((fTypeMask & kUnknown_Mask) && michael@0: !(fTypeMask & kOnlyPerspectiveValid_Mask)) { michael@0: fTypeMask = this->computePerspectiveTypeMask(); michael@0: } michael@0: return (TypeMask)(fTypeMask & 0xF); michael@0: } michael@0: michael@0: /** Returns true if we already know that the matrix is identity; michael@0: false otherwise. michael@0: */ michael@0: bool isTriviallyIdentity() const { michael@0: if (fTypeMask & kUnknown_Mask) { michael@0: return false; michael@0: } michael@0: return ((fTypeMask & 0xF) == 0); michael@0: } michael@0: michael@0: bool SK_WARN_UNUSED_RESULT invertNonIdentity(SkMatrix* inverse) const; michael@0: michael@0: static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); michael@0: static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); michael@0: static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); michael@0: michael@0: static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); michael@0: michael@0: static const MapXYProc gMapXYProcs[]; michael@0: michael@0: static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); michael@0: static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); michael@0: static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); michael@0: static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], michael@0: int count); michael@0: static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); michael@0: static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], michael@0: int count); michael@0: static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); michael@0: michael@0: static const MapPtsProc gMapPtsProcs[]; michael@0: michael@0: friend class SkPerspIter; michael@0: }; michael@0: michael@0: #endif