1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SkMatrix_DEFINED 18 #define SkMatrix_DEFINED 19 20 #include "SkRect.h" 21 22 class SkString; 23 24 /** \class SkMatrix 25 26 The SkMatrix class holds a 3x3 matrix for transforming coordinates. 27 SkMatrix does not have a constructor, so it must be explicitly initialized 28 using either reset() - to construct an identity matrix, or one of the set 29 functions (e.g. setTranslate, setRotate, etc.). 30 */ 31 class SkMatrix { 32 public: 33 /** Enum of bit fields for the mask return by getType(). 34 Use this to identify the complexity of the matrix. 35 */ 36 enum TypeMask { 37 kIdentity_Mask = 0, 38 kTranslate_Mask = 0x01, //!< set if the matrix has translation 39 kScale_Mask = 0x02, //!< set if the matrix has X or Y scale 40 kAffine_Mask = 0x04, //!< set if the matrix skews or rotates 41 kPerspective_Mask = 0x08 //!< set if the matrix is in perspective 42 }; 43 44 /** Returns a mask bitfield describing the types of transformations 45 that the matrix will perform. This information is used by routines 46 like mapPoints, to optimize its inner loops to only perform as much 47 arithmetic as is necessary. 48 */ getType()49 TypeMask getType() const { 50 if (fTypeMask & kUnknown_Mask) { 51 fTypeMask = this->computeTypeMask(); 52 } 53 // only return the public masks 54 return (TypeMask)(fTypeMask & 0xF); 55 } 56 57 /** Returns true if the matrix is identity. 58 */ isIdentity()59 bool isIdentity() const { 60 return this->getType() == 0; 61 } 62 63 /** Returns true if will map a rectangle to another rectangle. This can be 64 true if the matrix is identity, scale-only, or rotates a multiple of 65 90 degrees. 66 */ rectStaysRect()67 bool rectStaysRect() const { 68 if (fTypeMask & kUnknown_Mask) { 69 fTypeMask = this->computeTypeMask(); 70 } 71 return (fTypeMask & kRectStaysRect_Mask) != 0; 72 } 73 74 enum { 75 kMScaleX, 76 kMSkewX, 77 kMTransX, 78 kMSkewY, 79 kMScaleY, 80 kMTransY, 81 kMPersp0, 82 kMPersp1, 83 kMPersp2 84 }; 85 86 SkScalar operator[](int index) const { 87 SkASSERT((unsigned)index < 9); 88 return fMat[index]; 89 } 90 get(int index)91 SkScalar get(int index) const { 92 SkASSERT((unsigned)index < 9); 93 return fMat[index]; 94 } 95 getScaleX()96 SkScalar getScaleX() const { return fMat[kMScaleX]; } getScaleY()97 SkScalar getScaleY() const { return fMat[kMScaleY]; } getSkewY()98 SkScalar getSkewY() const { return fMat[kMSkewY]; } getSkewX()99 SkScalar getSkewX() const { return fMat[kMSkewX]; } getTranslateX()100 SkScalar getTranslateX() const { return fMat[kMTransX]; } getTranslateY()101 SkScalar getTranslateY() const { return fMat[kMTransY]; } getPerspX()102 SkScalar getPerspX() const { return fMat[kMPersp0]; } getPerspY()103 SkScalar getPerspY() const { return fMat[kMPersp1]; } 104 105 SkScalar& operator[](int index) { 106 SkASSERT((unsigned)index < 9); 107 this->setTypeMask(kUnknown_Mask); 108 return fMat[index]; 109 } 110 set(int index,SkScalar value)111 void set(int index, SkScalar value) { 112 SkASSERT((unsigned)index < 9); 113 fMat[index] = value; 114 this->setTypeMask(kUnknown_Mask); 115 } 116 setScaleX(SkScalar v)117 void setScaleX(SkScalar v) { this->set(kMScaleX, v); } setScaleY(SkScalar v)118 void setScaleY(SkScalar v) { this->set(kMScaleY, v); } setSkewY(SkScalar v)119 void setSkewY(SkScalar v) { this->set(kMSkewY, v); } setSkewX(SkScalar v)120 void setSkewX(SkScalar v) { this->set(kMSkewX, v); } setTranslateX(SkScalar v)121 void setTranslateX(SkScalar v) { this->set(kMTransX, v); } setTranslateY(SkScalar v)122 void setTranslateY(SkScalar v) { this->set(kMTransY, v); } setPerspX(SkScalar v)123 void setPerspX(SkScalar v) { this->set(kMPersp0, v); } setPerspY(SkScalar v)124 void setPerspY(SkScalar v) { this->set(kMPersp1, v); } 125 126 /** Set the matrix to identity 127 */ 128 void reset(); 129 130 /** Set the matrix to translate by (dx, dy). 131 */ 132 void setTranslate(SkScalar dx, SkScalar dy); 133 /** Set the matrix to scale by sx and sy, with a pivot point at (px, py). 134 The pivot point is the coordinate that should remain unchanged by the 135 specified transformation. 136 */ 137 void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 138 /** Set the matrix to scale by sx and sy. 139 */ 140 void setScale(SkScalar sx, SkScalar sy); 141 /** Set the matrix to rotate by the specified number of degrees, with a 142 pivot point at (px, py). The pivot point is the coordinate that should 143 remain unchanged by the specified transformation. 144 */ 145 void setRotate(SkScalar degrees, SkScalar px, SkScalar py); 146 /** Set the matrix to rotate about (0,0) by the specified number of degrees. 147 */ 148 void setRotate(SkScalar degrees); 149 /** Set the matrix to rotate by the specified sine and cosine values, with 150 a pivot point at (px, py). The pivot point is the coordinate that 151 should remain unchanged by the specified transformation. 152 */ 153 void setSinCos(SkScalar sinValue, SkScalar cosValue, 154 SkScalar px, SkScalar py); 155 /** Set the matrix to rotate by the specified sine and cosine values. 156 */ 157 void setSinCos(SkScalar sinValue, SkScalar cosValue); 158 /** Set the matrix to skew by sx and sy, with a pivot point at (px, py). 159 The pivot point is the coordinate that should remain unchanged by the 160 specified transformation. 161 */ 162 void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 163 /** Set the matrix to skew by sx and sy. 164 */ 165 void setSkew(SkScalar kx, SkScalar ky); 166 /** Set the matrix to the concatenation of the two specified matrices, 167 returning true if the the result can be represented. Either of the 168 two matrices may also be the target matrix. *this = a * b; 169 */ 170 bool setConcat(const SkMatrix& a, const SkMatrix& b); 171 172 /** Preconcats the matrix with the specified translation. 173 M' = M * T(dx, dy) 174 */ 175 bool preTranslate(SkScalar dx, SkScalar dy); 176 /** Preconcats the matrix with the specified scale. 177 M' = M * S(sx, sy, px, py) 178 */ 179 bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 180 /** Preconcats the matrix with the specified scale. 181 M' = M * S(sx, sy) 182 */ 183 bool preScale(SkScalar sx, SkScalar sy); 184 /** Preconcats the matrix with the specified rotation. 185 M' = M * R(degrees, px, py) 186 */ 187 bool preRotate(SkScalar degrees, SkScalar px, SkScalar py); 188 /** Preconcats the matrix with the specified rotation. 189 M' = M * R(degrees) 190 */ 191 bool preRotate(SkScalar degrees); 192 /** Preconcats the matrix with the specified skew. 193 M' = M * K(kx, ky, px, py) 194 */ 195 bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 196 /** Preconcats the matrix with the specified skew. 197 M' = M * K(kx, ky) 198 */ 199 bool preSkew(SkScalar kx, SkScalar ky); 200 /** Preconcats the matrix with the specified matrix. 201 M' = M * other 202 */ 203 bool preConcat(const SkMatrix& other); 204 205 /** Postconcats the matrix with the specified translation. 206 M' = T(dx, dy) * M 207 */ 208 bool postTranslate(SkScalar dx, SkScalar dy); 209 /** Postconcats the matrix with the specified scale. 210 M' = S(sx, sy, px, py) * M 211 */ 212 bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 213 /** Postconcats the matrix with the specified scale. 214 M' = S(sx, sy) * M 215 */ 216 bool postScale(SkScalar sx, SkScalar sy); 217 /** Postconcats the matrix by dividing it by the specified integers. 218 M' = S(1/divx, 1/divy, 0, 0) * M 219 */ 220 bool postIDiv(int divx, int divy); 221 /** Postconcats the matrix with the specified rotation. 222 M' = R(degrees, px, py) * M 223 */ 224 bool postRotate(SkScalar degrees, SkScalar px, SkScalar py); 225 /** Postconcats the matrix with the specified rotation. 226 M' = R(degrees) * M 227 */ 228 bool postRotate(SkScalar degrees); 229 /** Postconcats the matrix with the specified skew. 230 M' = K(kx, ky, px, py) * M 231 */ 232 bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 233 /** Postconcats the matrix with the specified skew. 234 M' = K(kx, ky) * M 235 */ 236 bool postSkew(SkScalar kx, SkScalar ky); 237 /** Postconcats the matrix with the specified matrix. 238 M' = other * M 239 */ 240 bool postConcat(const SkMatrix& other); 241 242 enum ScaleToFit { 243 /** 244 * Scale in X and Y independently, so that src matches dst exactly. 245 * This may change the aspect ratio of the src. 246 */ 247 kFill_ScaleToFit, 248 /** 249 * Compute a scale that will maintain the original src aspect ratio, 250 * but will also ensure that src fits entirely inside dst. At least one 251 * axis (X or Y) will fit exactly. kStart aligns the result to the 252 * left and top edges of dst. 253 */ 254 kStart_ScaleToFit, 255 /** 256 * Compute a scale that will maintain the original src aspect ratio, 257 * but will also ensure that src fits entirely inside dst. At least one 258 * axis (X or Y) will fit exactly. The result is centered inside dst. 259 */ 260 kCenter_ScaleToFit, 261 /** 262 * Compute a scale that will maintain the original src aspect ratio, 263 * but will also ensure that src fits entirely inside dst. At least one 264 * axis (X or Y) will fit exactly. kEnd aligns the result to the 265 * right and bottom edges of dst. 266 */ 267 kEnd_ScaleToFit 268 }; 269 270 /** Set the matrix to the scale and translate values that map the source 271 rectangle to the destination rectangle, returning true if the the result 272 can be represented. 273 @param src the source rectangle to map from. 274 @param dst the destination rectangle to map to. 275 @param stf the ScaleToFit option 276 @return true if the matrix can be represented by the rectangle mapping. 277 */ 278 bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); 279 280 /** Set the matrix such that the specified src points would map to the 281 specified dst points. count must be within [0..4]. 282 @param src The array of src points 283 @param dst The array of dst points 284 @param count The number of points to use for the transformation 285 @return true if the matrix was set to the specified transformation 286 */ 287 bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); 288 289 /** If this matrix can be inverted, return true and if inverse is not null, 290 set inverse to be the inverse of this matrix. If this matrix cannot be 291 inverted, ignore inverse and return false 292 */ 293 bool invert(SkMatrix* inverse) const; 294 295 /** Apply this matrix to the array of points specified by src, and write 296 the transformed points into the array of points specified by dst. 297 dst[] = M * src[] 298 @param dst Where the transformed coordinates are written. It must 299 contain at least count entries 300 @param src The original coordinates that are to be transformed. It 301 must contain at least count entries 302 @param count The number of points in src to read, and then transform 303 into dst. 304 */ 305 void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; 306 307 /** Apply this matrix to the array of points, overwriting it with the 308 transformed values. 309 dst[] = M * pts[] 310 @param pts The points to be transformed. It must contain at least 311 count entries 312 @param count The number of points in pts. 313 */ mapPoints(SkPoint pts[],int count)314 void mapPoints(SkPoint pts[], int count) const { 315 this->mapPoints(pts, pts, count); 316 } 317 mapXY(SkScalar x,SkScalar y,SkPoint * result)318 void mapXY(SkScalar x, SkScalar y, SkPoint* result) const { 319 SkASSERT(result); 320 this->getMapXYProc()(*this, x, y, result); 321 } 322 323 /** Apply this matrix to the array of vectors specified by src, and write 324 the transformed vectors into the array of vectors specified by dst. 325 This is similar to mapPoints, but ignores any translation in the matrix. 326 @param dst Where the transformed coordinates are written. It must 327 contain at least count entries 328 @param src The original coordinates that are to be transformed. It 329 must contain at least count entries 330 @param count The number of vectors in src to read, and then transform 331 into dst. 332 */ 333 void mapVectors(SkVector dst[], const SkVector src[], int count) const; 334 335 /** Apply this matrix to the array of vectors specified by src, and write 336 the transformed vectors into the array of vectors specified by dst. 337 This is similar to mapPoints, but ignores any translation in the matrix. 338 @param vecs The vectors to be transformed. It must contain at least 339 count entries 340 @param count The number of vectors in vecs. 341 */ mapVectors(SkVector vecs[],int count)342 void mapVectors(SkVector vecs[], int count) const { 343 this->mapVectors(vecs, vecs, count); 344 } 345 346 /** Apply this matrix to the src rectangle, and write the transformed 347 rectangle into dst. This is accomplished by transforming the 4 corners 348 of src, and then setting dst to the bounds of those points. 349 @param dst Where the transformed rectangle is written. 350 @param src The original rectangle to be transformed. 351 @return the result of calling rectStaysRect() 352 */ 353 bool mapRect(SkRect* dst, const SkRect& src) const; 354 355 /** Apply this matrix to the rectangle, and write the transformed rectangle 356 back into it. This is accomplished by transforming the 4 corners of 357 rect, and then setting it to the bounds of those points 358 @param rect The rectangle to transform. 359 @return the result of calling rectStaysRect() 360 */ mapRect(SkRect * rect)361 bool mapRect(SkRect* rect) const { 362 return this->mapRect(rect, *rect); 363 } 364 365 /** Return the mean radius of a circle after it has been mapped by 366 this matrix. NOTE: in perspective this value assumes the circle 367 has its center at the origin. 368 */ 369 SkScalar mapRadius(SkScalar radius) const; 370 371 typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, 372 SkPoint* result); 373 GetMapXYProc(TypeMask mask)374 static MapXYProc GetMapXYProc(TypeMask mask) { 375 SkASSERT((mask & ~kAllMasks) == 0); 376 return gMapXYProcs[mask & kAllMasks]; 377 } 378 getMapXYProc()379 MapXYProc getMapXYProc() const { 380 return GetMapXYProc(this->getType()); 381 } 382 383 typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], 384 const SkPoint src[], int count); 385 GetMapPtsProc(TypeMask mask)386 static MapPtsProc GetMapPtsProc(TypeMask mask) { 387 SkASSERT((mask & ~kAllMasks) == 0); 388 return gMapPtsProcs[mask & kAllMasks]; 389 } 390 getMapPtsProc()391 MapPtsProc getMapPtsProc() const { 392 return GetMapPtsProc(this->getType()); 393 } 394 395 /** If the matrix can be stepped in X (not complex perspective) 396 then return true and if step[XY] is not null, return the step[XY] value. 397 If it cannot, return false and ignore step. 398 */ 399 bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; 400 401 friend bool operator==(const SkMatrix& a, const SkMatrix& b) { 402 return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0; 403 } 404 405 friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { 406 return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0; 407 } 408 409 enum { 410 // flatten/unflatten will never return a value larger than this 411 kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t) 412 }; 413 // return the number of bytes written, whether or not buffer is null 414 uint32_t flatten(void* buffer) const; 415 // return the number of bytes read 416 uint32_t unflatten(const void* buffer); 417 418 void dump() const; 419 void toDumpString(SkString*) const; 420 421 private: 422 enum { 423 /** Set if the matrix will map a rectangle to another rectangle. This 424 can be true if the matrix is scale-only, or rotates a multiple of 425 90 degrees. This bit is not set if the matrix is identity. 426 427 This bit will be set on identity matrices 428 */ 429 kRectStaysRect_Mask = 0x10, 430 431 kUnknown_Mask = 0x80, 432 433 kAllMasks = kTranslate_Mask | 434 kScale_Mask | 435 kAffine_Mask | 436 kPerspective_Mask | 437 kRectStaysRect_Mask 438 }; 439 440 SkScalar fMat[9]; 441 mutable uint8_t fTypeMask; 442 443 uint8_t computeTypeMask() const; 444 setTypeMask(int mask)445 void setTypeMask(int mask) { 446 // allow kUnknown or a valid mask 447 SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask); 448 fTypeMask = SkToU8(mask); 449 } 450 clearTypeMask(int mask)451 void clearTypeMask(int mask) { 452 // only allow a valid mask 453 SkASSERT((mask & kAllMasks) == mask); 454 fTypeMask &= ~mask; 455 } 456 457 static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 458 static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 459 static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 460 461 static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 462 static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 463 static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 464 static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 465 static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 466 static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 467 static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 468 469 static const MapXYProc gMapXYProcs[]; 470 471 static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); 472 static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 473 static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 474 static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], 475 int count); 476 static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 477 static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], 478 int count); 479 static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 480 481 static const MapPtsProc gMapPtsProcs[]; 482 483 friend class SkPerspIter; 484 }; 485 486 #endif 487 488