1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2016 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 "src/gpu/geometry/GrStyledShape.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/private/SkIDChangeListener.h" 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_ci#include <utility> 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ciGrStyledShape& GrStyledShape::operator=(const GrStyledShape& that) { 15cb93a386Sopenharmony_ci fShape = that.fShape; 16cb93a386Sopenharmony_ci fStyle = that.fStyle; 17cb93a386Sopenharmony_ci fGenID = that.fGenID; 18cb93a386Sopenharmony_ci fSimplified = that.fSimplified; 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_ci fInheritedKey.reset(that.fInheritedKey.count()); 21cb93a386Sopenharmony_ci sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 22cb93a386Sopenharmony_ci sizeof(uint32_t) * fInheritedKey.count()); 23cb93a386Sopenharmony_ci if (that.fInheritedPathForListeners.isValid()) { 24cb93a386Sopenharmony_ci fInheritedPathForListeners.set(*that.fInheritedPathForListeners); 25cb93a386Sopenharmony_ci } else { 26cb93a386Sopenharmony_ci fInheritedPathForListeners.reset(); 27cb93a386Sopenharmony_ci } 28cb93a386Sopenharmony_ci return *this; 29cb93a386Sopenharmony_ci} 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_cistatic bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) { 32cb93a386Sopenharmony_ci switch (inversion) { 33cb93a386Sopenharmony_ci case GrStyledShape::FillInversion::kPreserve: 34cb93a386Sopenharmony_ci return originalIsInverted; 35cb93a386Sopenharmony_ci case GrStyledShape::FillInversion::kFlip: 36cb93a386Sopenharmony_ci return !originalIsInverted; 37cb93a386Sopenharmony_ci case GrStyledShape::FillInversion::kForceInverted: 38cb93a386Sopenharmony_ci return true; 39cb93a386Sopenharmony_ci case GrStyledShape::FillInversion::kForceNoninverted: 40cb93a386Sopenharmony_ci return false; 41cb93a386Sopenharmony_ci } 42cb93a386Sopenharmony_ci return false; 43cb93a386Sopenharmony_ci} 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ciGrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) { 46cb93a386Sopenharmony_ci bool newIsInverted = is_inverted(original.fShape.inverted(), inversion); 47cb93a386Sopenharmony_ci if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) { 48cb93a386Sopenharmony_ci // By returning the original rather than falling through we can preserve any inherited style 49cb93a386Sopenharmony_ci // key. Otherwise, we wipe it out below since the style change invalidates it. 50cb93a386Sopenharmony_ci return original; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci GrStyledShape result; 53cb93a386Sopenharmony_ci SkASSERT(result.fStyle.isSimpleFill()); 54cb93a386Sopenharmony_ci if (original.fInheritedPathForListeners.isValid()) { 55cb93a386Sopenharmony_ci result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners); 56cb93a386Sopenharmony_ci } 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci result.fShape = original.fShape; 59cb93a386Sopenharmony_ci result.fGenID = original.fGenID; 60cb93a386Sopenharmony_ci result.fShape.setInverted(newIsInverted); 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci if (!original.style().isSimpleFill()) { 63cb93a386Sopenharmony_ci // Going from a non-filled style to fill may allow additional simplifications (e.g. 64cb93a386Sopenharmony_ci // closing an open rect that wasn't closed in the original shape because it had 65cb93a386Sopenharmony_ci // stroke style). 66cb93a386Sopenharmony_ci result.simplify(); 67cb93a386Sopenharmony_ci // The above simplify() call only sets simplified to true if its geometry was changed, 68cb93a386Sopenharmony_ci // since it already sees its style as a simple fill. Since the original style was not a 69cb93a386Sopenharmony_ci // simple fill, MakeFilled always simplifies. 70cb93a386Sopenharmony_ci result.fSimplified = true; 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci // Verify that lines/points were converted to empty by the style change 74cb93a386Sopenharmony_ci SkASSERT((!original.fShape.isLine() && !original.fShape.isPoint()) || result.fShape.isEmpty()); 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci // We don't copy the inherited key since it can contain path effect information that we just 77cb93a386Sopenharmony_ci // stripped. 78cb93a386Sopenharmony_ci return result; 79cb93a386Sopenharmony_ci} 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ciSkRect GrStyledShape::styledBounds() const { 82cb93a386Sopenharmony_ci if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) { 83cb93a386Sopenharmony_ci return SkRect::MakeEmpty(); 84cb93a386Sopenharmony_ci } 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci SkRect bounds; 87cb93a386Sopenharmony_ci fStyle.adjustBounds(&bounds, this->bounds()); 88cb93a386Sopenharmony_ci return bounds; 89cb93a386Sopenharmony_ci} 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci// If the path is small enough to be keyed from its data this returns key length, otherwise -1. 92cb93a386Sopenharmony_cistatic int path_key_from_data_size(const SkPath& path) { 93cb93a386Sopenharmony_ci const int verbCnt = path.countVerbs(); 94cb93a386Sopenharmony_ci if (verbCnt > GrStyledShape::kMaxKeyFromDataVerbCnt) { 95cb93a386Sopenharmony_ci return -1; 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci const int pointCnt = path.countPoints(); 98cb93a386Sopenharmony_ci const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 101cb93a386Sopenharmony_ci static_assert(sizeof(SkScalar) == sizeof(uint32_t)); 102cb93a386Sopenharmony_ci // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to 103cb93a386Sopenharmony_ci // a uint32_t length. 104cb93a386Sopenharmony_ci return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt; 105cb93a386Sopenharmony_ci} 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci// Writes the path data key into the passed pointer. 108cb93a386Sopenharmony_cistatic void write_path_key_from_data(const SkPath& path, uint32_t* origKey) { 109cb93a386Sopenharmony_ci uint32_t* key = origKey; 110cb93a386Sopenharmony_ci // The check below should take care of negative values casted positive. 111cb93a386Sopenharmony_ci const int verbCnt = path.countVerbs(); 112cb93a386Sopenharmony_ci const int pointCnt = path.countPoints(); 113cb93a386Sopenharmony_ci const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 114cb93a386Sopenharmony_ci SkASSERT(verbCnt <= GrStyledShape::kMaxKeyFromDataVerbCnt); 115cb93a386Sopenharmony_ci SkASSERT(pointCnt && verbCnt); 116cb93a386Sopenharmony_ci *key++ = verbCnt; 117cb93a386Sopenharmony_ci memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t)); 118cb93a386Sopenharmony_ci int verbKeySize = SkAlign4(verbCnt); 119cb93a386Sopenharmony_ci // pad out to uint32_t alignment using value that will stand out when debugging. 120cb93a386Sopenharmony_ci uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt; 121cb93a386Sopenharmony_ci memset(pad, 0xDE, verbKeySize - verbCnt); 122cb93a386Sopenharmony_ci key += verbKeySize >> 2; 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt); 125cb93a386Sopenharmony_ci static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 126cb93a386Sopenharmony_ci key += 2 * pointCnt; 127cb93a386Sopenharmony_ci sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt); 128cb93a386Sopenharmony_ci static_assert(sizeof(SkScalar) == sizeof(uint32_t)); 129cb93a386Sopenharmony_ci SkDEBUGCODE(key += conicWeightCnt); 130cb93a386Sopenharmony_ci SkASSERT(key - origKey == path_key_from_data_size(path)); 131cb93a386Sopenharmony_ci} 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ciint GrStyledShape::unstyledKeySize() const { 134cb93a386Sopenharmony_ci if (fInheritedKey.count()) { 135cb93a386Sopenharmony_ci return fInheritedKey.count(); 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci int count = 1; // Every key has the state flags from the GrShape 139cb93a386Sopenharmony_ci switch(fShape.type()) { 140cb93a386Sopenharmony_ci case GrShape::Type::kPoint: 141cb93a386Sopenharmony_ci static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t)); 142cb93a386Sopenharmony_ci count += sizeof(SkPoint) / sizeof(uint32_t); 143cb93a386Sopenharmony_ci break; 144cb93a386Sopenharmony_ci case GrShape::Type::kRect: 145cb93a386Sopenharmony_ci static_assert(0 == sizeof(SkRect) % sizeof(uint32_t)); 146cb93a386Sopenharmony_ci count += sizeof(SkRect) / sizeof(uint32_t); 147cb93a386Sopenharmony_ci break; 148cb93a386Sopenharmony_ci case GrShape::Type::kRRect: 149cb93a386Sopenharmony_ci static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); 150cb93a386Sopenharmony_ci count += SkRRect::kSizeInMemory / sizeof(uint32_t); 151cb93a386Sopenharmony_ci break; 152cb93a386Sopenharmony_ci case GrShape::Type::kArc: 153cb93a386Sopenharmony_ci static_assert(0 == sizeof(GrArc) % sizeof(uint32_t)); 154cb93a386Sopenharmony_ci count += sizeof(GrArc) / sizeof(uint32_t); 155cb93a386Sopenharmony_ci break; 156cb93a386Sopenharmony_ci case GrShape::Type::kLine: 157cb93a386Sopenharmony_ci static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t)); 158cb93a386Sopenharmony_ci count += sizeof(GrLineSegment) / sizeof(uint32_t); 159cb93a386Sopenharmony_ci break; 160cb93a386Sopenharmony_ci case GrShape::Type::kPath: { 161cb93a386Sopenharmony_ci if (0 == fGenID) { 162cb93a386Sopenharmony_ci return -1; // volatile, so won't be keyed 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci int dataKeySize = path_key_from_data_size(fShape.path()); 165cb93a386Sopenharmony_ci if (dataKeySize >= 0) { 166cb93a386Sopenharmony_ci count += dataKeySize; 167cb93a386Sopenharmony_ci } else { 168cb93a386Sopenharmony_ci count++; // Just adds the gen ID. 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci break; } 171cb93a386Sopenharmony_ci default: 172cb93a386Sopenharmony_ci // else it's empty, which just needs the state flags for its key 173cb93a386Sopenharmony_ci SkASSERT(fShape.isEmpty()); 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci return count; 176cb93a386Sopenharmony_ci} 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_civoid GrStyledShape::writeUnstyledKey(uint32_t* key) const { 179cb93a386Sopenharmony_ci SkASSERT(this->unstyledKeySize()); 180cb93a386Sopenharmony_ci SkDEBUGCODE(uint32_t* origKey = key;) 181cb93a386Sopenharmony_ci if (fInheritedKey.count()) { 182cb93a386Sopenharmony_ci memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count()); 183cb93a386Sopenharmony_ci SkDEBUGCODE(key += fInheritedKey.count();) 184cb93a386Sopenharmony_ci } else { 185cb93a386Sopenharmony_ci // Dir and start are only used for rect and rrect shapes, so are not included in other 186cb93a386Sopenharmony_ci // shape type keys. Make sure that they are the defaults for other shapes so it doesn't 187cb93a386Sopenharmony_ci // matter that we universally include them in the flag key value. 188cb93a386Sopenharmony_ci SkASSERT((fShape.isRect() || fShape.isRRect()) || 189cb93a386Sopenharmony_ci (fShape.dir() == GrShape::kDefaultDir && 190cb93a386Sopenharmony_ci fShape.startIndex() == GrShape::kDefaultStart)); 191cb93a386Sopenharmony_ci 192cb93a386Sopenharmony_ci // Every key starts with the state from the GrShape (this includes path fill type, 193cb93a386Sopenharmony_ci // and any tracked winding, start, inversion, as well as the class of geometry). 194cb93a386Sopenharmony_ci *key++ = fShape.stateKey(); 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci switch(fShape.type()) { 197cb93a386Sopenharmony_ci case GrShape::Type::kPath: { 198cb93a386Sopenharmony_ci SkASSERT(fGenID != 0); 199cb93a386Sopenharmony_ci // Ensure that the path's inversion matches our state so that the path's key suffices. 200cb93a386Sopenharmony_ci SkASSERT(fShape.inverted() == fShape.path().isInverseFillType()); 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_ci int dataKeySize = path_key_from_data_size(fShape.path()); 203cb93a386Sopenharmony_ci if (dataKeySize >= 0) { 204cb93a386Sopenharmony_ci write_path_key_from_data(fShape.path(), key); 205cb93a386Sopenharmony_ci return; 206cb93a386Sopenharmony_ci } else { 207cb93a386Sopenharmony_ci *key++ = fGenID; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci break; } 210cb93a386Sopenharmony_ci case GrShape::Type::kPoint: 211cb93a386Sopenharmony_ci memcpy(key, &fShape.point(), sizeof(SkPoint)); 212cb93a386Sopenharmony_ci key += sizeof(SkPoint) / sizeof(uint32_t); 213cb93a386Sopenharmony_ci break; 214cb93a386Sopenharmony_ci case GrShape::Type::kRect: 215cb93a386Sopenharmony_ci memcpy(key, &fShape.rect(), sizeof(SkRect)); 216cb93a386Sopenharmony_ci key += sizeof(SkRect) / sizeof(uint32_t); 217cb93a386Sopenharmony_ci break; 218cb93a386Sopenharmony_ci case GrShape::Type::kRRect: 219cb93a386Sopenharmony_ci fShape.rrect().writeToMemory(key); 220cb93a386Sopenharmony_ci key += SkRRect::kSizeInMemory / sizeof(uint32_t); 221cb93a386Sopenharmony_ci break; 222cb93a386Sopenharmony_ci case GrShape::Type::kArc: 223cb93a386Sopenharmony_ci // Write dense floats first 224cb93a386Sopenharmony_ci memcpy(key, &fShape.arc(), sizeof(SkRect) + 2 * sizeof(float)); 225cb93a386Sopenharmony_ci key += (sizeof(GrArc) / sizeof(uint32_t) - 1); 226cb93a386Sopenharmony_ci // Then write the final bool as an int, to make sure upper bits are set 227cb93a386Sopenharmony_ci *key++ = fShape.arc().fUseCenter ? 1 : 0; 228cb93a386Sopenharmony_ci break; 229cb93a386Sopenharmony_ci case GrShape::Type::kLine: 230cb93a386Sopenharmony_ci memcpy(key, &fShape.line(), sizeof(GrLineSegment)); 231cb93a386Sopenharmony_ci key += sizeof(GrLineSegment) / sizeof(uint32_t); 232cb93a386Sopenharmony_ci break; 233cb93a386Sopenharmony_ci default: 234cb93a386Sopenharmony_ci // Nothing other than the flag state is needed in the key for an empty shape 235cb93a386Sopenharmony_ci SkASSERT(fShape.isEmpty()); 236cb93a386Sopenharmony_ci } 237cb93a386Sopenharmony_ci } 238cb93a386Sopenharmony_ci SkASSERT(key - origKey == this->unstyledKeySize()); 239cb93a386Sopenharmony_ci} 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_civoid GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply apply, 242cb93a386Sopenharmony_ci SkScalar scale) { 243cb93a386Sopenharmony_ci SkASSERT(!fInheritedKey.count()); 244cb93a386Sopenharmony_ci // If the output shape turns out to be simple, then we will just use its geometric key 245cb93a386Sopenharmony_ci if (fShape.isPath()) { 246cb93a386Sopenharmony_ci // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as 247cb93a386Sopenharmony_ci // ApplyFullStyle(shape). 248cb93a386Sopenharmony_ci // The full key is structured as (geo,path_effect,stroke). 249cb93a386Sopenharmony_ci // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then 250cb93a386Sopenharmony_ci // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key 251cb93a386Sopenharmony_ci // and then append the style key (which should now be stroke only) at the end. 252cb93a386Sopenharmony_ci int parentCnt = parent.fInheritedKey.count(); 253cb93a386Sopenharmony_ci bool useParentGeoKey = !parentCnt; 254cb93a386Sopenharmony_ci if (useParentGeoKey) { 255cb93a386Sopenharmony_ci parentCnt = parent.unstyledKeySize(); 256cb93a386Sopenharmony_ci if (parentCnt < 0) { 257cb93a386Sopenharmony_ci // The parent's geometry has no key so we will have no key. 258cb93a386Sopenharmony_ci fGenID = 0; 259cb93a386Sopenharmony_ci return; 260cb93a386Sopenharmony_ci } 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci uint32_t styleKeyFlags = 0; 263cb93a386Sopenharmony_ci if (parent.knownToBeClosed()) { 264cb93a386Sopenharmony_ci styleKeyFlags |= GrStyle::kClosed_KeyFlag; 265cb93a386Sopenharmony_ci } 266cb93a386Sopenharmony_ci if (parent.asLine(nullptr, nullptr)) { 267cb93a386Sopenharmony_ci styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; 268cb93a386Sopenharmony_ci } 269cb93a386Sopenharmony_ci int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); 270cb93a386Sopenharmony_ci if (styleCnt < 0) { 271cb93a386Sopenharmony_ci // The style doesn't allow a key, set the path gen ID to 0 so that we fail when 272cb93a386Sopenharmony_ci // we try to get a key for the shape. 273cb93a386Sopenharmony_ci fGenID = 0; 274cb93a386Sopenharmony_ci return; 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci fInheritedKey.reset(parentCnt + styleCnt); 277cb93a386Sopenharmony_ci if (useParentGeoKey) { 278cb93a386Sopenharmony_ci // This will be the geo key. 279cb93a386Sopenharmony_ci parent.writeUnstyledKey(fInheritedKey.get()); 280cb93a386Sopenharmony_ci } else { 281cb93a386Sopenharmony_ci // This should be (geo,path_effect). 282cb93a386Sopenharmony_ci memcpy(fInheritedKey.get(), parent.fInheritedKey.get(), 283cb93a386Sopenharmony_ci parentCnt * sizeof(uint32_t)); 284cb93a386Sopenharmony_ci } 285cb93a386Sopenharmony_ci // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke) 286cb93a386Sopenharmony_ci GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale, 287cb93a386Sopenharmony_ci styleKeyFlags); 288cb93a386Sopenharmony_ci } 289cb93a386Sopenharmony_ci} 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ciconst SkPath* GrStyledShape::originalPathForListeners() const { 292cb93a386Sopenharmony_ci if (fInheritedPathForListeners.isValid()) { 293cb93a386Sopenharmony_ci return fInheritedPathForListeners.get(); 294cb93a386Sopenharmony_ci } else if (fShape.isPath() && !fShape.path().isVolatile()) { 295cb93a386Sopenharmony_ci return &fShape.path(); 296cb93a386Sopenharmony_ci } 297cb93a386Sopenharmony_ci return nullptr; 298cb93a386Sopenharmony_ci} 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_civoid GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const { 301cb93a386Sopenharmony_ci if (const auto* lp = this->originalPathForListeners()) { 302cb93a386Sopenharmony_ci SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener)); 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci} 305cb93a386Sopenharmony_ci 306cb93a386Sopenharmony_ciGrStyledShape GrStyledShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, 307cb93a386Sopenharmony_ci SkScalar sweepAngleDegrees, bool useCenter, 308cb93a386Sopenharmony_ci const GrStyle& style, DoSimplify doSimplify) { 309cb93a386Sopenharmony_ci GrStyledShape result; 310cb93a386Sopenharmony_ci result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter}); 311cb93a386Sopenharmony_ci result.fStyle = style; 312cb93a386Sopenharmony_ci if (doSimplify == DoSimplify::kYes) { 313cb93a386Sopenharmony_ci result.simplify(); 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci return result; 316cb93a386Sopenharmony_ci} 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ciGrStyledShape::GrStyledShape(const GrStyledShape& that) 319cb93a386Sopenharmony_ci : fShape(that.fShape) 320cb93a386Sopenharmony_ci , fStyle(that.fStyle) 321cb93a386Sopenharmony_ci , fGenID(that.fGenID) 322cb93a386Sopenharmony_ci , fSimplified(that.fSimplified) { 323cb93a386Sopenharmony_ci fInheritedKey.reset(that.fInheritedKey.count()); 324cb93a386Sopenharmony_ci sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 325cb93a386Sopenharmony_ci sizeof(uint32_t) * fInheritedKey.count()); 326cb93a386Sopenharmony_ci if (that.fInheritedPathForListeners.isValid()) { 327cb93a386Sopenharmony_ci fInheritedPathForListeners.set(*that.fInheritedPathForListeners); 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci} 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ciGrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply, SkScalar scale) { 332cb93a386Sopenharmony_ci // TODO: Add some quantization of scale for better cache performance here or leave that up 333cb93a386Sopenharmony_ci // to caller? 334cb93a386Sopenharmony_ci // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel 335cb93a386Sopenharmony_ci // stroke of a rect). 336cb93a386Sopenharmony_ci if (!parent.style().applies() || 337cb93a386Sopenharmony_ci (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) { 338cb93a386Sopenharmony_ci *this = parent; 339cb93a386Sopenharmony_ci return; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci 342cb93a386Sopenharmony_ci SkPathEffect* pe = parent.fStyle.pathEffect(); 343cb93a386Sopenharmony_ci SkTLazy<SkPath> tmpPath; 344cb93a386Sopenharmony_ci const GrStyledShape* parentForKey = &parent; 345cb93a386Sopenharmony_ci SkTLazy<GrStyledShape> tmpParent; 346cb93a386Sopenharmony_ci 347cb93a386Sopenharmony_ci // Start out as an empty path that is filled in by the applied style 348cb93a386Sopenharmony_ci fShape.setPath(SkPath()); 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_ci if (pe) { 351cb93a386Sopenharmony_ci const SkPath* srcForPathEffect; 352cb93a386Sopenharmony_ci if (parent.fShape.isPath()) { 353cb93a386Sopenharmony_ci srcForPathEffect = &parent.fShape.path(); 354cb93a386Sopenharmony_ci } else { 355cb93a386Sopenharmony_ci srcForPathEffect = tmpPath.init(); 356cb93a386Sopenharmony_ci parent.asPath(tmpPath.get()); 357cb93a386Sopenharmony_ci } 358cb93a386Sopenharmony_ci // Should we consider bounds? Would have to include in key, but it'd be nice to know 359cb93a386Sopenharmony_ci // if the bounds actually modified anything before including in key. 360cb93a386Sopenharmony_ci SkStrokeRec strokeRec = parent.fStyle.strokeRec(); 361cb93a386Sopenharmony_ci if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect, 362cb93a386Sopenharmony_ci scale)) { 363cb93a386Sopenharmony_ci tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); 364cb93a386Sopenharmony_ci *this = tmpParent->applyStyle(apply, scale); 365cb93a386Sopenharmony_ci return; 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci // A path effect has access to change the res scale but we aren't expecting it to and it 368cb93a386Sopenharmony_ci // would mess up our key computation. 369cb93a386Sopenharmony_ci SkASSERT(scale == strokeRec.getResScale()); 370cb93a386Sopenharmony_ci if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) { 371cb93a386Sopenharmony_ci // The intermediate shape may not be a general path. If we we're just applying 372cb93a386Sopenharmony_ci // the path effect then attemptToReduceFromPath would catch it. This means that 373cb93a386Sopenharmony_ci // when we subsequently applied the remaining strokeRec we would have a non-path 374cb93a386Sopenharmony_ci // parent shape that would be used to determine the the stroked path's key. 375cb93a386Sopenharmony_ci // We detect that case here and change parentForKey to a temporary that represents 376cb93a386Sopenharmony_ci // the simpler shape so that applying both path effect and the strokerec all at 377cb93a386Sopenharmony_ci // once produces the same key. 378cb93a386Sopenharmony_ci tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr)); 379cb93a386Sopenharmony_ci tmpParent->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale); 380cb93a386Sopenharmony_ci if (!tmpPath.isValid()) { 381cb93a386Sopenharmony_ci tmpPath.init(); 382cb93a386Sopenharmony_ci } 383cb93a386Sopenharmony_ci tmpParent->asPath(tmpPath.get()); 384cb93a386Sopenharmony_ci SkStrokeRec::InitStyle fillOrHairline; 385cb93a386Sopenharmony_ci // The parent shape may have simplified away the strokeRec, check for that here. 386cb93a386Sopenharmony_ci if (tmpParent->style().applies()) { 387cb93a386Sopenharmony_ci SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline, 388cb93a386Sopenharmony_ci *tmpPath.get(), scale)); 389cb93a386Sopenharmony_ci } else if (tmpParent->style().isSimpleFill()) { 390cb93a386Sopenharmony_ci fillOrHairline = SkStrokeRec::kFill_InitStyle; 391cb93a386Sopenharmony_ci } else { 392cb93a386Sopenharmony_ci SkASSERT(tmpParent.get()->style().isSimpleHairline()); 393cb93a386Sopenharmony_ci fillOrHairline = SkStrokeRec::kHairline_InitStyle; 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci fStyle.resetToInitStyle(fillOrHairline); 396cb93a386Sopenharmony_ci parentForKey = tmpParent.get(); 397cb93a386Sopenharmony_ci } else { 398cb93a386Sopenharmony_ci fStyle = GrStyle(strokeRec, nullptr); 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci } else { 401cb93a386Sopenharmony_ci const SkPath* srcForParentStyle; 402cb93a386Sopenharmony_ci if (parent.fShape.isPath()) { 403cb93a386Sopenharmony_ci srcForParentStyle = &parent.fShape.path(); 404cb93a386Sopenharmony_ci } else { 405cb93a386Sopenharmony_ci srcForParentStyle = tmpPath.init(); 406cb93a386Sopenharmony_ci parent.asPath(tmpPath.get()); 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci SkStrokeRec::InitStyle fillOrHairline; 409cb93a386Sopenharmony_ci SkASSERT(parent.fStyle.applies()); 410cb93a386Sopenharmony_ci SkASSERT(!parent.fStyle.pathEffect()); 411cb93a386Sopenharmony_ci SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline, 412cb93a386Sopenharmony_ci *srcForParentStyle, scale)); 413cb93a386Sopenharmony_ci fStyle.resetToInitStyle(fillOrHairline); 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci if (parent.fInheritedPathForListeners.isValid()) { 417cb93a386Sopenharmony_ci fInheritedPathForListeners.set(*parent.fInheritedPathForListeners); 418cb93a386Sopenharmony_ci } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) { 419cb93a386Sopenharmony_ci fInheritedPathForListeners.set(parent.fShape.path()); 420cb93a386Sopenharmony_ci } 421cb93a386Sopenharmony_ci this->simplify(); 422cb93a386Sopenharmony_ci this->setInheritedKey(*parentForKey, apply, scale); 423cb93a386Sopenharmony_ci} 424cb93a386Sopenharmony_ci 425cb93a386Sopenharmony_cibool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, 426cb93a386Sopenharmony_ci bool* inverted) const { 427cb93a386Sopenharmony_ci if (!fShape.isRRect() && !fShape.isRect()) { 428cb93a386Sopenharmony_ci return false; 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci 431cb93a386Sopenharmony_ci // Validity check here, if we don't have a path effect on the style, we should have passed 432cb93a386Sopenharmony_ci // appropriate flags to GrShape::simplify() to have reset these parameters. 433cb93a386Sopenharmony_ci SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir && 434cb93a386Sopenharmony_ci fShape.startIndex() == GrShape::kDefaultStart)); 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci // If the shape is a regular rect, map to round rect winding parameters, including accounting 437cb93a386Sopenharmony_ci // for the automatic sorting of edges that SkRRect::MakeRect() performs. 438cb93a386Sopenharmony_ci if (fShape.isRect()) { 439cb93a386Sopenharmony_ci if (rrect) { 440cb93a386Sopenharmony_ci *rrect = SkRRect::MakeRect(fShape.rect()); 441cb93a386Sopenharmony_ci } 442cb93a386Sopenharmony_ci // Don't bother mapping these if we don't have a path effect, however. 443cb93a386Sopenharmony_ci if (!fStyle.hasPathEffect()) { 444cb93a386Sopenharmony_ci if (dir) { 445cb93a386Sopenharmony_ci *dir = GrShape::kDefaultDir; 446cb93a386Sopenharmony_ci } 447cb93a386Sopenharmony_ci if (start) { 448cb93a386Sopenharmony_ci *start = GrShape::kDefaultStart; 449cb93a386Sopenharmony_ci } 450cb93a386Sopenharmony_ci } else { 451cb93a386Sopenharmony_ci // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 452cb93a386Sopenharmony_ci // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 453cb93a386Sopenharmony_ci // rect edges. Thus, we may need to modify the rrect's start index and direction. 454cb93a386Sopenharmony_ci SkPathDirection rectDir = fShape.dir(); 455cb93a386Sopenharmony_ci unsigned rectStart = fShape.startIndex(); 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci if (fShape.rect().fLeft > fShape.rect().fRight) { 458cb93a386Sopenharmony_ci // Toggle direction, and modify index by mapping through the array 459cb93a386Sopenharmony_ci static const unsigned kMapping[] = {1, 0, 3, 2}; 460cb93a386Sopenharmony_ci rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW 461cb93a386Sopenharmony_ci : SkPathDirection::kCCW; 462cb93a386Sopenharmony_ci rectStart = kMapping[rectStart]; 463cb93a386Sopenharmony_ci } 464cb93a386Sopenharmony_ci if (fShape.rect().fTop > fShape.rect().fBottom) { 465cb93a386Sopenharmony_ci // Toggle direction and map index by 3 - start 466cb93a386Sopenharmony_ci // NOTE: if we earlier flipped for X as well, this results in no net direction 467cb93a386Sopenharmony_ci // change and effectively flipping the start index to the diagonal corners of the 468cb93a386Sopenharmony_ci // rect (matching what we'd expect for a rect with both X and Y flipped). 469cb93a386Sopenharmony_ci rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW 470cb93a386Sopenharmony_ci : SkPathDirection::kCCW; 471cb93a386Sopenharmony_ci rectStart = 3 - rectStart; 472cb93a386Sopenharmony_ci } 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci if (dir) { 475cb93a386Sopenharmony_ci *dir = rectDir; 476cb93a386Sopenharmony_ci } 477cb93a386Sopenharmony_ci if (start) { 478cb93a386Sopenharmony_ci // Convert to round rect indexing 479cb93a386Sopenharmony_ci *start = 2 * rectStart; 480cb93a386Sopenharmony_ci } 481cb93a386Sopenharmony_ci } 482cb93a386Sopenharmony_ci } else { 483cb93a386Sopenharmony_ci // Straight forward export 484cb93a386Sopenharmony_ci if (rrect) { 485cb93a386Sopenharmony_ci *rrect = fShape.rrect(); 486cb93a386Sopenharmony_ci } 487cb93a386Sopenharmony_ci if (dir) { 488cb93a386Sopenharmony_ci *dir = fShape.dir(); 489cb93a386Sopenharmony_ci } 490cb93a386Sopenharmony_ci if (start) { 491cb93a386Sopenharmony_ci *start = fShape.startIndex(); 492cb93a386Sopenharmony_ci // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special 493cb93a386Sopenharmony_ci // but we do for dashing placement 494cb93a386Sopenharmony_ci if (fShape.rrect().isOval()) { 495cb93a386Sopenharmony_ci *start &= 0b110; 496cb93a386Sopenharmony_ci } 497cb93a386Sopenharmony_ci } 498cb93a386Sopenharmony_ci } 499cb93a386Sopenharmony_ci 500cb93a386Sopenharmony_ci if (inverted) { 501cb93a386Sopenharmony_ci *inverted = fShape.inverted(); 502cb93a386Sopenharmony_ci } 503cb93a386Sopenharmony_ci 504cb93a386Sopenharmony_ci return true; 505cb93a386Sopenharmony_ci} 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_cibool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const { 508cb93a386Sopenharmony_ci if (!fShape.isLine()) { 509cb93a386Sopenharmony_ci return false; 510cb93a386Sopenharmony_ci } 511cb93a386Sopenharmony_ci 512cb93a386Sopenharmony_ci if (pts) { 513cb93a386Sopenharmony_ci pts[0] = fShape.line().fP1; 514cb93a386Sopenharmony_ci pts[1] = fShape.line().fP2; 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci if (inverted) { 517cb93a386Sopenharmony_ci *inverted = fShape.inverted(); 518cb93a386Sopenharmony_ci } 519cb93a386Sopenharmony_ci return true; 520cb93a386Sopenharmony_ci} 521cb93a386Sopenharmony_ci 522cb93a386Sopenharmony_cibool GrStyledShape::asNestedRects(SkRect rects[2]) const { 523cb93a386Sopenharmony_ci if (!fShape.isPath()) { 524cb93a386Sopenharmony_ci return false; 525cb93a386Sopenharmony_ci } 526cb93a386Sopenharmony_ci 527cb93a386Sopenharmony_ci // TODO: it would be better two store DRRects natively in the shape rather than converting 528cb93a386Sopenharmony_ci // them to a path and then reextracting the nested rects 529cb93a386Sopenharmony_ci if (fShape.path().isInverseFillType()) { 530cb93a386Sopenharmony_ci return false; 531cb93a386Sopenharmony_ci } 532cb93a386Sopenharmony_ci 533cb93a386Sopenharmony_ci SkPathDirection dirs[2]; 534cb93a386Sopenharmony_ci if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) { 535cb93a386Sopenharmony_ci return false; 536cb93a386Sopenharmony_ci } 537cb93a386Sopenharmony_ci 538cb93a386Sopenharmony_ci if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) { 539cb93a386Sopenharmony_ci // The two rects need to be wound opposite to each other 540cb93a386Sopenharmony_ci return false; 541cb93a386Sopenharmony_ci } 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci // Right now, nested rects where the margin is not the same width 544cb93a386Sopenharmony_ci // all around do not render correctly 545cb93a386Sopenharmony_ci const SkScalar* outer = rects[0].asScalars(); 546cb93a386Sopenharmony_ci const SkScalar* inner = rects[1].asScalars(); 547cb93a386Sopenharmony_ci 548cb93a386Sopenharmony_ci bool allEq = true; 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_ci SkScalar margin = SkScalarAbs(outer[0] - inner[0]); 551cb93a386Sopenharmony_ci bool allGoE1 = margin >= SK_Scalar1; 552cb93a386Sopenharmony_ci 553cb93a386Sopenharmony_ci for (int i = 1; i < 4; ++i) { 554cb93a386Sopenharmony_ci SkScalar temp = SkScalarAbs(outer[i] - inner[i]); 555cb93a386Sopenharmony_ci if (temp < SK_Scalar1) { 556cb93a386Sopenharmony_ci allGoE1 = false; 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(margin, temp)) { 559cb93a386Sopenharmony_ci allEq = false; 560cb93a386Sopenharmony_ci } 561cb93a386Sopenharmony_ci } 562cb93a386Sopenharmony_ci 563cb93a386Sopenharmony_ci return allEq || allGoE1; 564cb93a386Sopenharmony_ci} 565cb93a386Sopenharmony_ci 566cb93a386Sopenharmony_ciclass AutoRestoreInverseness { 567cb93a386Sopenharmony_cipublic: 568cb93a386Sopenharmony_ci AutoRestoreInverseness(GrShape* shape, const GrStyle& style) 569cb93a386Sopenharmony_ci // Dashing ignores inverseness skbug.com/5421. 570cb93a386Sopenharmony_ci : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {} 571cb93a386Sopenharmony_ci 572cb93a386Sopenharmony_ci ~AutoRestoreInverseness() { 573cb93a386Sopenharmony_ci // Restore invertedness after any modifications were made to the shape type 574cb93a386Sopenharmony_ci fShape->setInverted(fInverted); 575cb93a386Sopenharmony_ci SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType()); 576cb93a386Sopenharmony_ci } 577cb93a386Sopenharmony_ci 578cb93a386Sopenharmony_ciprivate: 579cb93a386Sopenharmony_ci GrShape* fShape; 580cb93a386Sopenharmony_ci bool fInverted; 581cb93a386Sopenharmony_ci}; 582cb93a386Sopenharmony_ci 583cb93a386Sopenharmony_civoid GrStyledShape::simplify() { 584cb93a386Sopenharmony_ci AutoRestoreInverseness ari(&fShape, fStyle); 585cb93a386Sopenharmony_ci 586cb93a386Sopenharmony_ci unsigned simplifyFlags = 0; 587cb93a386Sopenharmony_ci if (fStyle.isSimpleFill()) { 588cb93a386Sopenharmony_ci simplifyFlags = GrShape::kAll_Flags; 589cb93a386Sopenharmony_ci } else if (!fStyle.hasPathEffect()) { 590cb93a386Sopenharmony_ci // Everything but arcs with caps that might extend beyond the oval edge can ignore winding 591cb93a386Sopenharmony_ci if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) { 592cb93a386Sopenharmony_ci simplifyFlags |= GrShape::kIgnoreWinding_Flag; 593cb93a386Sopenharmony_ci } 594cb93a386Sopenharmony_ci simplifyFlags |= GrShape::kMakeCanonical_Flag; 595cb93a386Sopenharmony_ci } // else if there's a path effect, every destructive simplification is disabledd 596cb93a386Sopenharmony_ci 597cb93a386Sopenharmony_ci // Remember if the original shape was closed; in the event we simplify to a point or line 598cb93a386Sopenharmony_ci // because of degenerate geometry, we need to update joins and caps. 599cb93a386Sopenharmony_ci GrShape::Type oldType = fShape.type(); 600cb93a386Sopenharmony_ci fClosed = fShape.simplify(simplifyFlags); 601cb93a386Sopenharmony_ci fSimplified = oldType != fShape.type(); 602cb93a386Sopenharmony_ci 603cb93a386Sopenharmony_ci if (fShape.isPath()) { 604cb93a386Sopenharmony_ci // The shape remains a path, so configure the gen ID and canonicalize fill type if possible 605cb93a386Sopenharmony_ci if (fInheritedKey.count() || fShape.path().isVolatile()) { 606cb93a386Sopenharmony_ci fGenID = 0; 607cb93a386Sopenharmony_ci } else { 608cb93a386Sopenharmony_ci fGenID = fShape.path().getGenerationID(); 609cb93a386Sopenharmony_ci } 610cb93a386Sopenharmony_ci if (!fStyle.hasNonDashPathEffect() && 611cb93a386Sopenharmony_ci (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style || 612cb93a386Sopenharmony_ci fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style || 613cb93a386Sopenharmony_ci fShape.path().isConvex())) { 614cb93a386Sopenharmony_ci // Stroke styles don't differentiate between winding and even/odd. There is no 615cb93a386Sopenharmony_ci // distinction between even/odd and non-zero winding count for convex paths. 616cb93a386Sopenharmony_ci // Moreover, dashing ignores inverseness (skbug.com/5421) 617cb93a386Sopenharmony_ci fShape.path().setFillType(GrShape::kDefaultFillType); 618cb93a386Sopenharmony_ci } 619cb93a386Sopenharmony_ci } else { 620cb93a386Sopenharmony_ci fInheritedKey.reset(0); 621cb93a386Sopenharmony_ci // Whenever we simplify to a non-path, break the chain so we no longer refer to the 622cb93a386Sopenharmony_ci // original path. This prevents attaching genID listeners to temporary paths created when 623cb93a386Sopenharmony_ci // drawing simple shapes. 624cb93a386Sopenharmony_ci fInheritedPathForListeners.reset(); 625cb93a386Sopenharmony_ci // Further simplifications to the shape based on the style 626cb93a386Sopenharmony_ci this->simplifyStroke(); 627cb93a386Sopenharmony_ci } 628cb93a386Sopenharmony_ci} 629cb93a386Sopenharmony_ci 630cb93a386Sopenharmony_civoid GrStyledShape::simplifyStroke() { 631cb93a386Sopenharmony_ci AutoRestoreInverseness ari(&fShape, fStyle); 632cb93a386Sopenharmony_ci 633cb93a386Sopenharmony_ci // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape 634cb93a386Sopenharmony_ci // becomes a round rect. 635cb93a386Sopenharmony_ci if (!fStyle.hasPathEffect() && fShape.isRect() && 636cb93a386Sopenharmony_ci fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 637cb93a386Sopenharmony_ci if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join || 638cb93a386Sopenharmony_ci (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && 639cb93a386Sopenharmony_ci fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) { 640cb93a386Sopenharmony_ci // Bevel-stroked rect needs path rendering 641cb93a386Sopenharmony_ci return; 642cb93a386Sopenharmony_ci } 643cb93a386Sopenharmony_ci 644cb93a386Sopenharmony_ci SkScalar r = fStyle.strokeRec().getWidth() / 2; 645cb93a386Sopenharmony_ci fShape.rect().outset(r, r); 646cb93a386Sopenharmony_ci if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) { 647cb93a386Sopenharmony_ci // There's no dashing to worry about if we got here, so it's okay that this resets 648cb93a386Sopenharmony_ci // winding parameters 649cb93a386Sopenharmony_ci fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r)); 650cb93a386Sopenharmony_ci } 651cb93a386Sopenharmony_ci fStyle = GrStyle::SimpleFill(); 652cb93a386Sopenharmony_ci fSimplified = true; 653cb93a386Sopenharmony_ci return; 654cb93a386Sopenharmony_ci } 655cb93a386Sopenharmony_ci 656cb93a386Sopenharmony_ci // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the 657cb93a386Sopenharmony_ci // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce. 658cb93a386Sopenharmony_ci if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() || 659cb93a386Sopenharmony_ci fStyle.strokeRec().isHairlineStyle()) { 660cb93a386Sopenharmony_ci return; 661cb93a386Sopenharmony_ci } 662cb93a386Sopenharmony_ci 663cb93a386Sopenharmony_ci // Tracks style simplifications, even if the geometry can't be further simplified. 664cb93a386Sopenharmony_ci bool styleSimplified = false; 665cb93a386Sopenharmony_ci if (fStyle.isDashed()) { 666cb93a386Sopenharmony_ci // For dashing a point, if the first interval is on, we can drop the dash and just draw 667cb93a386Sopenharmony_ci // the caps. For dashing a line, if every off interval is 0 length, its a stroke. 668cb93a386Sopenharmony_ci bool dropDash = false; 669cb93a386Sopenharmony_ci if (fShape.isPoint()) { 670cb93a386Sopenharmony_ci dropDash = fStyle.dashIntervalCnt() > 0 && 671cb93a386Sopenharmony_ci SkToBool(fStyle.dashIntervals()[0]); 672cb93a386Sopenharmony_ci } else { 673cb93a386Sopenharmony_ci dropDash = true; 674cb93a386Sopenharmony_ci for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) { 675cb93a386Sopenharmony_ci if (SkToBool(fStyle.dashIntervals()[i])) { 676cb93a386Sopenharmony_ci // An off interval has non-zero length so this won't convert to a simple line 677cb93a386Sopenharmony_ci dropDash = false; 678cb93a386Sopenharmony_ci break; 679cb93a386Sopenharmony_ci } 680cb93a386Sopenharmony_ci } 681cb93a386Sopenharmony_ci } 682cb93a386Sopenharmony_ci 683cb93a386Sopenharmony_ci if (!dropDash) { 684cb93a386Sopenharmony_ci return; 685cb93a386Sopenharmony_ci } 686cb93a386Sopenharmony_ci // Fall through to modifying the shape to respect the new stroke geometry 687cb93a386Sopenharmony_ci fStyle = GrStyle(fStyle.strokeRec(), nullptr); 688cb93a386Sopenharmony_ci // Since the reduced the line or point after dashing is dependent on the caps of the dashes, 689cb93a386Sopenharmony_ci // we reset to be unclosed so we don't override the style based on joins later. 690cb93a386Sopenharmony_ci fClosed = false; 691cb93a386Sopenharmony_ci styleSimplified = true; 692cb93a386Sopenharmony_ci } 693cb93a386Sopenharmony_ci 694cb93a386Sopenharmony_ci // At this point, we're a line or point with no path effects. Any fill portion of the style 695cb93a386Sopenharmony_ci // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke. 696cb93a386Sopenharmony_ci if (fStyle.isSimpleFill()) { 697cb93a386Sopenharmony_ci fShape.reset(); 698cb93a386Sopenharmony_ci fSimplified = true; 699cb93a386Sopenharmony_ci return; 700cb93a386Sopenharmony_ci } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 701cb93a386Sopenharmony_ci // Stroke only 702cb93a386Sopenharmony_ci SkStrokeRec rec = fStyle.strokeRec(); 703cb93a386Sopenharmony_ci rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); 704cb93a386Sopenharmony_ci fStyle = GrStyle(rec, nullptr); 705cb93a386Sopenharmony_ci styleSimplified = true; 706cb93a386Sopenharmony_ci } 707cb93a386Sopenharmony_ci 708cb93a386Sopenharmony_ci // A point or line that was formed by a degenerate closed shape needs its style updated to 709cb93a386Sopenharmony_ci // reflect the fact that it doesn't actually produce caps. 710cb93a386Sopenharmony_ci if (fClosed) { 711cb93a386Sopenharmony_ci SkPaint::Cap cap; 712cb93a386Sopenharmony_ci if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) { 713cb93a386Sopenharmony_ci // As a closed shape, the line moves from a to b and back to a, producing a 180 degree 714cb93a386Sopenharmony_ci // turn. With round joins, this would make a semi-circle at each end, which is visually 715cb93a386Sopenharmony_ci // identical to a round cap on the reduced line geometry. 716cb93a386Sopenharmony_ci cap = SkPaint::kRound_Cap; 717cb93a386Sopenharmony_ci } else { 718cb93a386Sopenharmony_ci // If this were a closed line, the 180 degree turn either is a miter join that exceeds 719cb93a386Sopenharmony_ci // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape 720cb93a386Sopenharmony_ci // of a 180 degreen corner is equivalent to a butt cap. 721cb93a386Sopenharmony_ci // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so 722cb93a386Sopenharmony_ci // it fits this closed line description (it is not two 90 degree turns that could 723cb93a386Sopenharmony_ci // produce miter geometry). 724cb93a386Sopenharmony_ci cap = SkPaint::kButt_Cap; 725cb93a386Sopenharmony_ci } 726cb93a386Sopenharmony_ci 727cb93a386Sopenharmony_ci if (cap != fStyle.strokeRec().getCap() || 728cb93a386Sopenharmony_ci SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) { 729cb93a386Sopenharmony_ci SkStrokeRec rec = fStyle.strokeRec(); 730cb93a386Sopenharmony_ci rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter()); 731cb93a386Sopenharmony_ci fStyle = GrStyle(rec, nullptr); 732cb93a386Sopenharmony_ci styleSimplified = true; 733cb93a386Sopenharmony_ci } 734cb93a386Sopenharmony_ci } 735cb93a386Sopenharmony_ci 736cb93a386Sopenharmony_ci if (fShape.isPoint()) { 737cb93a386Sopenharmony_ci // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point 738cb93a386Sopenharmony_ci // doesn't draw anything, a round cap is an oval and a square cap is a square. 739cb93a386Sopenharmony_ci if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) { 740cb93a386Sopenharmony_ci fShape.reset(); 741cb93a386Sopenharmony_ci } else { 742cb93a386Sopenharmony_ci SkScalar w = fStyle.strokeRec().getWidth() / 2.f; 743cb93a386Sopenharmony_ci SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY}; 744cb93a386Sopenharmony_ci r.outset(w, w); 745cb93a386Sopenharmony_ci 746cb93a386Sopenharmony_ci if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { 747cb93a386Sopenharmony_ci fShape.setRRect(SkRRect::MakeOval(r)); 748cb93a386Sopenharmony_ci } else { 749cb93a386Sopenharmony_ci fShape.setRect(r); 750cb93a386Sopenharmony_ci } 751cb93a386Sopenharmony_ci } 752cb93a386Sopenharmony_ci } else { 753cb93a386Sopenharmony_ci // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we 754cb93a386Sopenharmony_ci // allowed rotation angle, this would work for any lines. 755cb93a386Sopenharmony_ci SkRect rect; 756cb93a386Sopenharmony_ci SkVector outset; 757cb93a386Sopenharmony_ci if (fShape.line().fP1.fY == fShape.line().fP2.fY) { 758cb93a386Sopenharmony_ci rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX); 759cb93a386Sopenharmony_ci rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX); 760cb93a386Sopenharmony_ci rect.fTop = rect.fBottom = fShape.line().fP1.fY; 761cb93a386Sopenharmony_ci outset.fY = fStyle.strokeRec().getWidth() / 2.f; 762cb93a386Sopenharmony_ci outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY; 763cb93a386Sopenharmony_ci } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) { 764cb93a386Sopenharmony_ci rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY); 765cb93a386Sopenharmony_ci rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY); 766cb93a386Sopenharmony_ci rect.fLeft = rect.fRight = fShape.line().fP1.fX; 767cb93a386Sopenharmony_ci outset.fX = fStyle.strokeRec().getWidth() / 2.f; 768cb93a386Sopenharmony_ci outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX; 769cb93a386Sopenharmony_ci } else { 770cb93a386Sopenharmony_ci // Geometrically can't apply the style and turn into a fill, but might still be simpler 771cb93a386Sopenharmony_ci // than before based solely on changes to fStyle. 772cb93a386Sopenharmony_ci fSimplified |= styleSimplified; 773cb93a386Sopenharmony_ci return; 774cb93a386Sopenharmony_ci } 775cb93a386Sopenharmony_ci rect.outset(outset.fX, outset.fY); 776cb93a386Sopenharmony_ci if (rect.isEmpty()) { 777cb93a386Sopenharmony_ci fShape.reset(); 778cb93a386Sopenharmony_ci } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { 779cb93a386Sopenharmony_ci SkASSERT(outset.fX == outset.fY); 780cb93a386Sopenharmony_ci fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY)); 781cb93a386Sopenharmony_ci } else { 782cb93a386Sopenharmony_ci fShape.setRect(rect); 783cb93a386Sopenharmony_ci } 784cb93a386Sopenharmony_ci } 785cb93a386Sopenharmony_ci // If we made it here, the stroke was fully applied to the new shape so we can become a fill. 786cb93a386Sopenharmony_ci fStyle = GrStyle::SimpleFill(); 787cb93a386Sopenharmony_ci fSimplified = true; 788cb93a386Sopenharmony_ci return; 789cb93a386Sopenharmony_ci} 790