1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/include/core/SkRRect.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,306 @@ 1.4 +/* 1.5 + * Copyright 2012 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#ifndef SkRRect_DEFINED 1.12 +#define SkRRect_DEFINED 1.13 + 1.14 +#include "SkRect.h" 1.15 +#include "SkPoint.h" 1.16 + 1.17 +class SkPath; 1.18 +class SkMatrix; 1.19 + 1.20 +// Path forward: 1.21 +// core work 1.22 +// add validate method (all radii positive, all radii sums < rect size, etc.) 1.23 +// add contains(SkRect&) - for clip stack 1.24 +// add contains(SkRRect&) - for clip stack 1.25 +// add heart rect computation (max rect inside RR) 1.26 +// add 9patch rect computation 1.27 +// add growToInclude(SkPath&) 1.28 +// analysis 1.29 +// use growToInclude to fit skp round rects & generate stats (RRs vs. real paths) 1.30 +// check on # of rectorus's the RRs could handle 1.31 +// rendering work 1.32 +// update SkPath.addRRect() to only use quads 1.33 +// add GM and bench 1.34 +// further out 1.35 +// detect and triangulate RRectorii rather than falling back to SW in Ganesh 1.36 +// 1.37 + 1.38 +/** \class SkRRect 1.39 + 1.40 + The SkRRect class represents a rounded rect with a potentially different 1.41 + radii for each corner. It does not have a constructor so must be 1.42 + initialized with one of the initialization functions (e.g., setEmpty, 1.43 + setRectRadii, etc.) 1.44 + 1.45 + This class is intended to roughly match CSS' border-*-*-radius capabilities. 1.46 + This means: 1.47 + If either of a corner's radii are 0 the corner will be square. 1.48 + Negative radii are not allowed (they are clamped to zero). 1.49 + If the corner curves overlap they will be proportionally reduced to fit. 1.50 +*/ 1.51 +class SK_API SkRRect { 1.52 +public: 1.53 + /** 1.54 + * Enum to capture the various possible subtypes of RR. Accessed 1.55 + * by type(). The subtypes become progressively less restrictive. 1.56 + */ 1.57 + enum Type { 1.58 + // !< Internal indicator that the sub type must be computed. 1.59 + kUnknown_Type = -1, 1.60 + 1.61 + // !< The RR is empty 1.62 + kEmpty_Type, 1.63 + 1.64 + //!< The RR is actually a (non-empty) rect (i.e., at least one radius 1.65 + //!< at each corner is zero) 1.66 + kRect_Type, 1.67 + 1.68 + //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal 1.69 + //!< and >= width/2 and all the y radii are equal and >= height/2 1.70 + kOval_Type, 1.71 + 1.72 + //!< The RR is non-empty and all the x radii are equal & all y radii 1.73 + //!< are equal but it is not an oval (i.e., there are lines between 1.74 + //!< the curves) nor a rect (i.e., both radii are non-zero) 1.75 + kSimple_Type, 1.76 + 1.77 + //!< A fully general (non-empty) RR. Some of the x and/or y radii are 1.78 + //!< different from the others and there must be one corner where 1.79 + //!< both radii are non-zero. 1.80 + kComplex_Type, 1.81 + }; 1.82 + 1.83 + /** 1.84 + * Returns the RR's sub type. 1.85 + */ 1.86 + Type getType() const { 1.87 + SkDEBUGCODE(this->validate();) 1.88 + 1.89 + if (kUnknown_Type == fType) { 1.90 + this->computeType(); 1.91 + } 1.92 + SkASSERT(kUnknown_Type != fType); 1.93 + return fType; 1.94 + } 1.95 + 1.96 + Type type() const { return this->getType(); } 1.97 + 1.98 + inline bool isEmpty() const { return kEmpty_Type == this->getType(); } 1.99 + inline bool isRect() const { return kRect_Type == this->getType(); } 1.100 + inline bool isOval() const { return kOval_Type == this->getType(); } 1.101 + inline bool isSimple() const { return kSimple_Type == this->getType(); } 1.102 + inline bool isSimpleCircular() const { 1.103 + return this->isSimple() && fRadii[0].fX == fRadii[0].fY; 1.104 + } 1.105 + inline bool isComplex() const { return kComplex_Type == this->getType(); } 1.106 + 1.107 + bool allCornersCircular() const; 1.108 + 1.109 + /** 1.110 + * Are both x-radii the same on the two left corners, and similar for the top, right, and 1.111 + * bottom. When this is the case the four ellipse centers form a rectangle. 1.112 + */ 1.113 + bool isNinePatch() const { 1.114 + return fRadii[kUpperLeft_Corner].fX == fRadii[kLowerLeft_Corner].fX && 1.115 + fRadii[kUpperRight_Corner].fX == fRadii[kLowerRight_Corner].fX && 1.116 + fRadii[kUpperLeft_Corner].fY == fRadii[kUpperRight_Corner].fY && 1.117 + fRadii[kLowerLeft_Corner].fY == fRadii[kLowerRight_Corner].fY; 1.118 + } 1.119 + 1.120 + SkScalar width() const { return fRect.width(); } 1.121 + SkScalar height() const { return fRect.height(); } 1.122 + 1.123 + /** 1.124 + * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii. 1.125 + */ 1.126 + void setEmpty() { 1.127 + fRect.setEmpty(); 1.128 + memset(fRadii, 0, sizeof(fRadii)); 1.129 + fType = kEmpty_Type; 1.130 + 1.131 + SkDEBUGCODE(this->validate();) 1.132 + } 1.133 + 1.134 + /** 1.135 + * Set this RR to match the supplied rect. All radii will be 0. 1.136 + */ 1.137 + void setRect(const SkRect& rect) { 1.138 + if (rect.isEmpty()) { 1.139 + this->setEmpty(); 1.140 + return; 1.141 + } 1.142 + 1.143 + fRect = rect; 1.144 + memset(fRadii, 0, sizeof(fRadii)); 1.145 + fType = kRect_Type; 1.146 + 1.147 + SkDEBUGCODE(this->validate();) 1.148 + } 1.149 + 1.150 + /** 1.151 + * Set this RR to match the supplied oval. All x radii will equal half the 1.152 + * width and all y radii will equal half the height. 1.153 + */ 1.154 + void setOval(const SkRect& oval) { 1.155 + if (oval.isEmpty()) { 1.156 + this->setEmpty(); 1.157 + return; 1.158 + } 1.159 + 1.160 + SkScalar xRad = SkScalarHalf(oval.width()); 1.161 + SkScalar yRad = SkScalarHalf(oval.height()); 1.162 + 1.163 + fRect = oval; 1.164 + for (int i = 0; i < 4; ++i) { 1.165 + fRadii[i].set(xRad, yRad); 1.166 + } 1.167 + fType = kOval_Type; 1.168 + 1.169 + SkDEBUGCODE(this->validate();) 1.170 + } 1.171 + 1.172 + /** 1.173 + * Initialize the RR with the same radii for all four corners. 1.174 + */ 1.175 + void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad); 1.176 + 1.177 + /** 1.178 + * Initialize the RR with potentially different radii for all four corners. 1.179 + */ 1.180 + void setRectRadii(const SkRect& rect, const SkVector radii[4]); 1.181 + 1.182 + // The radii are stored in UL, UR, LR, LL order. 1.183 + enum Corner { 1.184 + kUpperLeft_Corner, 1.185 + kUpperRight_Corner, 1.186 + kLowerRight_Corner, 1.187 + kLowerLeft_Corner 1.188 + }; 1.189 + 1.190 + const SkRect& rect() const { return fRect; } 1.191 + const SkVector& radii(Corner corner) const { return fRadii[corner]; } 1.192 + const SkRect& getBounds() const { return fRect; } 1.193 + 1.194 + /** 1.195 + * When a rrect is simple, all of its radii are equal. This returns one 1.196 + * of those radii. This call requires the rrect to be non-complex. 1.197 + */ 1.198 + const SkVector& getSimpleRadii() const { 1.199 + SkASSERT(!this->isComplex()); 1.200 + return fRadii[0]; 1.201 + } 1.202 + 1.203 + friend bool operator==(const SkRRect& a, const SkRRect& b) { 1.204 + return a.fRect == b.fRect && 1.205 + SkScalarsEqual(a.fRadii[0].asScalars(), 1.206 + b.fRadii[0].asScalars(), 8); 1.207 + } 1.208 + 1.209 + friend bool operator!=(const SkRRect& a, const SkRRect& b) { 1.210 + return a.fRect != b.fRect || 1.211 + !SkScalarsEqual(a.fRadii[0].asScalars(), 1.212 + b.fRadii[0].asScalars(), 8); 1.213 + } 1.214 + 1.215 + /** 1.216 + * Call inset on the bounds, and adjust the radii to reflect what happens 1.217 + * in stroking: If the corner is sharp (no curvature), leave it alone, 1.218 + * otherwise we grow/shrink the radii by the amount of the inset. If a 1.219 + * given radius becomes negative, it is pinned to 0. 1.220 + * 1.221 + * It is valid for dst == this. 1.222 + */ 1.223 + void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const; 1.224 + 1.225 + void inset(SkScalar dx, SkScalar dy) { 1.226 + this->inset(dx, dy, this); 1.227 + } 1.228 + 1.229 + /** 1.230 + * Call outset on the bounds, and adjust the radii to reflect what happens 1.231 + * in stroking: If the corner is sharp (no curvature), leave it alone, 1.232 + * otherwise we grow/shrink the radii by the amount of the inset. If a 1.233 + * given radius becomes negative, it is pinned to 0. 1.234 + * 1.235 + * It is valid for dst == this. 1.236 + */ 1.237 + void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 1.238 + this->inset(-dx, -dy, dst); 1.239 + } 1.240 + void outset(SkScalar dx, SkScalar dy) { 1.241 + this->inset(-dx, -dy, this); 1.242 + } 1.243 + 1.244 + /** 1.245 + * Translate the rrect by (dx, dy). 1.246 + */ 1.247 + void offset(SkScalar dx, SkScalar dy) { 1.248 + fRect.offset(dx, dy); 1.249 + } 1.250 + 1.251 + /** 1.252 + * Returns true if 'rect' is wholy inside the RR, and both 1.253 + * are not empty. 1.254 + */ 1.255 + bool contains(const SkRect& rect) const; 1.256 + 1.257 + SkDEBUGCODE(void validate() const;) 1.258 + 1.259 + enum { 1.260 + kSizeInMemory = 12 * sizeof(SkScalar) 1.261 + }; 1.262 + 1.263 + /** 1.264 + * Write the rrect into the specified buffer. This is guaranteed to always 1.265 + * write kSizeInMemory bytes, and that value is guaranteed to always be 1.266 + * a multiple of 4. Return kSizeInMemory. 1.267 + */ 1.268 + size_t writeToMemory(void* buffer) const; 1.269 + 1.270 + /** 1.271 + * Reads the rrect from the specified buffer 1.272 + * 1.273 + * If the specified buffer is large enough, this will read kSizeInMemory bytes, 1.274 + * and that value is guaranteed to always be a multiple of 4. 1.275 + * 1.276 + * @param buffer Memory to read from 1.277 + * @param length Amount of memory available in the buffer 1.278 + * @return number of bytes read (must be a multiple of 4) or 1.279 + * 0 if there was not enough memory available 1.280 + */ 1.281 + size_t readFromMemory(const void* buffer, size_t length); 1.282 + 1.283 + /** 1.284 + * Transform by the specified matrix, and put the result in dst. 1.285 + * 1.286 + * @param matrix SkMatrix specifying the transform. Must only contain 1.287 + * scale and/or translate, or this call will fail. 1.288 + * @param dst SkRRect to store the result. It is an error to use this, 1.289 + * which would make this function no longer const. 1.290 + * @return true on success, false on failure. If false, dst is unmodified. 1.291 + */ 1.292 + bool transform(const SkMatrix& matrix, SkRRect* dst) const; 1.293 + 1.294 +private: 1.295 + SkRect fRect; 1.296 + // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[] 1.297 + SkVector fRadii[4]; 1.298 + mutable Type fType; 1.299 + // TODO: add padding so we can use memcpy for flattening and not copy 1.300 + // uninitialized data 1.301 + 1.302 + void computeType() const; 1.303 + bool checkCornerContainment(SkScalar x, SkScalar y) const; 1.304 + 1.305 + // to access fRadii directly 1.306 + friend class SkPath; 1.307 +}; 1.308 + 1.309 +#endif