LCOV - code coverage report
Current view: top level - gfx/skia/skia/include/core - SkRRect.h (source / functions) Hit Total Coverage
Test: output.info Lines: 1 75 1.3 %
Date: 2017-07-14 16:53:18 Functions: 1 27 3.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2012 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 SkRRect_DEFINED
       9             : #define SkRRect_DEFINED
      10             : 
      11             : #include "SkRect.h"
      12             : #include "SkPoint.h"
      13             : 
      14             : class SkPath;
      15             : class SkMatrix;
      16             : 
      17             : // Path forward:
      18             : //   core work
      19             : //      add contains(SkRect&)  - for clip stack
      20             : //      add contains(SkRRect&) - for clip stack
      21             : //      add heart rect computation (max rect inside RR)
      22             : //      add 9patch rect computation
      23             : //      add growToInclude(SkPath&)
      24             : //   analysis
      25             : //      use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
      26             : //      check on # of rectorus's the RRs could handle
      27             : //   rendering work
      28             : //      update SkPath.addRRect() to only use quads
      29             : //      add GM and bench
      30             : //   further out
      31             : //      detect and triangulate RRectorii rather than falling back to SW in Ganesh
      32             : //
      33             : 
      34             : /** \class SkRRect
      35             : 
      36             :     The SkRRect class represents a rounded rect with a potentially different
      37             :     radii for each corner. It does not have a constructor so must be
      38             :     initialized with one of the initialization functions (e.g., setEmpty,
      39             :     setRectRadii, etc.)
      40             : 
      41             :     This class is intended to roughly match CSS' border-*-*-radius capabilities.
      42             :     This means:
      43             :         If either of a corner's radii are 0 the corner will be square.
      44             :         Negative radii are not allowed (they are clamped to zero).
      45             :         If the corner curves overlap they will be proportionally reduced to fit.
      46             : */
      47             : class SK_API SkRRect {
      48             : public:
      49          39 :     SkRRect() { /* unititialized */ }
      50             :     SkRRect(const SkRRect&) = default;
      51             :     SkRRect& operator=(const SkRRect&) = default;
      52             : 
      53             :     /**
      54             :      * Enum to capture the various possible subtypes of RR. Accessed
      55             :      * by type(). The subtypes become progressively less restrictive.
      56             :      */
      57             :     enum Type {
      58             :         // !< The RR is empty
      59             :         kEmpty_Type,
      60             : 
      61             :         //!< The RR is actually a (non-empty) rect (i.e., at least one radius
      62             :         //!< at each corner is zero)
      63             :         kRect_Type,
      64             : 
      65             :         //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
      66             :         //!< and >= width/2 and all the y radii are equal and >= height/2
      67             :         kOval_Type,
      68             : 
      69             :         //!< The RR is non-empty and all the x radii are equal & all y radii
      70             :         //!< are equal but it is not an oval (i.e., there are lines between
      71             :         //!< the curves) nor a rect (i.e., both radii are non-zero)
      72             :         kSimple_Type,
      73             : 
      74             :         //!< The RR is non-empty and the two left x radii are equal, the two top
      75             :         //!< y radii are equal, and the same for the right and bottom but it is
      76             :         //!< neither an rect, oval, nor a simple RR. It is called "nine patch"
      77             :         //!< because the centers of the corner ellipses form an axis aligned
      78             :         //!< rect with edges that divide the RR into an 9 rectangular patches:
      79             :         //!< an interior patch, four edge patches, and four corner patches.
      80             :         kNinePatch_Type,
      81             : 
      82             :         //!< A fully general (non-empty) RR. Some of the x and/or y radii are
      83             :         //!< different from the others and there must be one corner where
      84             :         //!< both radii are non-zero.
      85             :         kComplex_Type,
      86             :     };
      87             : 
      88             :     /**
      89             :      * Returns the RR's sub type.
      90             :      */
      91           0 :     Type getType() const {
      92           0 :         SkASSERT(this->isValid());
      93           0 :         return static_cast<Type>(fType);
      94             :     }
      95             : 
      96           0 :     Type type() const { return this->getType(); }
      97             : 
      98           0 :     inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
      99           0 :     inline bool isRect() const { return kRect_Type == this->getType(); }
     100           0 :     inline bool isOval() const { return kOval_Type == this->getType(); }
     101           0 :     inline bool isSimple() const { return kSimple_Type == this->getType(); }
     102             :     // TODO: should isSimpleCircular & isCircle take a tolerance? This could help
     103             :     // instances where the mapping to device space is noisy.
     104           0 :     inline bool isSimpleCircular() const {
     105           0 :         return this->isSimple() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
     106             :     }
     107             :     inline bool isCircle() const {
     108             :         return this->isOval() && SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY);
     109             :     }
     110           0 :     inline bool isNinePatch() const { return kNinePatch_Type == this->getType(); }
     111           0 :     inline bool isComplex() const { return kComplex_Type == this->getType(); }
     112             : 
     113             :     bool allCornersCircular() const;
     114             : 
     115           0 :     SkScalar width() const { return fRect.width(); }
     116           0 :     SkScalar height() const { return fRect.height(); }
     117             : 
     118             :     /**
     119             :      * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
     120             :      */
     121           0 :     void setEmpty() {
     122           0 :         fRect.setEmpty();
     123           0 :         memset(fRadii, 0, sizeof(fRadii));
     124           0 :         fType = kEmpty_Type;
     125             : 
     126           0 :         SkASSERT(this->isValid());
     127           0 :     }
     128             : 
     129             :     /**
     130             :      * Set this RR to match the supplied rect. All radii will be 0.
     131             :      */
     132           0 :     void setRect(const SkRect& rect) {
     133           0 :         fRect = rect;
     134           0 :         fRect.sort();
     135             : 
     136           0 :         if (fRect.isEmpty()) {
     137           0 :             this->setEmpty();
     138           0 :             return;
     139             :         }
     140             : 
     141           0 :         memset(fRadii, 0, sizeof(fRadii));
     142           0 :         fType = kRect_Type;
     143             : 
     144           0 :         SkASSERT(this->isValid());
     145             :     }
     146             : 
     147             :     static SkRRect MakeEmpty() {
     148             :         SkRRect rr;
     149             :         rr.setEmpty();
     150             :         return rr;
     151             :     }
     152             : 
     153           0 :     static SkRRect MakeRect(const SkRect& r) {
     154           0 :         SkRRect rr;
     155           0 :         rr.setRect(r);
     156           0 :         return rr;
     157             :     }
     158             : 
     159           0 :     static SkRRect MakeOval(const SkRect& oval) {
     160           0 :         SkRRect rr;
     161           0 :         rr.setOval(oval);
     162           0 :         return rr;
     163             :     }
     164             : 
     165           0 :     static SkRRect MakeRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
     166           0 :         SkRRect rr;
     167           0 :         rr.setRectXY(rect, xRad, yRad);
     168           0 :         return rr;
     169             :     }
     170             : 
     171             :     /**
     172             :      * Set this RR to match the supplied oval. All x radii will equal half the
     173             :      * width and all y radii will equal half the height.
     174             :      */
     175           0 :     void setOval(const SkRect& oval) {
     176           0 :         fRect = oval;
     177           0 :         fRect.sort();
     178             : 
     179           0 :         if (fRect.isEmpty()) {
     180           0 :             this->setEmpty();
     181           0 :             return;
     182             :         }
     183             : 
     184           0 :         SkScalar xRad = SkScalarHalf(fRect.width());
     185           0 :         SkScalar yRad = SkScalarHalf(fRect.height());
     186             : 
     187           0 :         for (int i = 0; i < 4; ++i) {
     188           0 :             fRadii[i].set(xRad, yRad);
     189             :         }
     190           0 :         fType = kOval_Type;
     191             : 
     192           0 :         SkASSERT(this->isValid());
     193             :     }
     194             : 
     195             :     /**
     196             :      * Initialize the RR with the same radii for all four corners.
     197             :      */
     198             :     void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
     199             : 
     200             :     /**
     201             :      * Initialize the rr with one radius per-side.
     202             :      */
     203             :     void setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
     204             :                       SkScalar rightRad, SkScalar bottomRad);
     205             : 
     206             :     /**
     207             :      * Initialize the RR with potentially different radii for all four corners.
     208             :      */
     209             :     void setRectRadii(const SkRect& rect, const SkVector radii[4]);
     210             : 
     211             :     // The radii are stored in UL, UR, LR, LL order.
     212             :     enum Corner {
     213             :         kUpperLeft_Corner,
     214             :         kUpperRight_Corner,
     215             :         kLowerRight_Corner,
     216             :         kLowerLeft_Corner
     217             :     };
     218             : 
     219           0 :     const SkRect& rect() const { return fRect; }
     220           0 :     const SkVector& radii(Corner corner) const { return fRadii[corner]; }
     221           0 :     const SkRect& getBounds() const { return fRect; }
     222             : 
     223             :     /**
     224             :      *  When a rrect is simple, all of its radii are equal. This returns one
     225             :      *  of those radii. This call requires the rrect to be non-complex.
     226             :      */
     227           0 :     const SkVector& getSimpleRadii() const {
     228           0 :         SkASSERT(!this->isComplex());
     229           0 :         return fRadii[0];
     230             :     }
     231             : 
     232           0 :     friend bool operator==(const SkRRect& a, const SkRRect& b) {
     233           0 :         return a.fRect == b.fRect &&
     234           0 :                SkScalarsEqual(a.fRadii[0].asScalars(),
     235           0 :                               b.fRadii[0].asScalars(), 8);
     236             :     }
     237             : 
     238           0 :     friend bool operator!=(const SkRRect& a, const SkRRect& b) {
     239           0 :         return a.fRect != b.fRect ||
     240           0 :                !SkScalarsEqual(a.fRadii[0].asScalars(),
     241           0 :                                b.fRadii[0].asScalars(), 8);
     242             :     }
     243             : 
     244             :     /**
     245             :      *  Call inset on the bounds, and adjust the radii to reflect what happens
     246             :      *  in stroking: If the corner is sharp (no curvature), leave it alone,
     247             :      *  otherwise we grow/shrink the radii by the amount of the inset. If a
     248             :      *  given radius becomes negative, it is pinned to 0.
     249             :      *
     250             :      *  It is valid for dst == this.
     251             :      */
     252             :     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
     253             : 
     254             :     void inset(SkScalar dx, SkScalar dy) {
     255             :         this->inset(dx, dy, this);
     256             :     }
     257             : 
     258             :     /**
     259             :      *  Call outset on the bounds, and adjust the radii to reflect what happens
     260             :      *  in stroking: If the corner is sharp (no curvature), leave it alone,
     261             :      *  otherwise we grow/shrink the radii by the amount of the inset. If a
     262             :      *  given radius becomes negative, it is pinned to 0.
     263             :      *
     264             :      *  It is valid for dst == this.
     265             :      */
     266           0 :     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
     267           0 :         this->inset(-dx, -dy, dst);
     268           0 :     }
     269             :     void outset(SkScalar dx, SkScalar dy) {
     270             :         this->inset(-dx, -dy, this);
     271             :     }
     272             : 
     273             :     /**
     274             :      * Translate the rrect by (dx, dy).
     275             :      */
     276           0 :     void offset(SkScalar dx, SkScalar dy) {
     277           0 :         fRect.offset(dx, dy);
     278           0 :     }
     279             : 
     280             :     SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const {
     281             :         return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
     282             :     }
     283             : 
     284             :     /**
     285             :      *  Returns true if 'rect' is wholy inside the RR, and both
     286             :      *  are not empty.
     287             :      */
     288             :     bool contains(const SkRect& rect) const;
     289             : 
     290             :     bool isValid() const;
     291             : 
     292             :     enum {
     293             :         kSizeInMemory = 12 * sizeof(SkScalar)
     294             :     };
     295             : 
     296             :     /**
     297             :      *  Write the rrect into the specified buffer. This is guaranteed to always
     298             :      *  write kSizeInMemory bytes, and that value is guaranteed to always be
     299             :      *  a multiple of 4. Return kSizeInMemory.
     300             :      */
     301             :     size_t writeToMemory(void* buffer) const;
     302             : 
     303             :     /**
     304             :      * Reads the rrect from the specified buffer
     305             :      *
     306             :      * If the specified buffer is large enough, this will read kSizeInMemory bytes,
     307             :      * and that value is guaranteed to always be a multiple of 4.
     308             :      *
     309             :      * @param buffer Memory to read from
     310             :      * @param length Amount of memory available in the buffer
     311             :      * @return number of bytes read (must be a multiple of 4) or
     312             :      *         0 if there was not enough memory available
     313             :      */
     314             :     size_t readFromMemory(const void* buffer, size_t length);
     315             : 
     316             :     /**
     317             :      *  Transform by the specified matrix, and put the result in dst.
     318             :      *
     319             :      *  @param matrix SkMatrix specifying the transform. Must only contain
     320             :      *      scale and/or translate, or this call will fail.
     321             :      *  @param dst SkRRect to store the result. It is an error to use this,
     322             :      *      which would make this function no longer const.
     323             :      *  @return true on success, false on failure. If false, dst is unmodified.
     324             :      */
     325             :     bool transform(const SkMatrix& matrix, SkRRect* dst) const;
     326             : 
     327             :     void dump(bool asHex) const;
     328           0 :     void dump() const { this->dump(false); }
     329             :     void dumpHex() const { this->dump(true); }
     330             : 
     331             : private:
     332             :     SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type)
     333             :         : fRect(rect)
     334             :         , fRadii{radii[0], radii[1], radii[2], radii[3]}
     335             :         , fType(type) {}
     336             : 
     337             :     SkRect fRect;
     338             :     // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
     339             :     SkVector fRadii[4];
     340             :     // use an explicitly sized type so we're sure the class is dense (no uninitialized bytes)
     341             :     int32_t fType;
     342             :     // TODO: add padding so we can use memcpy for flattening and not copy
     343             :     // uninitialized data
     344             : 
     345             :     void computeType();
     346             :     bool checkCornerContainment(SkScalar x, SkScalar y) const;
     347             :     void scaleRadii();
     348             : 
     349             :     // to access fRadii directly
     350             :     friend class SkPath;
     351             : };
     352             : 
     353             : #endif

Generated by: LCOV version 1.13