1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/core/SkRRect.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,431 @@ 1.4 +/* 1.5 + * Copyright 2012 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkRRect.h" 1.12 +#include "SkMatrix.h" 1.13 + 1.14 +/////////////////////////////////////////////////////////////////////////////// 1.15 + 1.16 +void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 1.17 + if (rect.isEmpty()) { 1.18 + this->setEmpty(); 1.19 + return; 1.20 + } 1.21 + 1.22 + if (xRad <= 0 || yRad <= 0) { 1.23 + // all corners are square in this case 1.24 + this->setRect(rect); 1.25 + return; 1.26 + } 1.27 + 1.28 + if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) { 1.29 + SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad), 1.30 + SkScalarDiv(rect.height(), yRad + yRad)); 1.31 + SkASSERT(scale < SK_Scalar1); 1.32 + xRad = SkScalarMul(xRad, scale); 1.33 + yRad = SkScalarMul(yRad, scale); 1.34 + } 1.35 + 1.36 + fRect = rect; 1.37 + for (int i = 0; i < 4; ++i) { 1.38 + fRadii[i].set(xRad, yRad); 1.39 + } 1.40 + fType = kSimple_Type; 1.41 + if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { 1.42 + fType = kOval_Type; 1.43 + // TODO: assert that all the x&y radii are already W/2 & H/2 1.44 + } 1.45 + 1.46 + SkDEBUGCODE(this->validate();) 1.47 +} 1.48 + 1.49 +void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { 1.50 + if (rect.isEmpty()) { 1.51 + this->setEmpty(); 1.52 + return; 1.53 + } 1.54 + 1.55 + fRect = rect; 1.56 + memcpy(fRadii, radii, sizeof(fRadii)); 1.57 + 1.58 + bool allCornersSquare = true; 1.59 + 1.60 + // Clamp negative radii to zero 1.61 + for (int i = 0; i < 4; ++i) { 1.62 + if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) { 1.63 + // In this case we are being a little fast & loose. Since one of 1.64 + // the radii is 0 the corner is square. However, the other radii 1.65 + // could still be non-zero and play in the global scale factor 1.66 + // computation. 1.67 + fRadii[i].fX = 0; 1.68 + fRadii[i].fY = 0; 1.69 + } else { 1.70 + allCornersSquare = false; 1.71 + } 1.72 + } 1.73 + 1.74 + if (allCornersSquare) { 1.75 + this->setRect(rect); 1.76 + return; 1.77 + } 1.78 + 1.79 + // Proportionally scale down all radii to fit. Find the minimum ratio 1.80 + // of a side and the radii on that side (for all four sides) and use 1.81 + // that to scale down _all_ the radii. This algorithm is from the 1.82 + // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping 1.83 + // Curves: 1.84 + // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, 1.85 + // Si is the sum of the two corresponding radii of the corners on side i, 1.86 + // and Ltop = Lbottom = the width of the box, 1.87 + // and Lleft = Lright = the height of the box. 1.88 + // If f < 1, then all corner radii are reduced by multiplying them by f." 1.89 + SkScalar scale = SK_Scalar1; 1.90 + 1.91 + if (fRadii[0].fX + fRadii[1].fX > rect.width()) { 1.92 + scale = SkMinScalar(scale, 1.93 + SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX)); 1.94 + } 1.95 + if (fRadii[1].fY + fRadii[2].fY > rect.height()) { 1.96 + scale = SkMinScalar(scale, 1.97 + SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY)); 1.98 + } 1.99 + if (fRadii[2].fX + fRadii[3].fX > rect.width()) { 1.100 + scale = SkMinScalar(scale, 1.101 + SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX)); 1.102 + } 1.103 + if (fRadii[3].fY + fRadii[0].fY > rect.height()) { 1.104 + scale = SkMinScalar(scale, 1.105 + SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY)); 1.106 + } 1.107 + 1.108 + if (scale < SK_Scalar1) { 1.109 + for (int i = 0; i < 4; ++i) { 1.110 + fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale); 1.111 + fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale); 1.112 + } 1.113 + } 1.114 + 1.115 + // At this point we're either oval, simple, or complex (not empty or rect) 1.116 + // but we lazily resolve the type to avoid the work if the information 1.117 + // isn't required. 1.118 + fType = (SkRRect::Type) kUnknown_Type; 1.119 + 1.120 + SkDEBUGCODE(this->validate();) 1.121 +} 1.122 + 1.123 +// This method determines if a point known to be inside the RRect's bounds is 1.124 +// inside all the corners. 1.125 +bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { 1.126 + SkPoint canonicalPt; // (x,y) translated to one of the quadrants 1.127 + int index; 1.128 + 1.129 + if (kOval_Type == this->type()) { 1.130 + canonicalPt.set(x - fRect.centerX(), y - fRect.centerY()); 1.131 + index = kUpperLeft_Corner; // any corner will do in this case 1.132 + } else { 1.133 + if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX && 1.134 + y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) { 1.135 + // UL corner 1.136 + index = kUpperLeft_Corner; 1.137 + canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX), 1.138 + y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY)); 1.139 + SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0); 1.140 + } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX && 1.141 + y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) { 1.142 + // LL corner 1.143 + index = kLowerLeft_Corner; 1.144 + canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX), 1.145 + y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY)); 1.146 + SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0); 1.147 + } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX && 1.148 + y < fRect.fTop + fRadii[kUpperRight_Corner].fY) { 1.149 + // UR corner 1.150 + index = kUpperRight_Corner; 1.151 + canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX), 1.152 + y - (fRect.fTop + fRadii[kUpperRight_Corner].fY)); 1.153 + SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0); 1.154 + } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX && 1.155 + y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) { 1.156 + // LR corner 1.157 + index = kLowerRight_Corner; 1.158 + canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX), 1.159 + y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY)); 1.160 + SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0); 1.161 + } else { 1.162 + // not in any of the corners 1.163 + return true; 1.164 + } 1.165 + } 1.166 + 1.167 + // A point is in an ellipse (in standard position) if: 1.168 + // x^2 y^2 1.169 + // ----- + ----- <= 1 1.170 + // a^2 b^2 1.171 + // or : 1.172 + // b^2*x^2 + a^2*y^2 <= (ab)^2 1.173 + SkScalar dist = SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) + 1.174 + SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX)); 1.175 + return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY)); 1.176 +} 1.177 + 1.178 +bool SkRRect::allCornersCircular() const { 1.179 + return fRadii[0].fX == fRadii[0].fY && 1.180 + fRadii[1].fX == fRadii[1].fY && 1.181 + fRadii[2].fX == fRadii[2].fY && 1.182 + fRadii[3].fX == fRadii[3].fY; 1.183 +} 1.184 + 1.185 +bool SkRRect::contains(const SkRect& rect) const { 1.186 + if (!this->getBounds().contains(rect)) { 1.187 + // If 'rect' isn't contained by the RR's bounds then the 1.188 + // RR definitely doesn't contain it 1.189 + return false; 1.190 + } 1.191 + 1.192 + if (this->isRect()) { 1.193 + // the prior test was sufficient 1.194 + return true; 1.195 + } 1.196 + 1.197 + // At this point we know all four corners of 'rect' are inside the 1.198 + // bounds of of this RR. Check to make sure all the corners are inside 1.199 + // all the curves 1.200 + return this->checkCornerContainment(rect.fLeft, rect.fTop) && 1.201 + this->checkCornerContainment(rect.fRight, rect.fTop) && 1.202 + this->checkCornerContainment(rect.fRight, rect.fBottom) && 1.203 + this->checkCornerContainment(rect.fLeft, rect.fBottom); 1.204 +} 1.205 + 1.206 +// There is a simplified version of this method in setRectXY 1.207 +void SkRRect::computeType() const { 1.208 + SkDEBUGCODE(this->validate();) 1.209 + 1.210 + if (fRect.isEmpty()) { 1.211 + fType = kEmpty_Type; 1.212 + return; 1.213 + } 1.214 + 1.215 + bool allRadiiEqual = true; // are all x radii equal and all y radii? 1.216 + bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY; 1.217 + 1.218 + for (int i = 1; i < 4; ++i) { 1.219 + if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 1.220 + // if either radius is zero the corner is square so both have to 1.221 + // be non-zero to have a rounded corner 1.222 + allCornersSquare = false; 1.223 + } 1.224 + if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 1.225 + allRadiiEqual = false; 1.226 + } 1.227 + } 1.228 + 1.229 + if (allCornersSquare) { 1.230 + fType = kRect_Type; 1.231 + return; 1.232 + } 1.233 + 1.234 + if (allRadiiEqual) { 1.235 + if (fRadii[0].fX >= SkScalarHalf(fRect.width()) && 1.236 + fRadii[0].fY >= SkScalarHalf(fRect.height())) { 1.237 + fType = kOval_Type; 1.238 + } else { 1.239 + fType = kSimple_Type; 1.240 + } 1.241 + return; 1.242 + } 1.243 + 1.244 + fType = kComplex_Type; 1.245 +} 1.246 + 1.247 +static bool matrix_only_scale_and_translate(const SkMatrix& matrix) { 1.248 + const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask 1.249 + | SkMatrix::kPerspective_Mask); 1.250 + return (matrix.getType() & m) == 0; 1.251 +} 1.252 + 1.253 +bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { 1.254 + if (NULL == dst) { 1.255 + return false; 1.256 + } 1.257 + 1.258 + // Assert that the caller is not trying to do this in place, which 1.259 + // would violate const-ness. Do not return false though, so that 1.260 + // if they know what they're doing and want to violate it they can. 1.261 + SkASSERT(dst != this); 1.262 + 1.263 + if (matrix.isIdentity()) { 1.264 + *dst = *this; 1.265 + return true; 1.266 + } 1.267 + 1.268 + // If transform supported 90 degree rotations (which it could), we could 1.269 + // use SkMatrix::rectStaysRect() to check for a valid transformation. 1.270 + if (!matrix_only_scale_and_translate(matrix)) { 1.271 + return false; 1.272 + } 1.273 + 1.274 + SkRect newRect; 1.275 + if (!matrix.mapRect(&newRect, fRect)) { 1.276 + return false; 1.277 + } 1.278 + 1.279 + // At this point, this is guaranteed to succeed, so we can modify dst. 1.280 + dst->fRect = newRect; 1.281 + 1.282 + // Now scale each corner 1.283 + SkScalar xScale = matrix.getScaleX(); 1.284 + const bool flipX = xScale < 0; 1.285 + if (flipX) { 1.286 + xScale = -xScale; 1.287 + } 1.288 + SkScalar yScale = matrix.getScaleY(); 1.289 + const bool flipY = yScale < 0; 1.290 + if (flipY) { 1.291 + yScale = -yScale; 1.292 + } 1.293 + 1.294 + // Scale the radii without respecting the flip. 1.295 + for (int i = 0; i < 4; ++i) { 1.296 + dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale); 1.297 + dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale); 1.298 + } 1.299 + 1.300 + // Now swap as necessary. 1.301 + if (flipX) { 1.302 + if (flipY) { 1.303 + // Swap with opposite corners 1.304 + SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]); 1.305 + SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]); 1.306 + } else { 1.307 + // Only swap in x 1.308 + SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]); 1.309 + SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]); 1.310 + } 1.311 + } else if (flipY) { 1.312 + // Only swap in y 1.313 + SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]); 1.314 + SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); 1.315 + } 1.316 + 1.317 + // Since the only transforms that were allowed are scale and translate, the type 1.318 + // remains unchanged. 1.319 + dst->fType = fType; 1.320 + 1.321 + SkDEBUGCODE(dst->validate();) 1.322 + 1.323 + return true; 1.324 +} 1.325 + 1.326 +/////////////////////////////////////////////////////////////////////////////// 1.327 + 1.328 +void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 1.329 + SkRect r = fRect; 1.330 + 1.331 + r.inset(dx, dy); 1.332 + if (r.isEmpty()) { 1.333 + dst->setEmpty(); 1.334 + return; 1.335 + } 1.336 + 1.337 + SkVector radii[4]; 1.338 + memcpy(radii, fRadii, sizeof(radii)); 1.339 + for (int i = 0; i < 4; ++i) { 1.340 + if (radii[i].fX) { 1.341 + radii[i].fX -= dx; 1.342 + } 1.343 + if (radii[i].fY) { 1.344 + radii[i].fY -= dy; 1.345 + } 1.346 + } 1.347 + dst->setRectRadii(r, radii); 1.348 +} 1.349 + 1.350 +/////////////////////////////////////////////////////////////////////////////// 1.351 + 1.352 +size_t SkRRect::writeToMemory(void* buffer) const { 1.353 + SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii)); 1.354 + 1.355 + memcpy(buffer, &fRect, sizeof(SkRect)); 1.356 + memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii)); 1.357 + return kSizeInMemory; 1.358 +} 1.359 + 1.360 +size_t SkRRect::readFromMemory(const void* buffer, size_t length) { 1.361 + if (length < kSizeInMemory) { 1.362 + return 0; 1.363 + } 1.364 + 1.365 + SkScalar storage[12]; 1.366 + SkASSERT(sizeof(storage) == kSizeInMemory); 1.367 + 1.368 + // we make a local copy, to ensure alignment before we cast 1.369 + memcpy(storage, buffer, kSizeInMemory); 1.370 + 1.371 + this->setRectRadii(*(const SkRect*)&storage[0], 1.372 + (const SkVector*)&storage[4]); 1.373 + return kSizeInMemory; 1.374 +} 1.375 + 1.376 +/////////////////////////////////////////////////////////////////////////////// 1.377 + 1.378 +#ifdef SK_DEBUG 1.379 +void SkRRect::validate() const { 1.380 + bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 1.381 + bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 1.382 + bool allRadiiSame = true; 1.383 + 1.384 + for (int i = 1; i < 4; ++i) { 1.385 + if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 1.386 + allRadiiZero = false; 1.387 + } 1.388 + 1.389 + if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 1.390 + allRadiiSame = false; 1.391 + } 1.392 + 1.393 + if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 1.394 + allCornersSquare = false; 1.395 + } 1.396 + } 1.397 + 1.398 + switch (fType) { 1.399 + case kEmpty_Type: 1.400 + SkASSERT(fRect.isEmpty()); 1.401 + SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 1.402 + 1.403 + SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop && 1.404 + 0 == fRect.fRight && 0 == fRect.fBottom); 1.405 + break; 1.406 + case kRect_Type: 1.407 + SkASSERT(!fRect.isEmpty()); 1.408 + SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 1.409 + break; 1.410 + case kOval_Type: 1.411 + SkASSERT(!fRect.isEmpty()); 1.412 + SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 1.413 + 1.414 + for (int i = 0; i < 4; ++i) { 1.415 + SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width()))); 1.416 + SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))); 1.417 + } 1.418 + break; 1.419 + case kSimple_Type: 1.420 + SkASSERT(!fRect.isEmpty()); 1.421 + SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 1.422 + break; 1.423 + case kComplex_Type: 1.424 + SkASSERT(!fRect.isEmpty()); 1.425 + SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare); 1.426 + break; 1.427 + case kUnknown_Type: 1.428 + // no limits on this 1.429 + break; 1.430 + } 1.431 +} 1.432 +#endif // SK_DEBUG 1.433 + 1.434 +///////////////////////////////////////////////////////////////////////////////