blob: 0e863e2c6e9167368f98523ab8a11e25dc917c3a [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrShape_DEFINED
#define GrShape_DEFINED
#include "GrStyle.h"
#include "SkPath.h"
#include "SkRRect.h"
#include "SkTemplates.h"
#include "SkTLazy.h"
/**
* Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
* It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
* reflects the styling information (e.g. is stroked). It is also possible to apply just the
* path effect from the style. In this case the resulting shape will include any remaining
* stroking information that is to be applied after the path effect.
*
* Shapes can produce keys that represent only the geometry information, not the style. Note that
* when styling information is applied to produce a new shape then the style has been converted
* to geometric information and is included in the new shape's key. When the same style is applied
* to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
* will be the same.
*
* Currently this can only be constructed from a rrect, though it can become a path by applying
* style to the geometry. The idea is to expand this to cover most or all of the geometries that
* have SkCanvas::draw APIs.
*/
class GrShape {
public:
GrShape() : fType(Type::kEmpty) {}
explicit GrShape(const SkPath& path)
: fType(Type::kPath)
, fPath(&path) {
this->attemptToReduceFromPath();
}
explicit GrShape(const SkRRect& rrect)
: fType(Type::kRRect)
, fRRect(rrect) {
this->attemptToReduceFromRRect();
}
explicit GrShape(const SkRect& rect)
: fType(Type::kRRect)
, fRRect(SkRRect::MakeRect(rect)) {
this->attemptToReduceFromRRect();
}
GrShape(const SkPath& path, const GrStyle& style)
: fType(Type::kPath)
, fPath(&path)
, fStyle(style) {
this->attemptToReduceFromPath();
}
GrShape(const SkRRect& rrect, const GrStyle& style)
: fType(Type::kRRect)
, fRRect(rrect)
, fStyle(style) {
this->attemptToReduceFromRRect();
}
GrShape(const SkRect& rect, const GrStyle& style)
: fType(Type::kRRect)
, fRRect(SkRRect::MakeRect(rect))
, fStyle(style) {
this->attemptToReduceFromRRect();
}
GrShape(const SkPath& path, const SkPaint& paint)
: fType(Type::kPath)
, fPath(&path)
, fStyle(paint) {
this->attemptToReduceFromPath();
}
GrShape(const SkRRect& rrect, const SkPaint& paint)
: fType(Type::kRRect)
, fRRect(rrect)
, fStyle(paint) {
this->attemptToReduceFromRRect();
}
GrShape(const SkRect& rect, const SkPaint& paint)
: fType(Type::kRRect)
, fRRect(SkRRect::MakeRect(rect))
, fStyle(paint) {
this->attemptToReduceFromRRect();
}
GrShape(const GrShape&);
GrShape& operator=(const GrShape& that);
~GrShape() {
if (Type::kPath == fType) {
fPath.reset();
}
}
const GrStyle& style() const { return fStyle; }
/**
* Returns a GrShape where the shape's geometry fully reflects the original shape's GrStyle.
* The GrStyle of the returned shape will either be fill or hairline.
*/
GrShape applyFullStyle() { return GrShape(*this, false); }
/**
* Similar to above but applies only the path effect. Path effects take the original geometry
* and fill/stroking information and compute a new geometry and residual fill/stroking
* information to be applied. The path effect's output geometry and stroking will be captured
* in the returned GrShape.
*/
GrShape applyPathEffect() { return GrShape(*this, true); }
bool asRRect(SkRRect* rrect) const {
if (Type::kRRect != fType) {
return false;
}
if (rrect) {
*rrect = fRRect;
}
return true;
}
void asPath(SkPath* out) const {
switch (fType) {
case Type::kRRect:
out->reset();
out->addRRect(fRRect);
break;
case Type::kPath:
*out = *fPath.get();
break;
case Type::kEmpty:
out->reset();
break;
}
}
/**
* Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
* A negative value is returned if the shape has no key (shouldn't be cached).
*/
int unstyledKeySize() const;
/**
* Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
* space allocated for the key and that unstyledKeySize() does not return a negative value
* for this shape.
*/
void writeUnstyledKey(uint32_t* key) const;
private:
enum class Type {
kEmpty,
kRRect,
kPath,
};
/**
* Computes the key length for a GrStyle. The return will be negative if it cannot be turned
* into a key.
*/
static int StyleKeySize(const GrStyle& , bool stopAfterPE);
/**
* Writes a unique key for the style into the provided buffer. This function assumes the buffer
* has room for at least StyleKeySize() values. It assumes that StyleKeySize() returns a
* positive value for the style and stopAfterPE param. This is written so that the key for just
* dash application followed by the key for the remaining SkStrokeRec is the same as the
* key for applying dashing and SkStrokeRec all at once.
*/
static void StyleKey(uint32_t*, const GrStyle&, bool stopAfterPE);
/** Constructor used by Apply* functions */
GrShape(const GrShape& parentShape, bool stopAfterPE);
/**
* Determines the key we should inherit from the input shape's geometry and style when
* we are applying the style to create a new shape.
*/
void setInheritedKey(const GrShape& parentShape, bool stopAfterPE);
void attemptToReduceFromPath() {
SkASSERT(Type::kPath == fType);
fType = AttemptToReduceFromPathImpl(*fPath.get(), &fRRect, fStyle.pathEffect(),
fStyle.strokeRec());
if (Type::kPath != fType) {
fPath.reset();
fInheritedKey.reset(0);
}
}
void attemptToReduceFromRRect() {
SkASSERT(Type::kRRect == fType);
SkASSERT(!fInheritedKey.count());
if (fRRect.isEmpty()) {
fType = Type::kEmpty;
}
}
static Type AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect,
const SkPathEffect* pe, const SkStrokeRec& strokeRec) {
if (path.isEmpty()) {
return Type::kEmpty;
}
if (path.isRRect(rrect)) {
SkASSERT(!rrect->isEmpty());
return Type::kRRect;
}
SkRect rect;
if (path.isOval(&rect)) {
rrect->setOval(rect);
return Type::kRRect;
}
bool closed;
if (path.isRect(&rect, &closed, nullptr)) {
if (closed || (!pe && strokeRec.isFillStyle())) {
rrect->setRect(rect);
return Type::kRRect;
}
}
return Type::kPath;
}
Type fType;
SkRRect fRRect;
SkTLazy<SkPath> fPath;
GrStyle fStyle;
SkAutoSTArray<8, uint32_t> fInheritedKey;
};
#endif