GrShape.h revision 06115ee4300ef6756729dfbcb3e2fc70ebf0413a
1/* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#ifndef GrShape_DEFINED 9#define GrShape_DEFINED 10 11#include "GrStyle.h" 12#include "SkPath.h" 13#include "SkPathPriv.h" 14#include "SkRRect.h" 15#include "SkTemplates.h" 16#include "SkTLazy.h" 17 18/** 19 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. 20 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry 21 * reflects the styling information (e.g. is stroked). It is also possible to apply just the 22 * path effect from the style. In this case the resulting shape will include any remaining 23 * stroking information that is to be applied after the path effect. 24 * 25 * Shapes can produce keys that represent only the geometry information, not the style. Note that 26 * when styling information is applied to produce a new shape then the style has been converted 27 * to geometric information and is included in the new shape's key. When the same style is applied 28 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes 29 * will be the same. 30 * 31 * Currently this can only be constructed from a path, rect, or rrect though it can become a path 32 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries 33 * that have SkCanvas::draw APIs. 34 */ 35class GrShape { 36public: 37 GrShape() : fType(Type::kEmpty) {} 38 39 explicit GrShape(const SkPath& path) 40 : fType(Type::kPath) 41 , fPath(&path) { 42 this->attemptToReduceFromPath(); 43 } 44 45 explicit GrShape(const SkRRect& rrect) 46 : fType(Type::kRRect) 47 , fRRect(rrect) { 48 fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir); 49 this->attemptToReduceFromRRect(); 50 } 51 52 explicit GrShape(const SkRect& rect) 53 : fType(Type::kRRect) 54 , fRRect(SkRRect::MakeRect(rect)) { 55 fRRectStart = DefaultRectDirAndStartIndex(rect, false, &fRRectDir); 56 this->attemptToReduceFromRRect(); 57 } 58 59 GrShape(const SkPath& path, const GrStyle& style) 60 : fType(Type::kPath) 61 , fPath(&path) 62 , fStyle(style) { 63 this->attemptToReduceFromPath(); 64 } 65 66 GrShape(const SkRRect& rrect, const GrStyle& style) 67 : fType(Type::kRRect) 68 , fRRect(rrect) 69 , fStyle(style) { 70 fRRectStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(), &fRRectDir); 71 this->attemptToReduceFromRRect(); 72 } 73 74 GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, const GrStyle& style) 75 : fType(Type::kRRect) 76 , fRRect(rrect) 77 , fStyle(style) { 78 if (style.pathEffect()) { 79 fRRectDir = dir; 80 fRRectStart = start; 81 } else { 82 fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir); 83 } 84 this->attemptToReduceFromRRect(); 85 } 86 87 GrShape(const SkRect& rect, const GrStyle& style) 88 : fType(Type::kRRect) 89 , fRRect(SkRRect::MakeRect(rect)) 90 , fStyle(style) { 91 fRRectStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(), &fRRectDir); 92 this->attemptToReduceFromRRect(); 93 } 94 95 GrShape(const SkPath& path, const SkPaint& paint) 96 : fType(Type::kPath) 97 , fPath(&path) 98 , fStyle(paint) { 99 this->attemptToReduceFromPath(); 100 } 101 102 GrShape(const SkRRect& rrect, const SkPaint& paint) 103 : fType(Type::kRRect) 104 , fRRect(rrect) 105 , fStyle(paint) { 106 fRRectStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(), &fRRectDir); 107 this->attemptToReduceFromRRect(); 108 } 109 110 GrShape(const SkRect& rect, const SkPaint& paint) 111 : fType(Type::kRRect) 112 , fRRect(SkRRect::MakeRect(rect)) 113 , fStyle(paint) { 114 fRRectStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(), &fRRectDir); 115 this->attemptToReduceFromRRect(); 116 } 117 118 GrShape(const GrShape&); 119 GrShape& operator=(const GrShape& that); 120 121 ~GrShape() { 122 if (Type::kPath == fType) { 123 fPath.reset(); 124 } 125 } 126 127 const GrStyle& style() const { return fStyle; } 128 129 /** 130 * Returns a shape that has either applied the path effect or path effect and stroking 131 * information from this shape's style to its geometry. Scale is used when approximating the 132 * output geometry and typically is computed from the view matrix 133 */ 134 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) { 135 return GrShape(*this, apply, scale); 136 } 137 138 /** Returns the unstyled geometry as a rrect if possible. */ 139 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start) const { 140 if (Type::kRRect != fType) { 141 return false; 142 } 143 if (rrect) { 144 *rrect = fRRect; 145 } 146 if (dir) { 147 *dir = fRRectDir; 148 } 149 if (start) { 150 *start = fRRectStart; 151 } 152 return true; 153 } 154 155 /** Returns the unstyled geometry as a path. */ 156 void asPath(SkPath* out) const { 157 switch (fType) { 158 case Type::kEmpty: 159 out->reset(); 160 break; 161 case Type::kRRect: 162 out->reset(); 163 out->addRRect(fRRect, fRRectDir, fRRectStart); 164 break; 165 case Type::kPath: 166 *out = *fPath.get(); 167 break; 168 } 169 } 170 171 /** 172 * Returns whether the geometry is empty. Note that applying the style could produce a 173 * non-empty shape. 174 */ 175 bool isEmpty() const { return Type::kEmpty == fType; } 176 177 /** Gets the bounds of the geometry without reflecting the shape's styling. */ 178 const SkRect& bounds() const; 179 180 /** Gets the bounds of the geometry reflecting the shape's styling. */ 181 void styledBounds(SkRect* bounds) const; 182 183 /** 184 * Is it known that the unstyled geometry has no unclosed contours. This means that it will 185 * not have any caps if stroked (modulo the effect of any path effect). 186 */ 187 bool knownToBeClosed() const { 188 switch (fType) { 189 case Type::kEmpty: 190 return true; 191 case Type::kRRect: 192 return true; 193 case Type::kPath: 194 return false; 195 } 196 return false; 197 } 198 199 uint32_t segmentMask() const { 200 switch (fType) { 201 case Type::kEmpty: 202 return 0; 203 case Type::kRRect: 204 if (fRRect.getType() == SkRRect::kOval_Type) { 205 return SkPath::kConic_SegmentMask; 206 } else if (fRRect.getType() == SkRRect::kRect_Type) { 207 return SkPath::kLine_SegmentMask; 208 } 209 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask; 210 case Type::kPath: 211 return fPath.get()->getSegmentMasks(); 212 } 213 return 0; 214 } 215 216 /** 217 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling). 218 * A negative value is returned if the shape has no key (shouldn't be cached). 219 */ 220 int unstyledKeySize() const; 221 222 /** 223 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough 224 * space allocated for the key and that unstyledKeySize() does not return a negative value 225 * for this shape. 226 */ 227 void writeUnstyledKey(uint32_t* key) const; 228 229private: 230 enum class Type { 231 kEmpty, 232 kRRect, 233 kPath, 234 }; 235 236 /** Constructor used by the applyStyle() function */ 237 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 238 239 /** 240 * Determines the key we should inherit from the input shape's geometry and style when 241 * we are applying the style to create a new shape. 242 */ 243 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 244 245 void attemptToReduceFromPath() { 246 SkASSERT(Type::kPath == fType); 247 fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, &fRRectDir, &fRRectStart, 248 fStyle.pathEffect(), fStyle.strokeRec()); 249 if (Type::kPath != fType) { 250 fPath.reset(); 251 fInheritedKey.reset(0); 252 } 253 } 254 255 void attemptToReduceFromRRect() { 256 SkASSERT(Type::kRRect == fType); 257 SkASSERT(!fInheritedKey.count()); 258 if (fRRect.isEmpty()) { 259 fType = Type::kEmpty; 260 } 261 } 262 263 static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect, 264 SkPath::Direction* rrectDir, unsigned* rrectStart, 265 const SkPathEffect* pe, const SkStrokeRec& strokeRec); 266 267 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction; 268 static constexpr unsigned kDefaultRRectStart = 0; 269 270 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect, 271 SkPath::Direction* dir) { 272 *dir = kDefaultRRectDir; 273 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise 274 // beginning at index 0 (which happens to correspond to rrect index 0 or 7). 275 if (!hasPathEffect) { 276 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 277 return kDefaultRRectStart; 278 } 279 // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 280 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 281 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort. 282 bool swapX = rect.fLeft > rect.fRight; 283 bool swapY = rect.fTop > rect.fBottom; 284 if (swapX && swapY) { 285 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices. 286 return 2 * 2; 287 } else if (swapX) { 288 *dir = SkPath::kCCW_Direction; 289 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices. 290 return 2 * 1; 291 } else if (swapY) { 292 *dir = SkPath::kCCW_Direction; 293 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices. 294 return 2 * 3; 295 } 296 return 0; 297 } 298 299 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect, 300 SkPath::Direction* dir) { 301 // This comes from SkPath's interface. The default for adding a SkRRect to a path is 302 // clockwise beginning at starting index 6. 303 static constexpr unsigned kPathRRectStartIdx = 6; 304 *dir = kDefaultRRectDir; 305 if (!hasPathEffect) { 306 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 307 return kDefaultRRectStart; 308 } 309 return kPathRRectStartIdx; 310 } 311 312 Type fType; 313 SkRRect fRRect; 314 SkPath::Direction fRRectDir; 315 unsigned fRRectStart; 316 SkTLazy<SkPath> fPath; 317 GrStyle fStyle; 318 SkAutoSTArray<8, uint32_t> fInheritedKey; 319}; 320#endif 321