1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h" 9cb93a386Sopenharmony_ci#include "include/core/SkString.h" 10cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h" 11cb93a386Sopenharmony_ci#include "src/core/SkBuffer.h" 12cb93a386Sopenharmony_ci#include "src/core/SkRRectPriv.h" 13cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h" 14cb93a386Sopenharmony_ci#include "src/core/SkScaleToSides.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci#include <cmath> 17cb93a386Sopenharmony_ci#include <utility> 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_civoid SkRRect::setOval(const SkRect& oval) { 22cb93a386Sopenharmony_ci if (!this->initializeRect(oval)) { 23cb93a386Sopenharmony_ci return; 24cb93a386Sopenharmony_ci } 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci SkScalar xRad = SkRectPriv::HalfWidth(fRect); 27cb93a386Sopenharmony_ci SkScalar yRad = SkRectPriv::HalfHeight(fRect); 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci if (xRad == 0.0f || yRad == 0.0f) { 30cb93a386Sopenharmony_ci // All the corners will be square 31cb93a386Sopenharmony_ci memset(fRadii, 0, sizeof(fRadii)); 32cb93a386Sopenharmony_ci fType = kRect_Type; 33cb93a386Sopenharmony_ci } else { 34cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 35cb93a386Sopenharmony_ci fRadii[i].set(xRad, yRad); 36cb93a386Sopenharmony_ci } 37cb93a386Sopenharmony_ci fType = kOval_Type; 38cb93a386Sopenharmony_ci } 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 41cb93a386Sopenharmony_ci} 42cb93a386Sopenharmony_ci 43cb93a386Sopenharmony_civoid SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 44cb93a386Sopenharmony_ci if (!this->initializeRect(rect)) { 45cb93a386Sopenharmony_ci return; 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci if (!SkScalarsAreFinite(xRad, yRad)) { 49cb93a386Sopenharmony_ci xRad = yRad = 0; // devolve into a simple rect 50cb93a386Sopenharmony_ci } 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) { 53cb93a386Sopenharmony_ci // At most one of these two divides will be by zero, and neither numerator is zero. 54cb93a386Sopenharmony_ci SkScalar scale = std::min(sk_ieee_float_divide(fRect. width(), xRad + xRad), 55cb93a386Sopenharmony_ci sk_ieee_float_divide(fRect.height(), yRad + yRad)); 56cb93a386Sopenharmony_ci SkASSERT(scale < SK_Scalar1); 57cb93a386Sopenharmony_ci xRad *= scale; 58cb93a386Sopenharmony_ci yRad *= scale; 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ci if (xRad <= 0 || yRad <= 0) { 62cb93a386Sopenharmony_ci // all corners are square in this case 63cb93a386Sopenharmony_ci this->setRect(rect); 64cb93a386Sopenharmony_ci return; 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 68cb93a386Sopenharmony_ci fRadii[i].set(xRad, yRad); 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci fType = kSimple_Type; 71cb93a386Sopenharmony_ci if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { 72cb93a386Sopenharmony_ci fType = kOval_Type; 73cb93a386Sopenharmony_ci // TODO: assert that all the x&y radii are already W/2 & H/2 74cb93a386Sopenharmony_ci } 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_civoid SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, 80cb93a386Sopenharmony_ci SkScalar rightRad, SkScalar bottomRad) { 81cb93a386Sopenharmony_ci if (!this->initializeRect(rect)) { 82cb93a386Sopenharmony_ci return; 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_ci const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; 86cb93a386Sopenharmony_ci if (!SkScalarsAreFinite(array, 4)) { 87cb93a386Sopenharmony_ci this->setRect(rect); // devolve into a simple rect 88cb93a386Sopenharmony_ci return; 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci leftRad = std::max(leftRad, 0.0f); 92cb93a386Sopenharmony_ci topRad = std::max(topRad, 0.0f); 93cb93a386Sopenharmony_ci rightRad = std::max(rightRad, 0.0f); 94cb93a386Sopenharmony_ci bottomRad = std::max(bottomRad, 0.0f); 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci SkScalar scale = SK_Scalar1; 97cb93a386Sopenharmony_ci if (leftRad + rightRad > fRect.width()) { 98cb93a386Sopenharmony_ci scale = fRect.width() / (leftRad + rightRad); 99cb93a386Sopenharmony_ci } 100cb93a386Sopenharmony_ci if (topRad + bottomRad > fRect.height()) { 101cb93a386Sopenharmony_ci scale = std::min(scale, fRect.height() / (topRad + bottomRad)); 102cb93a386Sopenharmony_ci } 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci if (scale < SK_Scalar1) { 105cb93a386Sopenharmony_ci leftRad *= scale; 106cb93a386Sopenharmony_ci topRad *= scale; 107cb93a386Sopenharmony_ci rightRad *= scale; 108cb93a386Sopenharmony_ci bottomRad *= scale; 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ci if (leftRad == rightRad && topRad == bottomRad) { 112cb93a386Sopenharmony_ci if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) { 113cb93a386Sopenharmony_ci fType = kOval_Type; 114cb93a386Sopenharmony_ci } else if (0 == leftRad || 0 == topRad) { 115cb93a386Sopenharmony_ci // If the left and (by equality check above) right radii are zero then it is a rect. 116cb93a386Sopenharmony_ci // Same goes for top/bottom. 117cb93a386Sopenharmony_ci fType = kRect_Type; 118cb93a386Sopenharmony_ci leftRad = 0; 119cb93a386Sopenharmony_ci topRad = 0; 120cb93a386Sopenharmony_ci rightRad = 0; 121cb93a386Sopenharmony_ci bottomRad = 0; 122cb93a386Sopenharmony_ci } else { 123cb93a386Sopenharmony_ci fType = kSimple_Type; 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci } else { 126cb93a386Sopenharmony_ci fType = kNinePatch_Type; 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci fRadii[kUpperLeft_Corner].set(leftRad, topRad); 130cb93a386Sopenharmony_ci fRadii[kUpperRight_Corner].set(rightRad, topRad); 131cb93a386Sopenharmony_ci fRadii[kLowerRight_Corner].set(rightRad, bottomRad); 132cb93a386Sopenharmony_ci fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 135cb93a386Sopenharmony_ci} 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci// These parameters intentionally double. Apropos crbug.com/463920, if one of the 138cb93a386Sopenharmony_ci// radii is huge while the other is small, single precision math can completely 139cb93a386Sopenharmony_ci// miss the fact that a scale is required. 140cb93a386Sopenharmony_cistatic double compute_min_scale(double rad1, double rad2, double limit, double curMin) { 141cb93a386Sopenharmony_ci if ((rad1 + rad2) > limit) { 142cb93a386Sopenharmony_ci return std::min(curMin, limit / (rad1 + rad2)); 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci return curMin; 145cb93a386Sopenharmony_ci} 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_cistatic bool clamp_to_zero(SkVector radii[4]) { 148cb93a386Sopenharmony_ci bool allCornersSquare = true; 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_ci // Clamp negative radii to zero 151cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 152cb93a386Sopenharmony_ci if (radii[i].fX <= 0 || radii[i].fY <= 0) { 153cb93a386Sopenharmony_ci // In this case we are being a little fast & loose. Since one of 154cb93a386Sopenharmony_ci // the radii is 0 the corner is square. However, the other radii 155cb93a386Sopenharmony_ci // could still be non-zero and play in the global scale factor 156cb93a386Sopenharmony_ci // computation. 157cb93a386Sopenharmony_ci radii[i].fX = 0; 158cb93a386Sopenharmony_ci radii[i].fY = 0; 159cb93a386Sopenharmony_ci } else { 160cb93a386Sopenharmony_ci allCornersSquare = false; 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci return allCornersSquare; 165cb93a386Sopenharmony_ci} 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_civoid SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { 168cb93a386Sopenharmony_ci if (!this->initializeRect(rect)) { 169cb93a386Sopenharmony_ci return; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci if (!SkScalarsAreFinite(&radii[0].fX, 8)) { 173cb93a386Sopenharmony_ci this->setRect(rect); // devolve into a simple rect 174cb93a386Sopenharmony_ci return; 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci memcpy(fRadii, radii, sizeof(fRadii)); 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ci if (clamp_to_zero(fRadii)) { 180cb93a386Sopenharmony_ci this->setRect(rect); 181cb93a386Sopenharmony_ci return; 182cb93a386Sopenharmony_ci } 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_ci this->scaleRadii(); 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci if (!this->isValid()) { 187cb93a386Sopenharmony_ci this->setRect(rect); 188cb93a386Sopenharmony_ci return; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci} 191cb93a386Sopenharmony_ci 192cb93a386Sopenharmony_cibool SkRRect::initializeRect(const SkRect& rect) { 193cb93a386Sopenharmony_ci // Check this before sorting because sorting can hide nans. 194cb93a386Sopenharmony_ci if (!rect.isFinite()) { 195cb93a386Sopenharmony_ci *this = SkRRect(); 196cb93a386Sopenharmony_ci return false; 197cb93a386Sopenharmony_ci } 198cb93a386Sopenharmony_ci fRect = rect.makeSorted(); 199cb93a386Sopenharmony_ci if (fRect.isEmpty()) { 200cb93a386Sopenharmony_ci memset(fRadii, 0, sizeof(fRadii)); 201cb93a386Sopenharmony_ci fType = kEmpty_Type; 202cb93a386Sopenharmony_ci return false; 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci return true; 205cb93a386Sopenharmony_ci} 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci// If we can't distinguish one of the radii relative to the other, force it to zero so it 208cb93a386Sopenharmony_ci// doesn't confuse us later. See crbug.com/850350 209cb93a386Sopenharmony_ci// 210cb93a386Sopenharmony_cistatic void flush_to_zero(SkScalar& a, SkScalar& b) { 211cb93a386Sopenharmony_ci SkASSERT(a >= 0); 212cb93a386Sopenharmony_ci SkASSERT(b >= 0); 213cb93a386Sopenharmony_ci if (a + b == a) { 214cb93a386Sopenharmony_ci b = 0; 215cb93a386Sopenharmony_ci } else if (a + b == b) { 216cb93a386Sopenharmony_ci a = 0; 217cb93a386Sopenharmony_ci } 218cb93a386Sopenharmony_ci} 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_cibool SkRRect::scaleRadii() { 221cb93a386Sopenharmony_ci // Proportionally scale down all radii to fit. Find the minimum ratio 222cb93a386Sopenharmony_ci // of a side and the radii on that side (for all four sides) and use 223cb93a386Sopenharmony_ci // that to scale down _all_ the radii. This algorithm is from the 224cb93a386Sopenharmony_ci // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping 225cb93a386Sopenharmony_ci // Curves: 226cb93a386Sopenharmony_ci // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, 227cb93a386Sopenharmony_ci // Si is the sum of the two corresponding radii of the corners on side i, 228cb93a386Sopenharmony_ci // and Ltop = Lbottom = the width of the box, 229cb93a386Sopenharmony_ci // and Lleft = Lright = the height of the box. 230cb93a386Sopenharmony_ci // If f < 1, then all corner radii are reduced by multiplying them by f." 231cb93a386Sopenharmony_ci double scale = 1.0; 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci // The sides of the rectangle may be larger than a float. 234cb93a386Sopenharmony_ci double width = (double)fRect.fRight - (double)fRect.fLeft; 235cb93a386Sopenharmony_ci double height = (double)fRect.fBottom - (double)fRect.fTop; 236cb93a386Sopenharmony_ci scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale); 237cb93a386Sopenharmony_ci scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale); 238cb93a386Sopenharmony_ci scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale); 239cb93a386Sopenharmony_ci scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale); 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci flush_to_zero(fRadii[0].fX, fRadii[1].fX); 242cb93a386Sopenharmony_ci flush_to_zero(fRadii[1].fY, fRadii[2].fY); 243cb93a386Sopenharmony_ci flush_to_zero(fRadii[2].fX, fRadii[3].fX); 244cb93a386Sopenharmony_ci flush_to_zero(fRadii[3].fY, fRadii[0].fY); 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_ci if (scale < 1.0) { 247cb93a386Sopenharmony_ci SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX); 248cb93a386Sopenharmony_ci SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY); 249cb93a386Sopenharmony_ci SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX); 250cb93a386Sopenharmony_ci SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY); 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci // adjust radii may set x or y to zero; set companion to zero as well 254cb93a386Sopenharmony_ci clamp_to_zero(fRadii); 255cb93a386Sopenharmony_ci 256cb93a386Sopenharmony_ci // May be simple, oval, or complex, or become a rect/empty if the radii adjustment made them 0 257cb93a386Sopenharmony_ci this->computeType(); 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci // TODO: Why can't we assert this here? 260cb93a386Sopenharmony_ci //SkASSERT(this->isValid()); 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci return scale < 1.0; 263cb93a386Sopenharmony_ci} 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci// This method determines if a point known to be inside the RRect's bounds is 266cb93a386Sopenharmony_ci// inside all the corners. 267cb93a386Sopenharmony_cibool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { 268cb93a386Sopenharmony_ci SkPoint canonicalPt; // (x,y) translated to one of the quadrants 269cb93a386Sopenharmony_ci int index; 270cb93a386Sopenharmony_ci 271cb93a386Sopenharmony_ci if (kOval_Type == this->type()) { 272cb93a386Sopenharmony_ci canonicalPt.set(x - fRect.centerX(), y - fRect.centerY()); 273cb93a386Sopenharmony_ci index = kUpperLeft_Corner; // any corner will do in this case 274cb93a386Sopenharmony_ci } else { 275cb93a386Sopenharmony_ci if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX && 276cb93a386Sopenharmony_ci y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) { 277cb93a386Sopenharmony_ci // UL corner 278cb93a386Sopenharmony_ci index = kUpperLeft_Corner; 279cb93a386Sopenharmony_ci canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX), 280cb93a386Sopenharmony_ci y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY)); 281cb93a386Sopenharmony_ci SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0); 282cb93a386Sopenharmony_ci } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX && 283cb93a386Sopenharmony_ci y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) { 284cb93a386Sopenharmony_ci // LL corner 285cb93a386Sopenharmony_ci index = kLowerLeft_Corner; 286cb93a386Sopenharmony_ci canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX), 287cb93a386Sopenharmony_ci y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY)); 288cb93a386Sopenharmony_ci SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0); 289cb93a386Sopenharmony_ci } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX && 290cb93a386Sopenharmony_ci y < fRect.fTop + fRadii[kUpperRight_Corner].fY) { 291cb93a386Sopenharmony_ci // UR corner 292cb93a386Sopenharmony_ci index = kUpperRight_Corner; 293cb93a386Sopenharmony_ci canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX), 294cb93a386Sopenharmony_ci y - (fRect.fTop + fRadii[kUpperRight_Corner].fY)); 295cb93a386Sopenharmony_ci SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0); 296cb93a386Sopenharmony_ci } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX && 297cb93a386Sopenharmony_ci y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) { 298cb93a386Sopenharmony_ci // LR corner 299cb93a386Sopenharmony_ci index = kLowerRight_Corner; 300cb93a386Sopenharmony_ci canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX), 301cb93a386Sopenharmony_ci y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY)); 302cb93a386Sopenharmony_ci SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0); 303cb93a386Sopenharmony_ci } else { 304cb93a386Sopenharmony_ci // not in any of the corners 305cb93a386Sopenharmony_ci return true; 306cb93a386Sopenharmony_ci } 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_ci // A point is in an ellipse (in standard position) if: 310cb93a386Sopenharmony_ci // x^2 y^2 311cb93a386Sopenharmony_ci // ----- + ----- <= 1 312cb93a386Sopenharmony_ci // a^2 b^2 313cb93a386Sopenharmony_ci // or : 314cb93a386Sopenharmony_ci // b^2*x^2 + a^2*y^2 <= (ab)^2 315cb93a386Sopenharmony_ci SkScalar dist = SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) + 316cb93a386Sopenharmony_ci SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX); 317cb93a386Sopenharmony_ci return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY); 318cb93a386Sopenharmony_ci} 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_cibool SkRRectPriv::IsNearlySimpleCircular(const SkRRect& rr, SkScalar tolerance) { 321cb93a386Sopenharmony_ci SkScalar simpleRadius = rr.fRadii[0].fX; 322cb93a386Sopenharmony_ci return SkScalarNearlyEqual(simpleRadius, rr.fRadii[0].fY, tolerance) && 323cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fX, tolerance) && 324cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fY, tolerance) && 325cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fX, tolerance) && 326cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fY, tolerance) && 327cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fX, tolerance) && 328cb93a386Sopenharmony_ci SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fY, tolerance); 329cb93a386Sopenharmony_ci} 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_cibool SkRRectPriv::AllCornersCircular(const SkRRect& rr, SkScalar tolerance) { 332cb93a386Sopenharmony_ci return SkScalarNearlyEqual(rr.fRadii[0].fX, rr.fRadii[0].fY, tolerance) && 333cb93a386Sopenharmony_ci SkScalarNearlyEqual(rr.fRadii[1].fX, rr.fRadii[1].fY, tolerance) && 334cb93a386Sopenharmony_ci SkScalarNearlyEqual(rr.fRadii[2].fX, rr.fRadii[2].fY, tolerance) && 335cb93a386Sopenharmony_ci SkScalarNearlyEqual(rr.fRadii[3].fX, rr.fRadii[3].fY, tolerance); 336cb93a386Sopenharmony_ci} 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_cibool SkRRect::contains(const SkRect& rect) const { 339cb93a386Sopenharmony_ci if (!this->getBounds().contains(rect)) { 340cb93a386Sopenharmony_ci // If 'rect' isn't contained by the RR's bounds then the 341cb93a386Sopenharmony_ci // RR definitely doesn't contain it 342cb93a386Sopenharmony_ci return false; 343cb93a386Sopenharmony_ci } 344cb93a386Sopenharmony_ci 345cb93a386Sopenharmony_ci if (this->isRect()) { 346cb93a386Sopenharmony_ci // the prior test was sufficient 347cb93a386Sopenharmony_ci return true; 348cb93a386Sopenharmony_ci } 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_ci // At this point we know all four corners of 'rect' are inside the 351cb93a386Sopenharmony_ci // bounds of of this RR. Check to make sure all the corners are inside 352cb93a386Sopenharmony_ci // all the curves 353cb93a386Sopenharmony_ci return this->checkCornerContainment(rect.fLeft, rect.fTop) && 354cb93a386Sopenharmony_ci this->checkCornerContainment(rect.fRight, rect.fTop) && 355cb93a386Sopenharmony_ci this->checkCornerContainment(rect.fRight, rect.fBottom) && 356cb93a386Sopenharmony_ci this->checkCornerContainment(rect.fLeft, rect.fBottom); 357cb93a386Sopenharmony_ci} 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_cistatic bool radii_are_nine_patch(const SkVector radii[4]) { 360cb93a386Sopenharmony_ci return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX && 361cb93a386Sopenharmony_ci radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY && 362cb93a386Sopenharmony_ci radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX && 363cb93a386Sopenharmony_ci radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY; 364cb93a386Sopenharmony_ci} 365cb93a386Sopenharmony_ci 366cb93a386Sopenharmony_ci// There is a simplified version of this method in setRectXY 367cb93a386Sopenharmony_civoid SkRRect::computeType() { 368cb93a386Sopenharmony_ci if (fRect.isEmpty()) { 369cb93a386Sopenharmony_ci SkASSERT(fRect.isSorted()); 370cb93a386Sopenharmony_ci for (size_t i = 0; i < SK_ARRAY_COUNT(fRadii); ++i) { 371cb93a386Sopenharmony_ci SkASSERT((fRadii[i] == SkVector{0, 0})); 372cb93a386Sopenharmony_ci } 373cb93a386Sopenharmony_ci fType = kEmpty_Type; 374cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 375cb93a386Sopenharmony_ci return; 376cb93a386Sopenharmony_ci } 377cb93a386Sopenharmony_ci 378cb93a386Sopenharmony_ci bool allRadiiEqual = true; // are all x radii equal and all y radii? 379cb93a386Sopenharmony_ci bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY; 380cb93a386Sopenharmony_ci 381cb93a386Sopenharmony_ci for (int i = 1; i < 4; ++i) { 382cb93a386Sopenharmony_ci if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 383cb93a386Sopenharmony_ci // if either radius is zero the corner is square so both have to 384cb93a386Sopenharmony_ci // be non-zero to have a rounded corner 385cb93a386Sopenharmony_ci allCornersSquare = false; 386cb93a386Sopenharmony_ci } 387cb93a386Sopenharmony_ci if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 388cb93a386Sopenharmony_ci allRadiiEqual = false; 389cb93a386Sopenharmony_ci } 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci 392cb93a386Sopenharmony_ci if (allCornersSquare) { 393cb93a386Sopenharmony_ci fType = kRect_Type; 394cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 395cb93a386Sopenharmony_ci return; 396cb93a386Sopenharmony_ci } 397cb93a386Sopenharmony_ci 398cb93a386Sopenharmony_ci if (allRadiiEqual) { 399cb93a386Sopenharmony_ci if (fRadii[0].fX >= SkScalarHalf(fRect.width()) && 400cb93a386Sopenharmony_ci fRadii[0].fY >= SkScalarHalf(fRect.height())) { 401cb93a386Sopenharmony_ci fType = kOval_Type; 402cb93a386Sopenharmony_ci } else { 403cb93a386Sopenharmony_ci fType = kSimple_Type; 404cb93a386Sopenharmony_ci } 405cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 406cb93a386Sopenharmony_ci return; 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci 409cb93a386Sopenharmony_ci if (radii_are_nine_patch(fRadii)) { 410cb93a386Sopenharmony_ci fType = kNinePatch_Type; 411cb93a386Sopenharmony_ci } else { 412cb93a386Sopenharmony_ci fType = kComplex_Type; 413cb93a386Sopenharmony_ci } 414cb93a386Sopenharmony_ci 415cb93a386Sopenharmony_ci if (!this->isValid()) { 416cb93a386Sopenharmony_ci this->setRect(this->rect()); 417cb93a386Sopenharmony_ci SkASSERT(this->isValid()); 418cb93a386Sopenharmony_ci } 419cb93a386Sopenharmony_ci} 420cb93a386Sopenharmony_ci 421cb93a386Sopenharmony_cibool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { 422cb93a386Sopenharmony_ci if (nullptr == dst) { 423cb93a386Sopenharmony_ci return false; 424cb93a386Sopenharmony_ci } 425cb93a386Sopenharmony_ci 426cb93a386Sopenharmony_ci // Assert that the caller is not trying to do this in place, which 427cb93a386Sopenharmony_ci // would violate const-ness. Do not return false though, so that 428cb93a386Sopenharmony_ci // if they know what they're doing and want to violate it they can. 429cb93a386Sopenharmony_ci SkASSERT(dst != this); 430cb93a386Sopenharmony_ci 431cb93a386Sopenharmony_ci if (matrix.isIdentity()) { 432cb93a386Sopenharmony_ci *dst = *this; 433cb93a386Sopenharmony_ci return true; 434cb93a386Sopenharmony_ci } 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci if (!matrix.preservesAxisAlignment()) { 437cb93a386Sopenharmony_ci return false; 438cb93a386Sopenharmony_ci } 439cb93a386Sopenharmony_ci 440cb93a386Sopenharmony_ci SkRect newRect; 441cb93a386Sopenharmony_ci if (!matrix.mapRect(&newRect, fRect)) { 442cb93a386Sopenharmony_ci return false; 443cb93a386Sopenharmony_ci } 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci // The matrix may have scaled us to zero (or due to float madness, we now have collapsed 446cb93a386Sopenharmony_ci // some dimension of the rect, so we need to check for that. Note that matrix must be 447cb93a386Sopenharmony_ci // scale and translate and mapRect() produces a sorted rect. So an empty rect indicates 448cb93a386Sopenharmony_ci // loss of precision. 449cb93a386Sopenharmony_ci if (!newRect.isFinite() || newRect.isEmpty()) { 450cb93a386Sopenharmony_ci return false; 451cb93a386Sopenharmony_ci } 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci // At this point, this is guaranteed to succeed, so we can modify dst. 454cb93a386Sopenharmony_ci dst->fRect = newRect; 455cb93a386Sopenharmony_ci 456cb93a386Sopenharmony_ci // Since the only transforms that were allowed are axis aligned, the type 457cb93a386Sopenharmony_ci // remains unchanged. 458cb93a386Sopenharmony_ci dst->fType = fType; 459cb93a386Sopenharmony_ci 460cb93a386Sopenharmony_ci if (kRect_Type == fType) { 461cb93a386Sopenharmony_ci SkASSERT(dst->isValid()); 462cb93a386Sopenharmony_ci return true; 463cb93a386Sopenharmony_ci } 464cb93a386Sopenharmony_ci if (kOval_Type == fType) { 465cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 466cb93a386Sopenharmony_ci dst->fRadii[i].fX = SkScalarHalf(newRect.width()); 467cb93a386Sopenharmony_ci dst->fRadii[i].fY = SkScalarHalf(newRect.height()); 468cb93a386Sopenharmony_ci } 469cb93a386Sopenharmony_ci SkASSERT(dst->isValid()); 470cb93a386Sopenharmony_ci return true; 471cb93a386Sopenharmony_ci } 472cb93a386Sopenharmony_ci 473cb93a386Sopenharmony_ci // Now scale each corner 474cb93a386Sopenharmony_ci SkScalar xScale = matrix.getScaleX(); 475cb93a386Sopenharmony_ci SkScalar yScale = matrix.getScaleY(); 476cb93a386Sopenharmony_ci 477cb93a386Sopenharmony_ci // There is a rotation of 90 (Clockwise 90) or 270 (Counter clockwise 90). 478cb93a386Sopenharmony_ci // 180 degrees rotations are simply flipX with a flipY and would come under 479cb93a386Sopenharmony_ci // a scale transform. 480cb93a386Sopenharmony_ci if (!matrix.isScaleTranslate()) { 481cb93a386Sopenharmony_ci const bool isClockwise = matrix.getSkewX() < 0; 482cb93a386Sopenharmony_ci 483cb93a386Sopenharmony_ci // The matrix location for scale changes if there is a rotation. 484cb93a386Sopenharmony_ci xScale = matrix.getSkewY() * (isClockwise ? 1 : -1); 485cb93a386Sopenharmony_ci yScale = matrix.getSkewX() * (isClockwise ? -1 : 1); 486cb93a386Sopenharmony_ci 487cb93a386Sopenharmony_ci const int dir = isClockwise ? 3 : 1; 488cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 489cb93a386Sopenharmony_ci const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir); 490cb93a386Sopenharmony_ci // Swap X and Y axis for the radii. 491cb93a386Sopenharmony_ci dst->fRadii[i].fX = fRadii[src].fY; 492cb93a386Sopenharmony_ci dst->fRadii[i].fY = fRadii[src].fX; 493cb93a386Sopenharmony_ci } 494cb93a386Sopenharmony_ci } else { 495cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 496cb93a386Sopenharmony_ci dst->fRadii[i].fX = fRadii[i].fX; 497cb93a386Sopenharmony_ci dst->fRadii[i].fY = fRadii[i].fY; 498cb93a386Sopenharmony_ci } 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci 501cb93a386Sopenharmony_ci const bool flipX = xScale < 0; 502cb93a386Sopenharmony_ci if (flipX) { 503cb93a386Sopenharmony_ci xScale = -xScale; 504cb93a386Sopenharmony_ci } 505cb93a386Sopenharmony_ci 506cb93a386Sopenharmony_ci const bool flipY = yScale < 0; 507cb93a386Sopenharmony_ci if (flipY) { 508cb93a386Sopenharmony_ci yScale = -yScale; 509cb93a386Sopenharmony_ci } 510cb93a386Sopenharmony_ci 511cb93a386Sopenharmony_ci // Scale the radii without respecting the flip. 512cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 513cb93a386Sopenharmony_ci dst->fRadii[i].fX *= xScale; 514cb93a386Sopenharmony_ci dst->fRadii[i].fY *= yScale; 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci // Now swap as necessary. 518cb93a386Sopenharmony_ci using std::swap; 519cb93a386Sopenharmony_ci if (flipX) { 520cb93a386Sopenharmony_ci if (flipY) { 521cb93a386Sopenharmony_ci // Swap with opposite corners 522cb93a386Sopenharmony_ci swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]); 523cb93a386Sopenharmony_ci swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]); 524cb93a386Sopenharmony_ci } else { 525cb93a386Sopenharmony_ci // Only swap in x 526cb93a386Sopenharmony_ci swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]); 527cb93a386Sopenharmony_ci swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]); 528cb93a386Sopenharmony_ci } 529cb93a386Sopenharmony_ci } else if (flipY) { 530cb93a386Sopenharmony_ci // Only swap in y 531cb93a386Sopenharmony_ci swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]); 532cb93a386Sopenharmony_ci swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); 533cb93a386Sopenharmony_ci } 534cb93a386Sopenharmony_ci 535cb93a386Sopenharmony_ci if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) { 536cb93a386Sopenharmony_ci return false; 537cb93a386Sopenharmony_ci } 538cb93a386Sopenharmony_ci 539cb93a386Sopenharmony_ci dst->scaleRadii(); 540cb93a386Sopenharmony_ci dst->isValid(); // TODO: is this meant to be SkASSERT(dst->isValid())? 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ci return true; 543cb93a386Sopenharmony_ci} 544cb93a386Sopenharmony_ci 545cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 546cb93a386Sopenharmony_ci 547cb93a386Sopenharmony_civoid SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 548cb93a386Sopenharmony_ci SkRect r = fRect.makeInset(dx, dy); 549cb93a386Sopenharmony_ci bool degenerate = false; 550cb93a386Sopenharmony_ci if (r.fRight <= r.fLeft) { 551cb93a386Sopenharmony_ci degenerate = true; 552cb93a386Sopenharmony_ci r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight); 553cb93a386Sopenharmony_ci } 554cb93a386Sopenharmony_ci if (r.fBottom <= r.fTop) { 555cb93a386Sopenharmony_ci degenerate = true; 556cb93a386Sopenharmony_ci r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom); 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci if (degenerate) { 559cb93a386Sopenharmony_ci dst->fRect = r; 560cb93a386Sopenharmony_ci memset(dst->fRadii, 0, sizeof(dst->fRadii)); 561cb93a386Sopenharmony_ci dst->fType = kEmpty_Type; 562cb93a386Sopenharmony_ci return; 563cb93a386Sopenharmony_ci } 564cb93a386Sopenharmony_ci if (!r.isFinite()) { 565cb93a386Sopenharmony_ci *dst = SkRRect(); 566cb93a386Sopenharmony_ci return; 567cb93a386Sopenharmony_ci } 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_ci SkVector radii[4]; 570cb93a386Sopenharmony_ci memcpy(radii, fRadii, sizeof(radii)); 571cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 572cb93a386Sopenharmony_ci if (radii[i].fX) { 573cb93a386Sopenharmony_ci radii[i].fX -= dx; 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci if (radii[i].fY) { 576cb93a386Sopenharmony_ci radii[i].fY -= dy; 577cb93a386Sopenharmony_ci } 578cb93a386Sopenharmony_ci } 579cb93a386Sopenharmony_ci dst->setRectRadii(r, radii); 580cb93a386Sopenharmony_ci} 581cb93a386Sopenharmony_ci 582cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 583cb93a386Sopenharmony_ci 584cb93a386Sopenharmony_cisize_t SkRRect::writeToMemory(void* buffer) const { 585cb93a386Sopenharmony_ci // Serialize only the rect and corners, but not the derived type tag. 586cb93a386Sopenharmony_ci memcpy(buffer, this, kSizeInMemory); 587cb93a386Sopenharmony_ci return kSizeInMemory; 588cb93a386Sopenharmony_ci} 589cb93a386Sopenharmony_ci 590cb93a386Sopenharmony_civoid SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) { 591cb93a386Sopenharmony_ci // Serialize only the rect and corners, but not the derived type tag. 592cb93a386Sopenharmony_ci buffer->write(&rr, SkRRect::kSizeInMemory); 593cb93a386Sopenharmony_ci} 594cb93a386Sopenharmony_ci 595cb93a386Sopenharmony_cisize_t SkRRect::readFromMemory(const void* buffer, size_t length) { 596cb93a386Sopenharmony_ci if (length < kSizeInMemory) { 597cb93a386Sopenharmony_ci return 0; 598cb93a386Sopenharmony_ci } 599cb93a386Sopenharmony_ci 600cb93a386Sopenharmony_ci // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect). 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_ci SkRRect raw; 603cb93a386Sopenharmony_ci memcpy((void*)&raw, buffer, kSizeInMemory); 604cb93a386Sopenharmony_ci this->setRectRadii(raw.fRect, raw.fRadii); 605cb93a386Sopenharmony_ci return kSizeInMemory; 606cb93a386Sopenharmony_ci} 607cb93a386Sopenharmony_ci 608cb93a386Sopenharmony_cibool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) { 609cb93a386Sopenharmony_ci if (buffer->available() < SkRRect::kSizeInMemory) { 610cb93a386Sopenharmony_ci return false; 611cb93a386Sopenharmony_ci } 612cb93a386Sopenharmony_ci SkRRect storage; 613cb93a386Sopenharmony_ci return buffer->read(&storage, SkRRect::kSizeInMemory) && 614cb93a386Sopenharmony_ci (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory); 615cb93a386Sopenharmony_ci} 616cb93a386Sopenharmony_ci 617cb93a386Sopenharmony_ci#include "include/core/SkString.h" 618cb93a386Sopenharmony_ci#include "src/core/SkStringUtils.h" 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ciSkString SkRRect::dumpToString(bool asHex) const { 621cb93a386Sopenharmony_ci SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 622cb93a386Sopenharmony_ci 623cb93a386Sopenharmony_ci fRect.dump(asHex); 624cb93a386Sopenharmony_ci SkString line("const SkPoint corners[] = {\n"); 625cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 626cb93a386Sopenharmony_ci SkString strX, strY; 627cb93a386Sopenharmony_ci SkAppendScalar(&strX, fRadii[i].x(), asType); 628cb93a386Sopenharmony_ci SkAppendScalar(&strY, fRadii[i].y(), asType); 629cb93a386Sopenharmony_ci line.appendf(" { %s, %s },", strX.c_str(), strY.c_str()); 630cb93a386Sopenharmony_ci if (asHex) { 631cb93a386Sopenharmony_ci line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y()); 632cb93a386Sopenharmony_ci } 633cb93a386Sopenharmony_ci line.append("\n"); 634cb93a386Sopenharmony_ci } 635cb93a386Sopenharmony_ci line.append("};"); 636cb93a386Sopenharmony_ci return line; 637cb93a386Sopenharmony_ci} 638cb93a386Sopenharmony_ci 639cb93a386Sopenharmony_civoid SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); } 640cb93a386Sopenharmony_ci 641cb93a386Sopenharmony_civoid SkRRect::dump(std::string& desc, int depth) const { 642cb93a386Sopenharmony_ci std::string split(depth, '\t'); 643cb93a386Sopenharmony_ci desc += split + "\n SkRRect:{ \n"; 644cb93a386Sopenharmony_ci 645cb93a386Sopenharmony_ci fRect.dump(desc, depth + 1); 646cb93a386Sopenharmony_ci desc += split + "\t const SkPoint corners[] = {\n"; 647cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 648cb93a386Sopenharmony_ci fRadii[i].dump(desc, depth + 1); 649cb93a386Sopenharmony_ci } 650cb93a386Sopenharmony_ci desc += split + "\t}\n"; 651cb93a386Sopenharmony_ci desc += split + "\t fType:" + std::to_string(fType) + "\n"; 652cb93a386Sopenharmony_ci desc += split + "}\n"; 653cb93a386Sopenharmony_ci} 654cb93a386Sopenharmony_ci 655cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 656cb93a386Sopenharmony_ci 657cb93a386Sopenharmony_ci/** 658cb93a386Sopenharmony_ci * We need all combinations of predicates to be true to have a "safe" radius value. 659cb93a386Sopenharmony_ci */ 660cb93a386Sopenharmony_cistatic bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) { 661cb93a386Sopenharmony_ci return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) && 662cb93a386Sopenharmony_ci rad >= 0; 663cb93a386Sopenharmony_ci} 664cb93a386Sopenharmony_ci 665cb93a386Sopenharmony_cibool SkRRect::isValid() const { 666cb93a386Sopenharmony_ci if (!AreRectAndRadiiValid(fRect, fRadii)) { 667cb93a386Sopenharmony_ci return false; 668cb93a386Sopenharmony_ci } 669cb93a386Sopenharmony_ci 670cb93a386Sopenharmony_ci bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 671cb93a386Sopenharmony_ci bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 672cb93a386Sopenharmony_ci bool allRadiiSame = true; 673cb93a386Sopenharmony_ci 674cb93a386Sopenharmony_ci for (int i = 1; i < 4; ++i) { 675cb93a386Sopenharmony_ci if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 676cb93a386Sopenharmony_ci allRadiiZero = false; 677cb93a386Sopenharmony_ci } 678cb93a386Sopenharmony_ci 679cb93a386Sopenharmony_ci if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 680cb93a386Sopenharmony_ci allRadiiSame = false; 681cb93a386Sopenharmony_ci } 682cb93a386Sopenharmony_ci 683cb93a386Sopenharmony_ci if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 684cb93a386Sopenharmony_ci allCornersSquare = false; 685cb93a386Sopenharmony_ci } 686cb93a386Sopenharmony_ci } 687cb93a386Sopenharmony_ci bool patchesOfNine = radii_are_nine_patch(fRadii); 688cb93a386Sopenharmony_ci 689cb93a386Sopenharmony_ci if (fType < 0 || fType > kLastType) { 690cb93a386Sopenharmony_ci return false; 691cb93a386Sopenharmony_ci } 692cb93a386Sopenharmony_ci 693cb93a386Sopenharmony_ci switch (fType) { 694cb93a386Sopenharmony_ci case kEmpty_Type: 695cb93a386Sopenharmony_ci if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 696cb93a386Sopenharmony_ci return false; 697cb93a386Sopenharmony_ci } 698cb93a386Sopenharmony_ci break; 699cb93a386Sopenharmony_ci case kRect_Type: 700cb93a386Sopenharmony_ci if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 701cb93a386Sopenharmony_ci return false; 702cb93a386Sopenharmony_ci } 703cb93a386Sopenharmony_ci break; 704cb93a386Sopenharmony_ci case kOval_Type: 705cb93a386Sopenharmony_ci if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 706cb93a386Sopenharmony_ci return false; 707cb93a386Sopenharmony_ci } 708cb93a386Sopenharmony_ci 709cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 710cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) || 711cb93a386Sopenharmony_ci !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) { 712cb93a386Sopenharmony_ci return false; 713cb93a386Sopenharmony_ci } 714cb93a386Sopenharmony_ci } 715cb93a386Sopenharmony_ci break; 716cb93a386Sopenharmony_ci case kSimple_Type: 717cb93a386Sopenharmony_ci if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 718cb93a386Sopenharmony_ci return false; 719cb93a386Sopenharmony_ci } 720cb93a386Sopenharmony_ci break; 721cb93a386Sopenharmony_ci case kNinePatch_Type: 722cb93a386Sopenharmony_ci if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 723cb93a386Sopenharmony_ci !patchesOfNine) { 724cb93a386Sopenharmony_ci return false; 725cb93a386Sopenharmony_ci } 726cb93a386Sopenharmony_ci break; 727cb93a386Sopenharmony_ci case kComplex_Type: 728cb93a386Sopenharmony_ci if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 729cb93a386Sopenharmony_ci patchesOfNine) { 730cb93a386Sopenharmony_ci return false; 731cb93a386Sopenharmony_ci } 732cb93a386Sopenharmony_ci break; 733cb93a386Sopenharmony_ci } 734cb93a386Sopenharmony_ci 735cb93a386Sopenharmony_ci return true; 736cb93a386Sopenharmony_ci} 737cb93a386Sopenharmony_ci 738cb93a386Sopenharmony_cibool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) { 739cb93a386Sopenharmony_ci if (!rect.isFinite() || !rect.isSorted()) { 740cb93a386Sopenharmony_ci return false; 741cb93a386Sopenharmony_ci } 742cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 743cb93a386Sopenharmony_ci if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) || 744cb93a386Sopenharmony_ci !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) { 745cb93a386Sopenharmony_ci return false; 746cb93a386Sopenharmony_ci } 747cb93a386Sopenharmony_ci } 748cb93a386Sopenharmony_ci return true; 749cb93a386Sopenharmony_ci} 750cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 751cb93a386Sopenharmony_ci 752cb93a386Sopenharmony_ciSkRect SkRRectPriv::InnerBounds(const SkRRect& rr) { 753cb93a386Sopenharmony_ci if (rr.isEmpty() || rr.isRect()) { 754cb93a386Sopenharmony_ci return rr.rect(); 755cb93a386Sopenharmony_ci } 756cb93a386Sopenharmony_ci 757cb93a386Sopenharmony_ci // We start with the outer bounds of the round rect and consider three subsets and take the 758cb93a386Sopenharmony_ci // one with maximum area. The first two are the horizontal and vertical rects inset from the 759cb93a386Sopenharmony_ci // corners, the third is the rect inscribed at the corner curves' maximal point. This forms 760cb93a386Sopenharmony_ci // the exact solution when all corners have the same radii (the radii do not have to be 761cb93a386Sopenharmony_ci // circular). 762cb93a386Sopenharmony_ci SkRect innerBounds = rr.getBounds(); 763cb93a386Sopenharmony_ci SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner); 764cb93a386Sopenharmony_ci SkVector tr = rr.radii(SkRRect::kUpperRight_Corner); 765cb93a386Sopenharmony_ci SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner); 766cb93a386Sopenharmony_ci SkVector br = rr.radii(SkRRect::kLowerRight_Corner); 767cb93a386Sopenharmony_ci 768cb93a386Sopenharmony_ci // Select maximum inset per edge, which may move an adjacent corner of the inscribed 769cb93a386Sopenharmony_ci // rectangle off of the rounded-rect path, but that is acceptable given that the general 770cb93a386Sopenharmony_ci // equation for inscribed area is non-trivial to evaluate. 771cb93a386Sopenharmony_ci SkScalar leftShift = std::max(tl.fX, bl.fX); 772cb93a386Sopenharmony_ci SkScalar topShift = std::max(tl.fY, tr.fY); 773cb93a386Sopenharmony_ci SkScalar rightShift = std::max(tr.fX, br.fX); 774cb93a386Sopenharmony_ci SkScalar bottomShift = std::max(bl.fY, br.fY); 775cb93a386Sopenharmony_ci 776cb93a386Sopenharmony_ci SkScalar dw = leftShift + rightShift; 777cb93a386Sopenharmony_ci SkScalar dh = topShift + bottomShift; 778cb93a386Sopenharmony_ci 779cb93a386Sopenharmony_ci // Area removed by shifting left/right 780cb93a386Sopenharmony_ci SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height(); 781cb93a386Sopenharmony_ci // And by shifting top/bottom 782cb93a386Sopenharmony_ci SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width(); 783cb93a386Sopenharmony_ci // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has 784cb93a386Sopenharmony_ci // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the 785cb93a386Sopenharmony_ci // safe shift per edge (since the shifts already are the max radius for that edge). 786cb93a386Sopenharmony_ci // - We actually scale by a value slightly increased to make it so that the shifted corners are 787cb93a386Sopenharmony_ci // safely inside the curves, otherwise numerical stability can cause it to fail contains(). 788cb93a386Sopenharmony_ci static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f; 789cb93a386Sopenharmony_ci SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh); 790cb93a386Sopenharmony_ci 791cb93a386Sopenharmony_ci if (horizArea > vertArea && horizArea > innerArea) { 792cb93a386Sopenharmony_ci // Cut off corners by insetting left and right 793cb93a386Sopenharmony_ci innerBounds.fLeft += leftShift; 794cb93a386Sopenharmony_ci innerBounds.fRight -= rightShift; 795cb93a386Sopenharmony_ci } else if (vertArea > innerArea) { 796cb93a386Sopenharmony_ci // Cut off corners by insetting top and bottom 797cb93a386Sopenharmony_ci innerBounds.fTop += topShift; 798cb93a386Sopenharmony_ci innerBounds.fBottom -= bottomShift; 799cb93a386Sopenharmony_ci } else if (innerArea > 0.f) { 800cb93a386Sopenharmony_ci // Inset on all sides, scaled to touch 801cb93a386Sopenharmony_ci innerBounds.fLeft += kScale * leftShift; 802cb93a386Sopenharmony_ci innerBounds.fRight -= kScale * rightShift; 803cb93a386Sopenharmony_ci innerBounds.fTop += kScale * topShift; 804cb93a386Sopenharmony_ci innerBounds.fBottom -= kScale * bottomShift; 805cb93a386Sopenharmony_ci } else { 806cb93a386Sopenharmony_ci // Inner region would collapse to empty 807cb93a386Sopenharmony_ci return SkRect::MakeEmpty(); 808cb93a386Sopenharmony_ci } 809cb93a386Sopenharmony_ci 810cb93a386Sopenharmony_ci SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty()); 811cb93a386Sopenharmony_ci return innerBounds; 812cb93a386Sopenharmony_ci} 813cb93a386Sopenharmony_ci 814cb93a386Sopenharmony_ciSkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) { 815cb93a386Sopenharmony_ci // Returns the coordinate of the rect matching the corner enum. 816cb93a386Sopenharmony_ci auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint { 817cb93a386Sopenharmony_ci switch(corner) { 818cb93a386Sopenharmony_ci case SkRRect::kUpperLeft_Corner: return {r.fLeft, r.fTop}; 819cb93a386Sopenharmony_ci case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop}; 820cb93a386Sopenharmony_ci case SkRRect::kLowerLeft_Corner: return {r.fLeft, r.fBottom}; 821cb93a386Sopenharmony_ci case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom}; 822cb93a386Sopenharmony_ci default: SkUNREACHABLE; 823cb93a386Sopenharmony_ci } 824cb93a386Sopenharmony_ci }; 825cb93a386Sopenharmony_ci // Returns true if shape A's extreme point is contained within shape B's extreme point, relative 826cb93a386Sopenharmony_ci // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this 827cb93a386Sopenharmony_ci // is sufficient for A's ellipse arc to be contained by B's ellipse arc. 828cb93a386Sopenharmony_ci auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) { 829cb93a386Sopenharmony_ci switch(corner) { 830cb93a386Sopenharmony_ci case SkRRect::kUpperLeft_Corner: return a.fX >= b.fX && a.fY >= b.fY; 831cb93a386Sopenharmony_ci case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY; 832cb93a386Sopenharmony_ci case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY; 833cb93a386Sopenharmony_ci case SkRRect::kLowerLeft_Corner: return a.fX >= b.fX && a.fY <= b.fY; 834cb93a386Sopenharmony_ci default: SkUNREACHABLE; 835cb93a386Sopenharmony_ci } 836cb93a386Sopenharmony_ci }; 837cb93a386Sopenharmony_ci 838cb93a386Sopenharmony_ci auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) { 839cb93a386Sopenharmony_ci SkPoint test = getCorner(r, corner); 840cb93a386Sopenharmony_ci SkPoint aCorner = getCorner(a.rect(), corner); 841cb93a386Sopenharmony_ci SkPoint bCorner = getCorner(b.rect(), corner); 842cb93a386Sopenharmony_ci 843cb93a386Sopenharmony_ci if (test == aCorner && test == bCorner) { 844cb93a386Sopenharmony_ci // The round rects share a corner anchor, so pick A or B such that its X and Y radii 845cb93a386Sopenharmony_ci // are both larger than the other rrect's, or return false if neither A or B has the max 846cb93a386Sopenharmony_ci // corner radii (this is more permissive than the single corner tests below). 847cb93a386Sopenharmony_ci SkVector aRadii = a.radii(corner); 848cb93a386Sopenharmony_ci SkVector bRadii = b.radii(corner); 849cb93a386Sopenharmony_ci if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) { 850cb93a386Sopenharmony_ci *radii = aRadii; 851cb93a386Sopenharmony_ci return true; 852cb93a386Sopenharmony_ci } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) { 853cb93a386Sopenharmony_ci *radii = bRadii; 854cb93a386Sopenharmony_ci return true; 855cb93a386Sopenharmony_ci } else { 856cb93a386Sopenharmony_ci return false; 857cb93a386Sopenharmony_ci } 858cb93a386Sopenharmony_ci } else if (test == aCorner) { 859cb93a386Sopenharmony_ci // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate 860cb93a386Sopenharmony_ci // so we resrict it to when the corners have the same radii. If not, we use the more 861cb93a386Sopenharmony_ci // conservative test that the extreme point of A's bounding box is contained in B. 862cb93a386Sopenharmony_ci *radii = a.radii(corner); 863cb93a386Sopenharmony_ci if (*radii == b.radii(corner)) { 864cb93a386Sopenharmony_ci return insideCorner(corner, aCorner, bCorner); // A inside B 865cb93a386Sopenharmony_ci } else { 866cb93a386Sopenharmony_ci return b.checkCornerContainment(aCorner.fX, aCorner.fY); 867cb93a386Sopenharmony_ci } 868cb93a386Sopenharmony_ci } else if (test == bCorner) { 869cb93a386Sopenharmony_ci // Mirror of the above 870cb93a386Sopenharmony_ci *radii = b.radii(corner); 871cb93a386Sopenharmony_ci if (*radii == a.radii(corner)) { 872cb93a386Sopenharmony_ci return insideCorner(corner, bCorner, aCorner); // B inside A 873cb93a386Sopenharmony_ci } else { 874cb93a386Sopenharmony_ci return a.checkCornerContainment(bCorner.fX, bCorner.fY); 875cb93a386Sopenharmony_ci } 876cb93a386Sopenharmony_ci } else { 877cb93a386Sopenharmony_ci // This is a corner formed by two straight edges of A and B, so confirm that it is 878cb93a386Sopenharmony_ci // contained in both (if not, then the intersection can't be a round rect). 879cb93a386Sopenharmony_ci *radii = {0.f, 0.f}; 880cb93a386Sopenharmony_ci return a.checkCornerContainment(test.fX, test.fY) && 881cb93a386Sopenharmony_ci b.checkCornerContainment(test.fX, test.fY); 882cb93a386Sopenharmony_ci } 883cb93a386Sopenharmony_ci }; 884cb93a386Sopenharmony_ci 885cb93a386Sopenharmony_ci // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by 886cb93a386Sopenharmony_ci // valid existing SkRRects, we know we are finite. 887cb93a386Sopenharmony_ci SkRRect intersection; 888cb93a386Sopenharmony_ci if (!intersection.fRect.intersect(a.rect(), b.rect())) { 889cb93a386Sopenharmony_ci // Definitely no intersection 890cb93a386Sopenharmony_ci return SkRRect::MakeEmpty(); 891cb93a386Sopenharmony_ci } 892cb93a386Sopenharmony_ci 893cb93a386Sopenharmony_ci const SkRRect::Corner corners[] = { 894cb93a386Sopenharmony_ci SkRRect::kUpperLeft_Corner, 895cb93a386Sopenharmony_ci SkRRect::kUpperRight_Corner, 896cb93a386Sopenharmony_ci SkRRect::kLowerRight_Corner, 897cb93a386Sopenharmony_ci SkRRect::kLowerLeft_Corner 898cb93a386Sopenharmony_ci }; 899cb93a386Sopenharmony_ci // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider 900cb93a386Sopenharmony_ci // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s. 901cb93a386Sopenharmony_ci // If the bound's corner point matches a's edges and is inside 'b', we use a's radii. 902cb93a386Sopenharmony_ci // Same for b's radii. If any corner fails these conditions, we reject the intersection as an 903cb93a386Sopenharmony_ci // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the 904cb93a386Sopenharmony_ci // intersection shape. 905cb93a386Sopenharmony_ci for (auto c : corners) { 906cb93a386Sopenharmony_ci if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) { 907cb93a386Sopenharmony_ci return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect 908cb93a386Sopenharmony_ci } 909cb93a386Sopenharmony_ci } 910cb93a386Sopenharmony_ci 911cb93a386Sopenharmony_ci // Check for radius overlap along the four edges, since the earlier evaluation was only a 912cb93a386Sopenharmony_ci // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect. 913cb93a386Sopenharmony_ci // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit. 914cb93a386Sopenharmony_ci // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means 915cb93a386Sopenharmony_ci // the intersection shape is definitively not a round rect. 916cb93a386Sopenharmony_ci if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) || 917cb93a386Sopenharmony_ci intersection.scaleRadii()) { 918cb93a386Sopenharmony_ci return SkRRect::MakeEmpty(); 919cb93a386Sopenharmony_ci } 920cb93a386Sopenharmony_ci 921cb93a386Sopenharmony_ci // The intersection is an rrect of the given radii. Potentially all 4 corners could have 922cb93a386Sopenharmony_ci // been simplified to (0,0) radii, making the intersection a rectangle. 923cb93a386Sopenharmony_ci intersection.computeType(); 924cb93a386Sopenharmony_ci return intersection; 925cb93a386Sopenharmony_ci} 926