2012-11-29 13:24:55 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef SkRRect_DEFINED
|
|
|
|
#define SkRRect_DEFINED
|
|
|
|
|
|
|
|
#include "SkRect.h"
|
|
|
|
#include "SkPoint.h"
|
|
|
|
|
2012-12-12 20:48:18 +00:00
|
|
|
class SkPath;
|
|
|
|
|
2012-11-29 13:24:55 +00:00
|
|
|
// Path forward:
|
|
|
|
// core work
|
|
|
|
// add validate method (all radii positive, all radii sums < rect size, etc.)
|
|
|
|
// add contains(SkRect&) - for clip stack
|
|
|
|
// add contains(SkRRect&) - for clip stack
|
|
|
|
// add heart rect computation (max rect inside RR)
|
|
|
|
// add 9patch rect computation
|
|
|
|
// add growToInclude(SkPath&)
|
|
|
|
// analysis
|
|
|
|
// use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
|
|
|
|
// check on # of rectorus's the RRs could handle
|
|
|
|
// rendering work
|
|
|
|
// add entry points (clipRRect, drawRRect) - plumb down to SkDevice
|
|
|
|
// update SkPath.addRRect() to take an SkRRect - only use quads
|
|
|
|
// -- alternatively add addRRectToPath here
|
|
|
|
// add GM and bench
|
|
|
|
// clipping opt
|
|
|
|
// update SkClipStack to perform logic with RRs
|
|
|
|
// further out
|
|
|
|
// add RR rendering shader to Ganesh (akin to cicle drawing code)
|
|
|
|
// - only for simple RRs
|
|
|
|
// detect and triangulate RRectorii rather than falling back to SW in Ganesh
|
2012-11-30 02:01:24 +00:00
|
|
|
//
|
2012-11-29 13:24:55 +00:00
|
|
|
|
|
|
|
/** \class SkRRect
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
The SkRRect class represents a rounded rect with a potentially different
|
|
|
|
radii for each corner. It does not have a constructor so must be
|
|
|
|
initialized with one of the initialization functions (e.g., setEmpty,
|
2012-11-29 13:24:55 +00:00
|
|
|
setRectRadii, etc.)
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
This class is intended to roughly match CSS' border-*-*-radius capabilities.
|
2012-11-29 13:24:55 +00:00
|
|
|
This means:
|
2012-11-30 02:01:24 +00:00
|
|
|
If either of a corner's radii are 0 the corner will be square.
|
2012-11-29 13:24:55 +00:00
|
|
|
Negative radii are not allowed (they are clamped to zero).
|
|
|
|
If the corner curves overlap they will be proportionally reduced to fit.
|
|
|
|
*/
|
|
|
|
class SK_API SkRRect {
|
|
|
|
public:
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Enum to capture the various possible subtypes of RR. Accessed
|
|
|
|
* by type(). The subtypes become progressively less restrictive.
|
|
|
|
*/
|
|
|
|
enum Type {
|
2012-12-03 11:41:21 +00:00
|
|
|
// !< Internal indicator that the sub type must be computed.
|
|
|
|
kUnknown_Type = -1,
|
|
|
|
|
2012-11-29 13:24:55 +00:00
|
|
|
// !< The RR is empty
|
|
|
|
kEmpty_Type,
|
|
|
|
|
|
|
|
//!< The RR is actually a (non-empty) rect (i.e., at least one radius
|
|
|
|
//!< at each corner is zero)
|
2012-11-30 02:01:24 +00:00
|
|
|
kRect_Type,
|
2012-11-29 13:24:55 +00:00
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
//!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
|
2012-11-29 13:24:55 +00:00
|
|
|
//!< and >= width/2 and all the y radii are equal and >= height/2
|
2012-11-30 02:01:24 +00:00
|
|
|
kOval_Type,
|
2012-11-29 13:24:55 +00:00
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
//!< The RR is non-empty and all the x radii are equal & all y radii
|
|
|
|
//!< are equal but it is not an oval (i.e., there are lines between
|
2012-11-29 13:24:55 +00:00
|
|
|
//!< the curves) nor a rect (i.e., both radii are non-zero)
|
2012-11-30 02:01:24 +00:00
|
|
|
kSimple_Type,
|
2012-11-29 13:24:55 +00:00
|
|
|
|
|
|
|
//!< A fully general (non-empty) RR. Some of the x and/or y radii are
|
|
|
|
//!< different from the others and there must be one corner where
|
|
|
|
//!< both radii are non-zero.
|
|
|
|
kComplex_Type,
|
|
|
|
};
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Returns the RR's sub type.
|
|
|
|
*/
|
2012-12-12 20:48:18 +00:00
|
|
|
Type getType() const {
|
2012-11-29 13:24:55 +00:00
|
|
|
SkDEBUGCODE(this->validate();)
|
|
|
|
|
|
|
|
if (kUnknown_Type == fType) {
|
|
|
|
this->computeType();
|
|
|
|
}
|
|
|
|
SkASSERT(kUnknown_Type != fType);
|
|
|
|
return fType;
|
|
|
|
}
|
|
|
|
|
2012-12-12 20:48:18 +00:00
|
|
|
Type type() const { return this->getType(); }
|
|
|
|
|
|
|
|
inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
|
|
|
|
inline bool isRect() const { return kRect_Type == this->getType(); }
|
|
|
|
inline bool isOval() const { return kOval_Type == this->getType(); }
|
|
|
|
inline bool isSimple() const { return kSimple_Type == this->getType(); }
|
|
|
|
inline bool isComplex() const { return kComplex_Type == this->getType(); }
|
|
|
|
|
2012-12-17 02:10:42 +00:00
|
|
|
SkScalar width() const { return fRect.width(); }
|
|
|
|
SkScalar height() const { return fRect.height(); }
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
|
|
|
|
*/
|
2012-11-30 02:01:24 +00:00
|
|
|
void setEmpty() {
|
2012-11-29 13:24:55 +00:00
|
|
|
fRect.setEmpty();
|
|
|
|
memset(fRadii, 0, sizeof(fRadii));
|
|
|
|
fType = kEmpty_Type;
|
|
|
|
|
|
|
|
SkDEBUGCODE(this->validate();)
|
|
|
|
}
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Set this RR to match the supplied rect. All radii will be 0.
|
|
|
|
*/
|
|
|
|
void setRect(const SkRect& rect) {
|
|
|
|
if (rect.isEmpty()) {
|
|
|
|
this->setEmpty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fRect = rect;
|
|
|
|
memset(fRadii, 0, sizeof(fRadii));
|
|
|
|
fType = kRect_Type;
|
|
|
|
|
|
|
|
SkDEBUGCODE(this->validate();)
|
|
|
|
}
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Set this RR to match the supplied oval. All x radii will equal half the
|
|
|
|
* width and all y radii will equal half the height.
|
|
|
|
*/
|
|
|
|
void setOval(const SkRect& oval) {
|
|
|
|
if (oval.isEmpty()) {
|
|
|
|
this->setEmpty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkScalar xRad = SkScalarHalf(oval.width());
|
|
|
|
SkScalar yRad = SkScalarHalf(oval.height());
|
|
|
|
|
|
|
|
fRect = oval;
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
fRadii[i].set(xRad, yRad);
|
|
|
|
}
|
|
|
|
fType = kOval_Type;
|
|
|
|
|
|
|
|
SkDEBUGCODE(this->validate();)
|
|
|
|
}
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Initialize the RR with the same radii for all four corners.
|
|
|
|
*/
|
|
|
|
void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
/**
|
2012-11-29 13:24:55 +00:00
|
|
|
* Initialize the RR with potentially different radii for all four corners.
|
|
|
|
*/
|
|
|
|
void setRectRadii(const SkRect& rect, const SkVector radii[4]);
|
|
|
|
|
2012-11-30 02:01:24 +00:00
|
|
|
// The radii are stored in UL, UR, LR, LL order.
|
2012-11-29 13:24:55 +00:00
|
|
|
enum Corner {
|
|
|
|
kUpperLeft_Corner,
|
|
|
|
kUpperRight_Corner,
|
|
|
|
kLowerRight_Corner,
|
|
|
|
kLowerLeft_Corner
|
|
|
|
};
|
|
|
|
|
|
|
|
const SkRect& rect() const { return fRect; }
|
|
|
|
const SkVector& radii(Corner corner) const { return fRadii[corner]; }
|
2012-12-12 20:48:18 +00:00
|
|
|
const SkRect& getBounds() const { return fRect; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When a rrect is simple, all of its radii are equal. This returns one
|
|
|
|
* of those radii. This call requires the rrect to be non-complex.
|
|
|
|
*/
|
|
|
|
const SkVector& getSimpleRadii() const {
|
|
|
|
SkASSERT(!this->isComplex());
|
|
|
|
return fRadii[0];
|
|
|
|
}
|
2012-11-29 13:24:55 +00:00
|
|
|
|
|
|
|
friend bool operator==(const SkRRect& a, const SkRRect& b) {
|
|
|
|
return a.fRect == b.fRect &&
|
2013-03-08 18:35:13 +00:00
|
|
|
SkScalarsEqual(a.fRadii[0].asScalars(),
|
|
|
|
b.fRadii[0].asScalars(), 8);
|
2012-11-29 13:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
friend bool operator!=(const SkRRect& a, const SkRRect& b) {
|
|
|
|
return a.fRect != b.fRect ||
|
2013-03-08 18:35:13 +00:00
|
|
|
!SkScalarsEqual(a.fRadii[0].asScalars(),
|
|
|
|
b.fRadii[0].asScalars(), 8);
|
2012-11-29 13:24:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if (p.fX,p.fY) is inside the RR, and the RR
|
|
|
|
* is not empty.
|
|
|
|
*
|
|
|
|
* Contains treats the left and top differently from the right and bottom.
|
|
|
|
* The left and top coordinates of the RR are themselves considered
|
|
|
|
* to be inside, while the right and bottom are not. All the points on the
|
|
|
|
* edges of the corners are considered to be inside.
|
|
|
|
*/
|
|
|
|
bool contains(const SkPoint& p) const {
|
|
|
|
return contains(p.fX, p.fY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if (x,y) is inside the RR, and the RR
|
|
|
|
* is not empty.
|
|
|
|
*
|
|
|
|
* Contains treats the left and top differently from the right and bottom.
|
|
|
|
* The left and top coordinates of the RR are themselves considered
|
|
|
|
* to be inside, while the right and bottom are not. All the points on the
|
|
|
|
* edges of the corners are considered to be inside.
|
|
|
|
*/
|
|
|
|
bool contains(SkScalar x, SkScalar y) const;
|
|
|
|
|
2012-12-23 23:11:21 +00:00
|
|
|
/**
|
|
|
|
* Call inset on the bounds, and adjust the radii to reflect what happens
|
|
|
|
* in stroking: If the corner is sharp (no curvature), leave it alone,
|
|
|
|
* otherwise we grow/shrink the radii by the amount of the inset. If a
|
|
|
|
* given radius becomes negative, it is pinned to 0.
|
|
|
|
*
|
|
|
|
* It is valid for dst == this.
|
|
|
|
*/
|
2012-12-17 02:10:42 +00:00
|
|
|
void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
|
2012-12-23 23:11:21 +00:00
|
|
|
|
2012-12-17 02:10:42 +00:00
|
|
|
void inset(SkScalar dx, SkScalar dy) {
|
|
|
|
this->inset(dx, dy, this);
|
|
|
|
}
|
2012-12-23 23:11:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Call outset on the bounds, and adjust the radii to reflect what happens
|
|
|
|
* in stroking: If the corner is sharp (no curvature), leave it alone,
|
|
|
|
* otherwise we grow/shrink the radii by the amount of the inset. If a
|
|
|
|
* given radius becomes negative, it is pinned to 0.
|
|
|
|
*
|
|
|
|
* It is valid for dst == this.
|
|
|
|
*/
|
2012-12-17 02:10:42 +00:00
|
|
|
void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
|
|
|
|
this->inset(-dx, -dy, dst);
|
|
|
|
}
|
|
|
|
void outset(SkScalar dx, SkScalar dy) {
|
|
|
|
this->inset(-dx, -dy, this);
|
|
|
|
}
|
|
|
|
|
2013-04-25 12:23:00 +00:00
|
|
|
/**
|
|
|
|
* Returns true if 'rect' is wholy inside the RR, and both
|
|
|
|
* are not empty.
|
|
|
|
*/
|
|
|
|
bool contains(const SkRect& rect) const;
|
|
|
|
|
2012-11-29 13:24:55 +00:00
|
|
|
SkDEBUGCODE(void validate() const;)
|
|
|
|
|
2012-12-12 20:48:18 +00:00
|
|
|
enum {
|
|
|
|
kSizeInMemory = 12 * sizeof(SkScalar)
|
|
|
|
};
|
2012-12-13 02:01:33 +00:00
|
|
|
|
2012-12-12 20:48:18 +00:00
|
|
|
/**
|
|
|
|
* Write the rrect into the specified buffer. This is guaranteed to always
|
|
|
|
* write kSizeInMemory bytes, and that value is guaranteed to always be
|
|
|
|
* a multiple of 4. Return kSizeInMemory.
|
|
|
|
*/
|
|
|
|
uint32_t writeToMemory(void* buffer) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read the rrect from the specified buffer. This is guaranteed to always
|
|
|
|
* read kSizeInMemory bytes, and that value is guaranteed to always be
|
|
|
|
* a multiple of 4. Return kSizeInMemory.
|
|
|
|
*/
|
|
|
|
uint32_t readFromMemory(const void* buffer);
|
|
|
|
|
2012-11-29 13:24:55 +00:00
|
|
|
private:
|
|
|
|
SkRect fRect;
|
|
|
|
// Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
|
2012-11-30 02:01:24 +00:00
|
|
|
SkVector fRadii[4];
|
2012-11-29 13:24:55 +00:00
|
|
|
mutable Type fType;
|
|
|
|
// TODO: add padding so we can use memcpy for flattening and not copy
|
|
|
|
// uninitialized data
|
|
|
|
|
|
|
|
void computeType() const;
|
2013-04-25 12:23:00 +00:00
|
|
|
bool checkCornerContainment(SkScalar x, SkScalar y) const;
|
2012-12-12 20:48:18 +00:00
|
|
|
|
|
|
|
// to access fRadii directly
|
|
|
|
friend class SkPath;
|
2012-11-29 13:24:55 +00:00
|
|
|
};
|
|
|
|
|
2012-11-29 13:49:33 +00:00
|
|
|
#endif
|