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 SK_API 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 */ 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 */ 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 */ 67 bool rectStaysRect() const { 68 if (fTypeMask & kUnknown_Mask) { 69 fTypeMask = this->computeTypeMask(); 70 } 71 return (fTypeMask & kRectStaysRect_Mask) != 0; 72 } 73 // alias for rectStaysRect() 74 bool preservesAxisAlignment() const { return this->rectStaysRect(); } 75 76 /** 77 * Returns true if the perspective contains perspective elements. 78 */ 79 bool hasPerspective() const { 80 return SkToBool(this->getType() & kPerspective_Mask); 81 } 82 83 enum { 84 kMScaleX, 85 kMSkewX, 86 kMTransX, 87 kMSkewY, 88 kMScaleY, 89 kMTransY, 90 kMPersp0, 91 kMPersp1, 92 kMPersp2 93 }; 94 95 SkScalar operator[](int index) const { 96 SkASSERT((unsigned)index < 9); 97 return fMat[index]; 98 } 99 100 SkScalar get(int index) const { 101 SkASSERT((unsigned)index < 9); 102 return fMat[index]; 103 } 104 105 SkScalar getScaleX() const { return fMat[kMScaleX]; } 106 SkScalar getScaleY() const { return fMat[kMScaleY]; } 107 SkScalar getSkewY() const { return fMat[kMSkewY]; } 108 SkScalar getSkewX() const { return fMat[kMSkewX]; } 109 SkScalar getTranslateX() const { return fMat[kMTransX]; } 110 SkScalar getTranslateY() const { return fMat[kMTransY]; } 111 SkScalar getPerspX() const { return fMat[kMPersp0]; } 112 SkScalar getPerspY() const { return fMat[kMPersp1]; } 113 114 SkScalar& operator[](int index) { 115 SkASSERT((unsigned)index < 9); 116 this->setTypeMask(kUnknown_Mask); 117 return fMat[index]; 118 } 119 120 void set(int index, SkScalar value) { 121 SkASSERT((unsigned)index < 9); 122 fMat[index] = value; 123 this->setTypeMask(kUnknown_Mask); 124 } 125 126 void setScaleX(SkScalar v) { this->set(kMScaleX, v); } 127 void setScaleY(SkScalar v) { this->set(kMScaleY, v); } 128 void setSkewY(SkScalar v) { this->set(kMSkewY, v); } 129 void setSkewX(SkScalar v) { this->set(kMSkewX, v); } 130 void setTranslateX(SkScalar v) { this->set(kMTransX, v); } 131 void setTranslateY(SkScalar v) { this->set(kMTransY, v); } 132 void setPerspX(SkScalar v) { this->set(kMPersp0, v); } 133 void setPerspY(SkScalar v) { this->set(kMPersp1, v); } 134 135 void setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, 136 SkScalar skewY, SkScalar scaleY, SkScalar transY, 137 SkScalar persp0, SkScalar persp1, SkScalar persp2) { 138 fMat[kMScaleX] = scaleX; 139 fMat[kMSkewX] = skewX; 140 fMat[kMTransX] = transX; 141 fMat[kMSkewY] = skewY; 142 fMat[kMScaleY] = scaleY; 143 fMat[kMTransY] = transY; 144 fMat[kMPersp0] = persp0; 145 fMat[kMPersp1] = persp1; 146 fMat[kMPersp2] = persp2; 147 this->setTypeMask(kUnknown_Mask); 148 } 149 150 /** Set the matrix to identity 151 */ 152 void reset(); 153 // alias for reset() 154 void setIdentity() { this->reset(); } 155 156 /** Set the matrix to translate by (dx, dy). 157 */ 158 void setTranslate(SkScalar dx, SkScalar dy); 159 /** Set the matrix to scale by sx and sy, with a pivot point at (px, py). 160 The pivot point is the coordinate that should remain unchanged by the 161 specified transformation. 162 */ 163 void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 164 /** Set the matrix to scale by sx and sy. 165 */ 166 void setScale(SkScalar sx, SkScalar sy); 167 /** Set the matrix to rotate by the specified number of degrees, with a 168 pivot point at (px, py). The pivot point is the coordinate that should 169 remain unchanged by the specified transformation. 170 */ 171 void setRotate(SkScalar degrees, SkScalar px, SkScalar py); 172 /** Set the matrix to rotate about (0,0) by the specified number of degrees. 173 */ 174 void setRotate(SkScalar degrees); 175 /** Set the matrix to rotate by the specified sine and cosine values, with 176 a pivot point at (px, py). The pivot point is the coordinate that 177 should remain unchanged by the specified transformation. 178 */ 179 void setSinCos(SkScalar sinValue, SkScalar cosValue, 180 SkScalar px, SkScalar py); 181 /** Set the matrix to rotate by the specified sine and cosine values. 182 */ 183 void setSinCos(SkScalar sinValue, SkScalar cosValue); 184 /** Set the matrix to skew by sx and sy, with a pivot point at (px, py). 185 The pivot point is the coordinate that should remain unchanged by the 186 specified transformation. 187 */ 188 void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 189 /** Set the matrix to skew by sx and sy. 190 */ 191 void setSkew(SkScalar kx, SkScalar ky); 192 /** Set the matrix to the concatenation of the two specified matrices, 193 returning true if the the result can be represented. Either of the 194 two matrices may also be the target matrix. *this = a * b; 195 */ 196 bool setConcat(const SkMatrix& a, const SkMatrix& b); 197 198 /** Preconcats the matrix with the specified translation. 199 M' = M * T(dx, dy) 200 */ 201 bool preTranslate(SkScalar dx, SkScalar dy); 202 /** Preconcats the matrix with the specified scale. 203 M' = M * S(sx, sy, px, py) 204 */ 205 bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 206 /** Preconcats the matrix with the specified scale. 207 M' = M * S(sx, sy) 208 */ 209 bool preScale(SkScalar sx, SkScalar sy); 210 /** Preconcats the matrix with the specified rotation. 211 M' = M * R(degrees, px, py) 212 */ 213 bool preRotate(SkScalar degrees, SkScalar px, SkScalar py); 214 /** Preconcats the matrix with the specified rotation. 215 M' = M * R(degrees) 216 */ 217 bool preRotate(SkScalar degrees); 218 /** Preconcats the matrix with the specified skew. 219 M' = M * K(kx, ky, px, py) 220 */ 221 bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 222 /** Preconcats the matrix with the specified skew. 223 M' = M * K(kx, ky) 224 */ 225 bool preSkew(SkScalar kx, SkScalar ky); 226 /** Preconcats the matrix with the specified matrix. 227 M' = M * other 228 */ 229 bool preConcat(const SkMatrix& other); 230 231 /** Postconcats the matrix with the specified translation. 232 M' = T(dx, dy) * M 233 */ 234 bool postTranslate(SkScalar dx, SkScalar dy); 235 /** Postconcats the matrix with the specified scale. 236 M' = S(sx, sy, px, py) * M 237 */ 238 bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py); 239 /** Postconcats the matrix with the specified scale. 240 M' = S(sx, sy) * M 241 */ 242 bool postScale(SkScalar sx, SkScalar sy); 243 /** Postconcats the matrix by dividing it by the specified integers. 244 M' = S(1/divx, 1/divy, 0, 0) * M 245 */ 246 bool postIDiv(int divx, int divy); 247 /** Postconcats the matrix with the specified rotation. 248 M' = R(degrees, px, py) * M 249 */ 250 bool postRotate(SkScalar degrees, SkScalar px, SkScalar py); 251 /** Postconcats the matrix with the specified rotation. 252 M' = R(degrees) * M 253 */ 254 bool postRotate(SkScalar degrees); 255 /** Postconcats the matrix with the specified skew. 256 M' = K(kx, ky, px, py) * M 257 */ 258 bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py); 259 /** Postconcats the matrix with the specified skew. 260 M' = K(kx, ky) * M 261 */ 262 bool postSkew(SkScalar kx, SkScalar ky); 263 /** Postconcats the matrix with the specified matrix. 264 M' = other * M 265 */ 266 bool postConcat(const SkMatrix& other); 267 268 enum ScaleToFit { 269 /** 270 * Scale in X and Y independently, so that src matches dst exactly. 271 * This may change the aspect ratio of the src. 272 */ 273 kFill_ScaleToFit, 274 /** 275 * Compute a scale that will maintain the original src aspect ratio, 276 * but will also ensure that src fits entirely inside dst. At least one 277 * axis (X or Y) will fit exactly. kStart aligns the result to the 278 * left and top edges of dst. 279 */ 280 kStart_ScaleToFit, 281 /** 282 * Compute a scale that will maintain the original src aspect ratio, 283 * but will also ensure that src fits entirely inside dst. At least one 284 * axis (X or Y) will fit exactly. The result is centered inside dst. 285 */ 286 kCenter_ScaleToFit, 287 /** 288 * Compute a scale that will maintain the original src aspect ratio, 289 * but will also ensure that src fits entirely inside dst. At least one 290 * axis (X or Y) will fit exactly. kEnd aligns the result to the 291 * right and bottom edges of dst. 292 */ 293 kEnd_ScaleToFit 294 }; 295 296 /** Set the matrix to the scale and translate values that map the source 297 rectangle to the destination rectangle, returning true if the the result 298 can be represented. 299 @param src the source rectangle to map from. 300 @param dst the destination rectangle to map to. 301 @param stf the ScaleToFit option 302 @return true if the matrix can be represented by the rectangle mapping. 303 */ 304 bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); 305 306 /** Set the matrix such that the specified src points would map to the 307 specified dst points. count must be within [0..4]. 308 @param src The array of src points 309 @param dst The array of dst points 310 @param count The number of points to use for the transformation 311 @return true if the matrix was set to the specified transformation 312 */ 313 bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); 314 315 /** If this matrix can be inverted, return true and if inverse is not null, 316 set inverse to be the inverse of this matrix. If this matrix cannot be 317 inverted, ignore inverse and return false 318 */ 319 bool invert(SkMatrix* inverse) const; 320 321 /** Fills the passed array with the tranform values in the right order 322 for PDFs. If the matrix is a perspective transform, returns false 323 and fills the array with an identity transform. 324 @param transform The array to fill in. 325 */ 326 bool pdfTransform(SkScalar transform[6]) const; 327 328 /** Apply this matrix to the array of points specified by src, and write 329 the transformed points into the array of points specified by dst. 330 dst[] = M * src[] 331 @param dst Where the transformed coordinates are written. It must 332 contain at least count entries 333 @param src The original coordinates that are to be transformed. It 334 must contain at least count entries 335 @param count The number of points in src to read, and then transform 336 into dst. 337 */ 338 void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; 339 340 /** Apply this matrix to the array of points, overwriting it with the 341 transformed values. 342 dst[] = M * pts[] 343 @param pts The points to be transformed. It must contain at least 344 count entries 345 @param count The number of points in pts. 346 */ 347 void mapPoints(SkPoint pts[], int count) const { 348 this->mapPoints(pts, pts, count); 349 } 350 351 void mapXY(SkScalar x, SkScalar y, SkPoint* result) const { 352 SkASSERT(result); 353 this->getMapXYProc()(*this, x, y, result); 354 } 355 356 /** Apply this matrix to the array of vectors specified by src, and write 357 the transformed vectors into the array of vectors specified by dst. 358 This is similar to mapPoints, but ignores any translation in the matrix. 359 @param dst Where the transformed coordinates are written. It must 360 contain at least count entries 361 @param src The original coordinates that are to be transformed. It 362 must contain at least count entries 363 @param count The number of vectors in src to read, and then transform 364 into dst. 365 */ 366 void mapVectors(SkVector dst[], const SkVector src[], int count) const; 367 368 /** Apply this matrix to the array of vectors specified by src, and write 369 the transformed vectors into the array of vectors specified by dst. 370 This is similar to mapPoints, but ignores any translation in the matrix. 371 @param vecs The vectors to be transformed. It must contain at least 372 count entries 373 @param count The number of vectors in vecs. 374 */ 375 void mapVectors(SkVector vecs[], int count) const { 376 this->mapVectors(vecs, vecs, count); 377 } 378 379 /** Apply this matrix to the src rectangle, and write the transformed 380 rectangle into dst. This is accomplished by transforming the 4 corners 381 of src, and then setting dst to the bounds of those points. 382 @param dst Where the transformed rectangle is written. 383 @param src The original rectangle to be transformed. 384 @return the result of calling rectStaysRect() 385 */ 386 bool mapRect(SkRect* dst, const SkRect& src) const; 387 388 /** Apply this matrix to the rectangle, and write the transformed rectangle 389 back into it. This is accomplished by transforming the 4 corners of 390 rect, and then setting it to the bounds of those points 391 @param rect The rectangle to transform. 392 @return the result of calling rectStaysRect() 393 */ 394 bool mapRect(SkRect* rect) const { 395 return this->mapRect(rect, *rect); 396 } 397 398 void mapPointsWithStride(SkPoint pts[], size_t stride, int count) const { 399 for (int i = 0; i < count; ++i) { 400 this->mapPoints(pts, pts, 1); 401 pts = (SkPoint*)((intptr_t)pts + stride); 402 } 403 } 404 405 /** Return the mean radius of a circle after it has been mapped by 406 this matrix. NOTE: in perspective this value assumes the circle 407 has its center at the origin. 408 */ 409 SkScalar mapRadius(SkScalar radius) const; 410 411 typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, 412 SkPoint* result); 413 414 static MapXYProc GetMapXYProc(TypeMask mask) { 415 SkASSERT((mask & ~kAllMasks) == 0); 416 return gMapXYProcs[mask & kAllMasks]; 417 } 418 419 MapXYProc getMapXYProc() const { 420 return GetMapXYProc(this->getType()); 421 } 422 423 typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], 424 const SkPoint src[], int count); 425 426 static MapPtsProc GetMapPtsProc(TypeMask mask) { 427 SkASSERT((mask & ~kAllMasks) == 0); 428 return gMapPtsProcs[mask & kAllMasks]; 429 } 430 431 MapPtsProc getMapPtsProc() const { 432 return GetMapPtsProc(this->getType()); 433 } 434 435 /** If the matrix can be stepped in X (not complex perspective) 436 then return true and if step[XY] is not null, return the step[XY] value. 437 If it cannot, return false and ignore step. 438 */ 439 bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; 440 441 #ifdef SK_SCALAR_IS_FIXED 442 friend bool operator==(const SkMatrix& a, const SkMatrix& b) { 443 return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0; 444 } 445 446 friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { 447 return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0; 448 } 449 #else 450 friend bool operator==(const SkMatrix& a, const SkMatrix& b); 451 friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { 452 return !(a == b); 453 } 454 #endif 455 456 enum { 457 // flatten/unflatten will never return a value larger than this 458 kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t) 459 }; 460 // return the number of bytes written, whether or not buffer is null 461 uint32_t flatten(void* buffer) const; 462 // return the number of bytes read 463 uint32_t unflatten(const void* buffer); 464 465 void dump() const; 466 void toDumpString(SkString*) const; 467 468 /** 469 * Calculates the maximum stretching factor of the matrix. Only defined if 470 * the matrix does not have perspective. 471 * 472 * @return maximum strecthing factor or negative if matrix has perspective. 473 */ 474 SkScalar getMaxStretch() const; 475 476 /** 477 * Return a reference to a const identity matrix 478 */ 479 static const SkMatrix& I(); 480 481 /** 482 * Return a reference to a const matrix that is "invalid", one that could 483 * never be used. 484 */ 485 static const SkMatrix& InvalidMatrix(); 486 487 private: 488 enum { 489 /** Set if the matrix will map a rectangle to another rectangle. This 490 can be true if the matrix is scale-only, or rotates a multiple of 491 90 degrees. This bit is not set if the matrix is identity. 492 493 This bit will be set on identity matrices 494 */ 495 kRectStaysRect_Mask = 0x10, 496 497 kUnknown_Mask = 0x80, 498 499 kORableMasks = kTranslate_Mask | 500 kScale_Mask | 501 kAffine_Mask | 502 kPerspective_Mask, 503 504 kAllMasks = kTranslate_Mask | 505 kScale_Mask | 506 kAffine_Mask | 507 kPerspective_Mask | 508 kRectStaysRect_Mask 509 }; 510 511 SkScalar fMat[9]; 512 mutable uint8_t fTypeMask; 513 514 uint8_t computeTypeMask() const; 515 516 void setTypeMask(int mask) { 517 // allow kUnknown or a valid mask 518 SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask); 519 fTypeMask = SkToU8(mask); 520 } 521 522 void orTypeMask(int mask) { 523 SkASSERT((mask & kORableMasks) == mask); 524 fTypeMask = SkToU8(fTypeMask | mask); 525 } 526 527 void clearTypeMask(int mask) { 528 // only allow a valid mask 529 SkASSERT((mask & kAllMasks) == mask); 530 fTypeMask &= ~mask; 531 } 532 533 static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 534 static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 535 static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale); 536 537 static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 538 static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 539 static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 540 static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 541 static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 542 static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 543 static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); 544 545 static const MapXYProc gMapXYProcs[]; 546 547 static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); 548 static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 549 static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 550 static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], 551 int count); 552 static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 553 static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], 554 int count); 555 static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); 556 557 static const MapPtsProc gMapPtsProcs[]; 558 559 friend class SkPerspIter; 560 }; 561 562 #endif 563 564