/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStrokeInfo_DEFINED
#define GrStrokeInfo_DEFINED
#include "SkPathEffect.h"
#include "SkStrokeRec.h"
#include "SkTemplates.h"
class GrUniqueKey;
/*
* GrStrokeInfo encapsulates all the pertinent infomation regarding the stroke. The SkStrokeRec
* which holds information on fill style, width, miter, cap, and join. It also holds information
* about the dash like intervals, count, and phase.
*/
class GrStrokeInfo : public SkStrokeRec {
public:
static const GrStrokeInfo& FillInfo() {
static const GrStrokeInfo gFill(kFill_InitStyle);
return gFill;
}
GrStrokeInfo(SkStrokeRec::InitStyle style)
: INHERITED(style)
, fDashType(SkPathEffect::kNone_DashType) {
}
GrStrokeInfo(const GrStrokeInfo& src, bool includeDash = true)
: INHERITED(src) {
if (includeDash && src.isDashed()) {
fDashType = src.fDashType;
fDashPhase = src.fDashPhase;
fIntervals.reset(src.getDashCount());
memcpy(fIntervals.get(), src.fIntervals.get(), fIntervals.count() * sizeof(SkScalar));
} else {
fDashType = SkPathEffect::kNone_DashType;
}
}
GrStrokeInfo(const SkPaint& paint, SkPaint::Style styleOverride)
: INHERITED(paint, styleOverride)
, fDashType(SkPathEffect::kNone_DashType) {
this->init(paint);
}
explicit GrStrokeInfo(const SkPaint& paint)
: INHERITED(paint)
, fDashType(SkPathEffect::kNone_DashType) {
this->init(paint);
}
GrStrokeInfo& operator=(const GrStrokeInfo& other) {
if (other.isDashed()) {
fDashType = other.fDashType;
fDashPhase = other.fDashPhase;
fIntervals.reset(other.getDashCount());
memcpy(fIntervals.get(), other.fIntervals.get(), fIntervals.count() * sizeof(SkScalar));
} else {
this->removeDash();
}
this->INHERITED::operator=(other);
return *this;
}
bool hasEqualEffect(const GrStrokeInfo& other) const {
if (this->isDashed() != other.isDashed()) {
return false;
}
if (this->isDashed()) {
if (fDashPhase != other.fDashPhase ||
fIntervals.count() != other.fIntervals.count() ||
memcmp(fIntervals.get(), other.fIntervals.get(),
fIntervals.count() * sizeof(SkScalar)) != 0) {
return false;
}
}
return this->INHERITED::hasEqualEffect(other);
}
/*
* This functions takes in a patheffect and updates the dashing information if the path effect
* is a Dash type. Returns true if the path effect is a dashed effect and we are stroking,
* otherwise it returns false.
*/
bool setDashInfo(const SkPathEffect* pe) {
if (pe && !this->isFillStyle()) {
SkPathEffect::DashInfo dashInfo;
fDashType = pe->asADash(&dashInfo);
if (SkPathEffect::kDash_DashType == fDashType) {
fIntervals.reset(dashInfo.fCount);
dashInfo.fIntervals = fIntervals.get();
pe->asADash(&dashInfo);
fDashPhase = dashInfo.fPhase;
return true;
}
}
return false;
}
/*
* Like the above, but sets with an explicit SkPathEffect::DashInfo
*/
bool setDashInfo(const SkPathEffect::DashInfo& info) {
if (!this->isFillStyle()) {
fDashType = SkPathEffect::kDash_DashType;
fDashPhase = info.fPhase;
fIntervals.reset(info.fCount);
for (int i = 0; i < fIntervals.count(); i++) {
fIntervals[i] = info.fIntervals[i];
}
return true;
}
return false;
}
bool isDashed() const {
return (!this->isFillStyle() && SkPathEffect::kDash_DashType == fDashType);
}
int32_t getDashCount() const {
SkASSERT(this->isDashed());
return fIntervals.count();
}
SkScalar getDashPhase() const {
SkASSERT(this->isDashed());
return fDashPhase;
}
const SkScalar* getDashIntervals() const {
SkASSERT(this->isDashed());
return fIntervals.get();
}
void removeDash() {
fDashType = SkPathEffect::kNone_DashType;
}
/** Applies the dash to a path, if the stroke info has dashing.
* @return true if the dashing was applied (dst and dstStrokeInfo will be modified).
* false if the stroke info did not have dashing. The dst and dstStrokeInfo
* will be unmodified. The stroking in the SkStrokeRec might still
* be applicable.
*/
bool applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo, const SkPath& src) const;
/**
* Computes the length of the data that will be written by asUniqueKeyFragment() function.
*/
int computeUniqueKeyFragmentData32Cnt() const {
const int kSkScalarData32Cnt = sizeof(SkScalar) / sizeof(uint32_t);
// SkStrokeRec data: 32 bits for style+join+cap and 2 scalars for miter and width.
int strokeKeyData32Cnt = 1 + 2 * kSkScalarData32Cnt;
if (this->isDashed()) {
// One scalar for dash phase and one for each dash value.
strokeKeyData32Cnt += (1 + this->getDashCount()) * kSkScalarData32Cnt;
}
return strokeKeyData32Cnt;
}
/**
* Writes the object contents as uint32_t data, to be used with GrUniqueKey.
* Note: the data written does not encode the length, so care must be taken to ensure
* that the full unique key data is encoded properly. For example, GrStrokeInfo
* fragment can be placed last in the sequence, at fixed index.
*/
void asUniqueKeyFragment(uint32_t*) const;
private:
// Prevent accidental usage, should use GrStrokeInfo::hasEqualEffect.
bool hasEqualEffect(const SkStrokeRec& other) const;
void init(const SkPaint& paint) {
const SkPathEffect* pe = paint.getPathEffect();
this->setDashInfo(pe);
}
SkPathEffect::DashType fDashType;
SkScalar fDashPhase;
SkAutoSTArray<2, SkScalar> fIntervals;
typedef SkStrokeRec INHERITED;
};
#endif