1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 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 "src/gpu/geometry/GrShape.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h" 11cb93a386Sopenharmony_ci#include "src/core/SkRRectPriv.h" 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ciGrShape& GrShape::operator=(const GrShape& shape) { 14cb93a386Sopenharmony_ci switch (shape.type()) { 15cb93a386Sopenharmony_ci case Type::kEmpty: 16cb93a386Sopenharmony_ci this->reset(); 17cb93a386Sopenharmony_ci break; 18cb93a386Sopenharmony_ci case Type::kPoint: 19cb93a386Sopenharmony_ci this->setPoint(shape.fPoint); 20cb93a386Sopenharmony_ci break; 21cb93a386Sopenharmony_ci case Type::kRect: 22cb93a386Sopenharmony_ci this->setRect(shape.fRect); 23cb93a386Sopenharmony_ci break; 24cb93a386Sopenharmony_ci case Type::kRRect: 25cb93a386Sopenharmony_ci this->setRRect(shape.fRRect); 26cb93a386Sopenharmony_ci break; 27cb93a386Sopenharmony_ci case Type::kPath: 28cb93a386Sopenharmony_ci this->setPath(shape.fPath); 29cb93a386Sopenharmony_ci break; 30cb93a386Sopenharmony_ci case Type::kArc: 31cb93a386Sopenharmony_ci this->setArc(shape.fArc); 32cb93a386Sopenharmony_ci break; 33cb93a386Sopenharmony_ci case Type::kLine: 34cb93a386Sopenharmony_ci this->setLine(shape.fLine); 35cb93a386Sopenharmony_ci break; 36cb93a386Sopenharmony_ci } 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci fStart = shape.fStart; 39cb93a386Sopenharmony_ci fCW = shape.fCW; 40cb93a386Sopenharmony_ci fInverted = shape.fInverted; 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci return *this; 43cb93a386Sopenharmony_ci} 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ciuint32_t GrShape::stateKey() const { 46cb93a386Sopenharmony_ci // Use the path's full fill type instead of just whether or not it's inverted. 47cb93a386Sopenharmony_ci uint32_t key = this->isPath() ? static_cast<uint32_t>(fPath.getFillType()) 48cb93a386Sopenharmony_ci : (fInverted ? 1 : 0); 49cb93a386Sopenharmony_ci key |= ((uint32_t) fType) << 2; // fill type was 2 bits 50cb93a386Sopenharmony_ci key |= fStart << 5; // type was 3 bits, total 5 bits so far 51cb93a386Sopenharmony_ci key |= (fCW ? 1 : 0) << 8; // start was 3 bits, total 8 bits so far 52cb93a386Sopenharmony_ci return key; 53cb93a386Sopenharmony_ci} 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_cibool GrShape::simplifyPath(unsigned flags) { 56cb93a386Sopenharmony_ci SkASSERT(this->isPath()); 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci SkRect rect; 59cb93a386Sopenharmony_ci SkRRect rrect; 60cb93a386Sopenharmony_ci SkPoint pts[2]; 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci SkPathDirection dir; 63cb93a386Sopenharmony_ci unsigned start; 64cb93a386Sopenharmony_ci 65cb93a386Sopenharmony_ci if (fPath.isEmpty()) { 66cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 67cb93a386Sopenharmony_ci return false; 68cb93a386Sopenharmony_ci } else if (fPath.isLine(pts)) { 69cb93a386Sopenharmony_ci this->simplifyLine(pts[0], pts[1], flags); 70cb93a386Sopenharmony_ci return false; 71cb93a386Sopenharmony_ci } else if (SkPathPriv::IsRRect(fPath, &rrect, &dir, &start)) { 72cb93a386Sopenharmony_ci this->simplifyRRect(rrect, dir, start, flags); 73cb93a386Sopenharmony_ci return true; 74cb93a386Sopenharmony_ci } else if (SkPathPriv::IsOval(fPath, &rect, &dir, &start)) { 75cb93a386Sopenharmony_ci // Convert to rrect indexing since oval is not represented explicitly 76cb93a386Sopenharmony_ci this->simplifyRRect(SkRRect::MakeOval(rect), dir, start * 2, flags); 77cb93a386Sopenharmony_ci return true; 78cb93a386Sopenharmony_ci } else if (SkPathPriv::IsSimpleRect(fPath, (flags & kSimpleFill_Flag), &rect, &dir, &start)) { 79cb93a386Sopenharmony_ci // When there is a path effect we restrict rect detection to the narrower API that 80cb93a386Sopenharmony_ci // gives us the starting position. Otherwise, we will retry with the more aggressive 81cb93a386Sopenharmony_ci // isRect(). 82cb93a386Sopenharmony_ci this->simplifyRect(rect, dir, start, flags); 83cb93a386Sopenharmony_ci return true; 84cb93a386Sopenharmony_ci } else if (flags & kIgnoreWinding_Flag) { 85cb93a386Sopenharmony_ci // Attempt isRect() since we don't have to preserve any winding info 86cb93a386Sopenharmony_ci bool closed; 87cb93a386Sopenharmony_ci if (fPath.isRect(&rect, &closed) && (closed || (flags & kSimpleFill_Flag))) { 88cb93a386Sopenharmony_ci this->simplifyRect(rect, kDefaultDir, kDefaultStart, flags); 89cb93a386Sopenharmony_ci return true; 90cb93a386Sopenharmony_ci } 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci // No further simplification for a path. For performance reasons, we don't query the path to 93cb93a386Sopenharmony_ci // determine it was closed, as whether or not it was closed when it remains a path type is not 94cb93a386Sopenharmony_ci // important for styling. 95cb93a386Sopenharmony_ci return false; 96cb93a386Sopenharmony_ci} 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_cibool GrShape::simplifyArc(unsigned flags) { 99cb93a386Sopenharmony_ci SkASSERT(this->isArc()); 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci // Arcs can simplify to rrects, lines, points, or empty; regardless of what it simplifies to 102cb93a386Sopenharmony_ci // it was closed if went through the center point. 103cb93a386Sopenharmony_ci bool wasClosed = fArc.fUseCenter; 104cb93a386Sopenharmony_ci if (fArc.fOval.isEmpty() || !fArc.fSweepAngle) { 105cb93a386Sopenharmony_ci if (flags & kSimpleFill_Flag) { 106cb93a386Sopenharmony_ci // Go straight to empty, since the other degenerate shapes all have 0 area anyway. 107cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 108cb93a386Sopenharmony_ci } else if (!fArc.fSweepAngle) { 109cb93a386Sopenharmony_ci SkPoint center = {fArc.fOval.centerX(), fArc.fOval.centerY()}; 110cb93a386Sopenharmony_ci SkScalar startRad = SkDegreesToRadians(fArc.fStartAngle); 111cb93a386Sopenharmony_ci SkPoint start = {center.fX + 0.5f * fArc.fOval.width() * SkScalarCos(startRad), 112cb93a386Sopenharmony_ci center.fY + 0.5f * fArc.fOval.height() * SkScalarSin(startRad)}; 113cb93a386Sopenharmony_ci // Either just the starting point, or a line from the center to the start 114cb93a386Sopenharmony_ci if (fArc.fUseCenter) { 115cb93a386Sopenharmony_ci this->simplifyLine(center, start, flags); 116cb93a386Sopenharmony_ci } else { 117cb93a386Sopenharmony_ci this->simplifyPoint(start, flags); 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci } else { 120cb93a386Sopenharmony_ci // TODO: Theoretically, we could analyze the arc projected into the empty bounds to 121cb93a386Sopenharmony_ci // determine a line, but that is somewhat complex for little value (since the arc 122cb93a386Sopenharmony_ci // can backtrack on itself if the sweep angle is large enough). 123cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci } else { 126cb93a386Sopenharmony_ci if ((flags & kSimpleFill_Flag) || ((flags & kIgnoreWinding_Flag) && !fArc.fUseCenter)) { 127cb93a386Sopenharmony_ci // Eligible to turn into an oval if it sweeps a full circle 128cb93a386Sopenharmony_ci if (fArc.fSweepAngle <= -360.f || fArc.fSweepAngle >= 360.f) { 129cb93a386Sopenharmony_ci this->simplifyRRect(SkRRect::MakeOval(fArc.fOval), 130cb93a386Sopenharmony_ci kDefaultDir, kDefaultStart, flags); 131cb93a386Sopenharmony_ci return true; 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci } 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci if (flags & kMakeCanonical_Flag) { 136cb93a386Sopenharmony_ci // Map start to 0 to 360, sweep is always positive 137cb93a386Sopenharmony_ci if (fArc.fSweepAngle < 0) { 138cb93a386Sopenharmony_ci fArc.fStartAngle = fArc.fStartAngle + fArc.fSweepAngle; 139cb93a386Sopenharmony_ci fArc.fSweepAngle = -fArc.fSweepAngle; 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci if (fArc.fStartAngle < 0 || fArc.fStartAngle >= 360.f) { 143cb93a386Sopenharmony_ci fArc.fStartAngle = SkScalarMod(fArc.fStartAngle, 360.f); 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci return wasClosed; 149cb93a386Sopenharmony_ci} 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_civoid GrShape::simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, 152cb93a386Sopenharmony_ci unsigned flags) { 153cb93a386Sopenharmony_ci if (rrect.isEmpty() || rrect.isRect()) { 154cb93a386Sopenharmony_ci // Change index from rrect to rect 155cb93a386Sopenharmony_ci start = ((start + 1) / 2) % 4; 156cb93a386Sopenharmony_ci this->simplifyRect(rrect.rect(), dir, start, flags); 157cb93a386Sopenharmony_ci } else if (!this->isRRect()) { 158cb93a386Sopenharmony_ci this->setType(Type::kRRect); 159cb93a386Sopenharmony_ci fRRect = rrect; 160cb93a386Sopenharmony_ci this->setPathWindingParams(dir, start); 161cb93a386Sopenharmony_ci // A round rect is already canonical, so there's nothing more to do 162cb93a386Sopenharmony_ci } else { 163cb93a386Sopenharmony_ci // If starting as a round rect, the provided rrect/winding params should be already set 164cb93a386Sopenharmony_ci SkASSERT(fRRect == rrect && this->dir() == dir && this->startIndex() == start); 165cb93a386Sopenharmony_ci } 166cb93a386Sopenharmony_ci} 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_civoid GrShape::simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, 169cb93a386Sopenharmony_ci unsigned flags) { 170cb93a386Sopenharmony_ci if (!rect.width() || !rect.height()) { 171cb93a386Sopenharmony_ci if (flags & kSimpleFill_Flag) { 172cb93a386Sopenharmony_ci // A zero area, filled shape so go straight to empty 173cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 174cb93a386Sopenharmony_ci } else if (!rect.width() ^ !rect.height()) { 175cb93a386Sopenharmony_ci // A line, choose the first point that best matches the starting index 176cb93a386Sopenharmony_ci SkPoint p1 = {rect.fLeft, rect.fTop}; 177cb93a386Sopenharmony_ci SkPoint p2 = {rect.fRight, rect.fBottom}; 178cb93a386Sopenharmony_ci if (start >= 2 && !(flags & kIgnoreWinding_Flag)) { 179cb93a386Sopenharmony_ci using std::swap; 180cb93a386Sopenharmony_ci swap(p1, p2); 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci this->simplifyLine(p1, p2, flags); 183cb93a386Sopenharmony_ci } else { 184cb93a386Sopenharmony_ci // A point (all edges are equal, so start+dir doesn't affect choice) 185cb93a386Sopenharmony_ci this->simplifyPoint({rect.fLeft, rect.fTop}, flags); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci } else { 188cb93a386Sopenharmony_ci if (!this->isRect()) { 189cb93a386Sopenharmony_ci this->setType(Type::kRect); 190cb93a386Sopenharmony_ci fRect = rect; 191cb93a386Sopenharmony_ci this->setPathWindingParams(dir, start); 192cb93a386Sopenharmony_ci } else { 193cb93a386Sopenharmony_ci // If starting as a rect, the provided rect/winding params should already be set 194cb93a386Sopenharmony_ci SkASSERT(fRect == rect && this->dir() == dir && this->startIndex() == start); 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci if (flags & kMakeCanonical_Flag) { 197cb93a386Sopenharmony_ci fRect.sort(); 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci} 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_civoid GrShape::simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags) { 203cb93a386Sopenharmony_ci if (flags & kSimpleFill_Flag) { 204cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 205cb93a386Sopenharmony_ci } else if (p1 == p2) { 206cb93a386Sopenharmony_ci this->simplifyPoint(p1, false); 207cb93a386Sopenharmony_ci } else { 208cb93a386Sopenharmony_ci if (!this->isLine()) { 209cb93a386Sopenharmony_ci this->setType(Type::kLine); 210cb93a386Sopenharmony_ci fLine.fP1 = p1; 211cb93a386Sopenharmony_ci fLine.fP2 = p2; 212cb93a386Sopenharmony_ci } else { 213cb93a386Sopenharmony_ci // If starting as a line, the provided points should already be set 214cb93a386Sopenharmony_ci SkASSERT(fLine.fP1 == p1 && fLine.fP2 == p2); 215cb93a386Sopenharmony_ci } 216cb93a386Sopenharmony_ci if (flags & kMakeCanonical_Flag) { 217cb93a386Sopenharmony_ci // Sort the end points 218cb93a386Sopenharmony_ci if (fLine.fP2.fY < fLine.fP1.fY || 219cb93a386Sopenharmony_ci (fLine.fP2.fY == fLine.fP1.fY && fLine.fP2.fX < fLine.fP1.fX)) { 220cb93a386Sopenharmony_ci using std::swap; 221cb93a386Sopenharmony_ci swap(fLine.fP1, fLine.fP2); 222cb93a386Sopenharmony_ci } 223cb93a386Sopenharmony_ci } 224cb93a386Sopenharmony_ci } 225cb93a386Sopenharmony_ci} 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_civoid GrShape::simplifyPoint(const SkPoint& point, unsigned flags) { 228cb93a386Sopenharmony_ci if (flags & kSimpleFill_Flag) { 229cb93a386Sopenharmony_ci this->setType(Type::kEmpty); 230cb93a386Sopenharmony_ci } else if (!this->isPoint()) { 231cb93a386Sopenharmony_ci this->setType(Type::kPoint); 232cb93a386Sopenharmony_ci fPoint = point; 233cb93a386Sopenharmony_ci } else { 234cb93a386Sopenharmony_ci // If starting as a point, the provided position should already be set 235cb93a386Sopenharmony_ci SkASSERT(point == fPoint); 236cb93a386Sopenharmony_ci } 237cb93a386Sopenharmony_ci} 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_cibool GrShape::simplify(unsigned flags) { 240cb93a386Sopenharmony_ci // Verify that winding parameters are valid for the current type. 241cb93a386Sopenharmony_ci SkASSERT((fType == Type::kRect || fType == Type::kRRect) || 242cb93a386Sopenharmony_ci (this->dir() == kDefaultDir && this->startIndex() == kDefaultStart)); 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci // The type specific functions automatically fall through to the simpler shapes, so 245cb93a386Sopenharmony_ci // we only need to start in the right place. 246cb93a386Sopenharmony_ci bool wasClosed = false; 247cb93a386Sopenharmony_ci switch (fType) { 248cb93a386Sopenharmony_ci case Type::kEmpty: 249cb93a386Sopenharmony_ci // do nothing 250cb93a386Sopenharmony_ci break; 251cb93a386Sopenharmony_ci case Type::kPoint: 252cb93a386Sopenharmony_ci this->simplifyPoint(fPoint, flags); 253cb93a386Sopenharmony_ci break; 254cb93a386Sopenharmony_ci case Type::kLine: 255cb93a386Sopenharmony_ci this->simplifyLine(fLine.fP1, fLine.fP2, flags); 256cb93a386Sopenharmony_ci break; 257cb93a386Sopenharmony_ci case Type::kRect: 258cb93a386Sopenharmony_ci this->simplifyRect(fRect, this->dir(), this->startIndex(), flags); 259cb93a386Sopenharmony_ci wasClosed = true; 260cb93a386Sopenharmony_ci break; 261cb93a386Sopenharmony_ci case Type::kRRect: 262cb93a386Sopenharmony_ci this->simplifyRRect(fRRect, this->dir(), this->startIndex(), flags); 263cb93a386Sopenharmony_ci wasClosed = true; 264cb93a386Sopenharmony_ci break; 265cb93a386Sopenharmony_ci case Type::kPath: 266cb93a386Sopenharmony_ci wasClosed = this->simplifyPath(flags); 267cb93a386Sopenharmony_ci break; 268cb93a386Sopenharmony_ci case Type::kArc: 269cb93a386Sopenharmony_ci wasClosed = this->simplifyArc(flags); 270cb93a386Sopenharmony_ci break; 271cb93a386Sopenharmony_ci 272cb93a386Sopenharmony_ci default: 273cb93a386Sopenharmony_ci SkUNREACHABLE; 274cb93a386Sopenharmony_ci } 275cb93a386Sopenharmony_ci 276cb93a386Sopenharmony_ci if (((flags & kIgnoreWinding_Flag) || (fType != Type::kRect && fType != Type::kRRect))) { 277cb93a386Sopenharmony_ci // Reset winding parameters if we don't need them anymore 278cb93a386Sopenharmony_ci this->setPathWindingParams(kDefaultDir, kDefaultStart); 279cb93a386Sopenharmony_ci } 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci return wasClosed; 282cb93a386Sopenharmony_ci} 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_cibool GrShape::conservativeContains(const SkRect& rect) const { 285cb93a386Sopenharmony_ci switch (this->type()) { 286cb93a386Sopenharmony_ci case Type::kEmpty: 287cb93a386Sopenharmony_ci case Type::kPoint: // fall through since a point has 0 area 288cb93a386Sopenharmony_ci case Type::kLine: // fall through, "" (currently choosing not to test if 'rect' == line) 289cb93a386Sopenharmony_ci return false; 290cb93a386Sopenharmony_ci case Type::kRect: 291cb93a386Sopenharmony_ci return fRect.contains(rect); 292cb93a386Sopenharmony_ci case Type::kRRect: 293cb93a386Sopenharmony_ci return fRRect.contains(rect); 294cb93a386Sopenharmony_ci case Type::kPath: 295cb93a386Sopenharmony_ci return fPath.conservativelyContainsRect(rect); 296cb93a386Sopenharmony_ci case Type::kArc: 297cb93a386Sopenharmony_ci if (fArc.fUseCenter) { 298cb93a386Sopenharmony_ci SkPath arc; 299cb93a386Sopenharmony_ci this->asPath(&arc); 300cb93a386Sopenharmony_ci return arc.conservativelyContainsRect(rect); 301cb93a386Sopenharmony_ci } else { 302cb93a386Sopenharmony_ci return false; 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci } 305cb93a386Sopenharmony_ci SkUNREACHABLE; 306cb93a386Sopenharmony_ci} 307cb93a386Sopenharmony_ci 308cb93a386Sopenharmony_cibool GrShape::conservativeContains(const SkPoint& point) const { 309cb93a386Sopenharmony_ci switch (this->type()) { 310cb93a386Sopenharmony_ci case Type::kEmpty: 311cb93a386Sopenharmony_ci case Type::kPoint: // fall through, currently choosing not to test if shape == point 312cb93a386Sopenharmony_ci case Type::kLine: // fall through, "" 313cb93a386Sopenharmony_ci case Type::kArc: 314cb93a386Sopenharmony_ci return false; 315cb93a386Sopenharmony_ci case Type::kRect: 316cb93a386Sopenharmony_ci return fRect.contains(point.fX, point.fY); 317cb93a386Sopenharmony_ci case Type::kRRect: 318cb93a386Sopenharmony_ci return SkRRectPriv::ContainsPoint(fRRect, point); 319cb93a386Sopenharmony_ci case Type::kPath: 320cb93a386Sopenharmony_ci return fPath.contains(point.fX, point.fY); 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci SkUNREACHABLE; 323cb93a386Sopenharmony_ci} 324cb93a386Sopenharmony_ci 325cb93a386Sopenharmony_cibool GrShape::closed() const { 326cb93a386Sopenharmony_ci switch (this->type()) { 327cb93a386Sopenharmony_ci case Type::kEmpty: // fall through 328cb93a386Sopenharmony_ci case Type::kRect: // fall through 329cb93a386Sopenharmony_ci case Type::kRRect: 330cb93a386Sopenharmony_ci return true; 331cb93a386Sopenharmony_ci case Type::kPath: 332cb93a386Sopenharmony_ci // SkPath doesn't keep track of the closed status of each contour. 333cb93a386Sopenharmony_ci return SkPathPriv::IsClosedSingleContour(fPath); 334cb93a386Sopenharmony_ci case Type::kArc: 335cb93a386Sopenharmony_ci return fArc.fUseCenter; 336cb93a386Sopenharmony_ci case Type::kPoint: // fall through 337cb93a386Sopenharmony_ci case Type::kLine: 338cb93a386Sopenharmony_ci return false; 339cb93a386Sopenharmony_ci } 340cb93a386Sopenharmony_ci SkUNREACHABLE; 341cb93a386Sopenharmony_ci} 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_cibool GrShape::convex(bool simpleFill) const { 344cb93a386Sopenharmony_ci switch (this->type()) { 345cb93a386Sopenharmony_ci case Type::kEmpty: // fall through 346cb93a386Sopenharmony_ci case Type::kRect: // fall through 347cb93a386Sopenharmony_ci case Type::kRRect: 348cb93a386Sopenharmony_ci return true; 349cb93a386Sopenharmony_ci case Type::kPath: 350cb93a386Sopenharmony_ci // SkPath.isConvex() really means "is this path convex were it to be closed". 351cb93a386Sopenharmony_ci // Convex paths may only have one contour hence isLastContourClosed() is sufficient. 352cb93a386Sopenharmony_ci return (simpleFill || fPath.isLastContourClosed()) && fPath.isConvex(); 353cb93a386Sopenharmony_ci case Type::kArc: 354cb93a386Sopenharmony_ci return SkPathPriv::DrawArcIsConvex(fArc.fSweepAngle, fArc.fUseCenter, simpleFill); 355cb93a386Sopenharmony_ci case Type::kPoint: // fall through 356cb93a386Sopenharmony_ci case Type::kLine: 357cb93a386Sopenharmony_ci return false; 358cb93a386Sopenharmony_ci } 359cb93a386Sopenharmony_ci SkUNREACHABLE; 360cb93a386Sopenharmony_ci} 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ciSkRect GrShape::bounds() const { 363cb93a386Sopenharmony_ci // Bounds where left == bottom or top == right can indicate a line or point shape. We return 364cb93a386Sopenharmony_ci // inverted bounds for a truly empty shape. 365cb93a386Sopenharmony_ci static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1); 366cb93a386Sopenharmony_ci switch (this->type()) { 367cb93a386Sopenharmony_ci case Type::kEmpty: 368cb93a386Sopenharmony_ci return kInverted; 369cb93a386Sopenharmony_ci case Type::kPoint: 370cb93a386Sopenharmony_ci return {fPoint.fX, fPoint.fY, fPoint.fX, fPoint.fY}; 371cb93a386Sopenharmony_ci case Type::kRect: 372cb93a386Sopenharmony_ci return fRect.makeSorted(); 373cb93a386Sopenharmony_ci case Type::kRRect: 374cb93a386Sopenharmony_ci return fRRect.getBounds(); 375cb93a386Sopenharmony_ci case Type::kPath: 376cb93a386Sopenharmony_ci return fPath.getBounds(); 377cb93a386Sopenharmony_ci case Type::kArc: 378cb93a386Sopenharmony_ci return fArc.fOval; 379cb93a386Sopenharmony_ci case Type::kLine: { 380cb93a386Sopenharmony_ci SkRect b = SkRect::MakeLTRB(fLine.fP1.fX, fLine.fP1.fY, 381cb93a386Sopenharmony_ci fLine.fP2.fX, fLine.fP2.fY); 382cb93a386Sopenharmony_ci b.sort(); 383cb93a386Sopenharmony_ci return b; } 384cb93a386Sopenharmony_ci } 385cb93a386Sopenharmony_ci SkUNREACHABLE; 386cb93a386Sopenharmony_ci} 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_ciuint32_t GrShape::segmentMask() const { 389cb93a386Sopenharmony_ci // In order to match what a path would report, this has to inspect the shapes slightly 390cb93a386Sopenharmony_ci // to reflect what they might simplify to. 391cb93a386Sopenharmony_ci switch (this->type()) { 392cb93a386Sopenharmony_ci case Type::kEmpty: 393cb93a386Sopenharmony_ci return 0; 394cb93a386Sopenharmony_ci case Type::kRRect: 395cb93a386Sopenharmony_ci if (fRRect.isEmpty() || fRRect.isRect()) { 396cb93a386Sopenharmony_ci return SkPath::kLine_SegmentMask; 397cb93a386Sopenharmony_ci } else if (fRRect.isOval()) { 398cb93a386Sopenharmony_ci return SkPath::kConic_SegmentMask; 399cb93a386Sopenharmony_ci } else { 400cb93a386Sopenharmony_ci return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask; 401cb93a386Sopenharmony_ci } 402cb93a386Sopenharmony_ci case Type::kPath: 403cb93a386Sopenharmony_ci return fPath.getSegmentMasks(); 404cb93a386Sopenharmony_ci case Type::kArc: 405cb93a386Sopenharmony_ci if (fArc.fUseCenter) { 406cb93a386Sopenharmony_ci return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask; 407cb93a386Sopenharmony_ci } else { 408cb93a386Sopenharmony_ci return SkPath::kConic_SegmentMask; 409cb93a386Sopenharmony_ci } 410cb93a386Sopenharmony_ci case Type::kPoint: // fall through 411cb93a386Sopenharmony_ci case Type::kLine: // "" 412cb93a386Sopenharmony_ci case Type::kRect: 413cb93a386Sopenharmony_ci return SkPath::kLine_SegmentMask; 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci SkUNREACHABLE; 416cb93a386Sopenharmony_ci} 417cb93a386Sopenharmony_ci 418cb93a386Sopenharmony_civoid GrShape::asPath(SkPath* out, bool simpleFill) const { 419cb93a386Sopenharmony_ci if (!this->isPath() && !this->isArc()) { 420cb93a386Sopenharmony_ci // When not a path, we need to set fill type on the path to match invertedness. 421cb93a386Sopenharmony_ci // All the non-path geometries produce equivalent shapes with either even-odd or winding 422cb93a386Sopenharmony_ci // so we can use the default fill type. 423cb93a386Sopenharmony_ci out->reset(); 424cb93a386Sopenharmony_ci out->setFillType(kDefaultFillType); 425cb93a386Sopenharmony_ci if (fInverted) { 426cb93a386Sopenharmony_ci out->toggleInverseFillType(); 427cb93a386Sopenharmony_ci } 428cb93a386Sopenharmony_ci } // Else when we're already a path, that will assign the fill type directly to 'out'. 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci switch (this->type()) { 431cb93a386Sopenharmony_ci case Type::kEmpty: 432cb93a386Sopenharmony_ci return; 433cb93a386Sopenharmony_ci case Type::kPoint: 434cb93a386Sopenharmony_ci // A plain moveTo() or moveTo+close() does not match the expected path for a 435cb93a386Sopenharmony_ci // point that is being dashed (see SkDashPath's handling of zero-length segments). 436cb93a386Sopenharmony_ci out->moveTo(fPoint); 437cb93a386Sopenharmony_ci out->lineTo(fPoint); 438cb93a386Sopenharmony_ci return; 439cb93a386Sopenharmony_ci case Type::kRect: 440cb93a386Sopenharmony_ci out->addRect(fRect, this->dir(), this->startIndex()); 441cb93a386Sopenharmony_ci return; 442cb93a386Sopenharmony_ci case Type::kRRect: 443cb93a386Sopenharmony_ci out->addRRect(fRRect, this->dir(), this->startIndex()); 444cb93a386Sopenharmony_ci return; 445cb93a386Sopenharmony_ci case Type::kPath: 446cb93a386Sopenharmony_ci *out = fPath; 447cb93a386Sopenharmony_ci return; 448cb93a386Sopenharmony_ci case Type::kArc: 449cb93a386Sopenharmony_ci SkPathPriv::CreateDrawArcPath(out, fArc.fOval, fArc.fStartAngle, fArc.fSweepAngle, 450cb93a386Sopenharmony_ci fArc.fUseCenter, simpleFill); 451cb93a386Sopenharmony_ci // CreateDrawArcPath resets the output path and configures its fill type, so we just 452cb93a386Sopenharmony_ci // have to ensure invertedness is correct. 453cb93a386Sopenharmony_ci if (fInverted) { 454cb93a386Sopenharmony_ci out->toggleInverseFillType(); 455cb93a386Sopenharmony_ci } 456cb93a386Sopenharmony_ci return; 457cb93a386Sopenharmony_ci case Type::kLine: 458cb93a386Sopenharmony_ci out->moveTo(fLine.fP1); 459cb93a386Sopenharmony_ci out->lineTo(fLine.fP2); 460cb93a386Sopenharmony_ci return; 461cb93a386Sopenharmony_ci } 462cb93a386Sopenharmony_ci SkUNREACHABLE; 463cb93a386Sopenharmony_ci} 464