Improve caching of dashed paths in GrStencilAndCoverPathRenderer
Improve caching of dashed paths in GrStencilAndCoverPathRenderer. Look up the (NVPR specific) GrGLPath based on GrStrokeInfo and the original path. Use unique keys for all GrPaths. Dash the path with Skia dash stroker and use that path geometry for NVPR path. NVPR internal dashing stroke is not used, because the dashing implementation of NVPR does not match Skia implementation. Review URL: https://codereview.chromium.org/1116123003
This commit is contained in:
parent
54b8511189
commit
50b58e6fbc
@ -7,49 +7,14 @@
|
||||
|
||||
#include "GrPath.h"
|
||||
|
||||
template<int NumBits> static uint64_t get_top_n_float_bits(float f) {
|
||||
char* floatData = reinterpret_cast<char*>(&f);
|
||||
uint32_t floatBits = *reinterpret_cast<uint32_t*>(floatData);
|
||||
return floatBits >> (32 - NumBits);
|
||||
void GrPath::ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key) {
|
||||
static const GrUniqueKey::Domain kPathDomain = GrUniqueKey::GenerateDomain();
|
||||
int strokeDataCnt = stroke.computeUniqueKeyFragmentData32Cnt();
|
||||
GrUniqueKey::Builder builder(key, kPathDomain, 2 + strokeDataCnt);
|
||||
builder[0] = path.getGenerationID();
|
||||
builder[1] = path.getFillType();
|
||||
if (strokeDataCnt > 0) {
|
||||
stroke.asUniqueKeyFragment(&builder[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key) {
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(key, kDomain, 3);
|
||||
*reinterpret_cast<uint64_t*>(&builder[0]) = ComputeStrokeKey(stroke);
|
||||
builder[2] = path.getGenerationID();
|
||||
}
|
||||
|
||||
uint64_t GrPath::ComputeStrokeKey(const SkStrokeRec& stroke) {
|
||||
enum {
|
||||
kStyleBits = 2,
|
||||
kJoinBits = 2,
|
||||
kCapBits = 2,
|
||||
kWidthBits = 29,
|
||||
kMiterBits = 29,
|
||||
|
||||
kJoinShift = kStyleBits,
|
||||
kCapShift = kJoinShift + kJoinBits,
|
||||
kWidthShift = kCapShift + kCapBits,
|
||||
kMiterShift = kWidthShift + kWidthBits,
|
||||
|
||||
kBitCount = kMiterShift + kMiterBits
|
||||
};
|
||||
|
||||
SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong);
|
||||
SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong);
|
||||
SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), miter_shift_will_be_wrong);
|
||||
SK_COMPILE_ASSERT(kBitCount == 64, wrong_stroke_key_size);
|
||||
|
||||
if (!stroke.needToApply()) {
|
||||
return SkStrokeRec::kFill_Style;
|
||||
}
|
||||
|
||||
uint64_t key = stroke.getStyle();
|
||||
key |= stroke.getJoin() << kJoinShift;
|
||||
key |= stroke.getCap() << kCapShift;
|
||||
key |= get_top_n_float_bits<kWidthBits>(stroke.getWidth()) << kWidthShift;
|
||||
key |= get_top_n_float_bits<kMiterBits>(stroke.getMiter()) << kMiterShift;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
@ -9,9 +9,9 @@
|
||||
#define GrPath_DEFINED
|
||||
|
||||
#include "GrGpuResource.h"
|
||||
#include "GrStrokeInfo.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkStrokeRec.h"
|
||||
|
||||
class GrPath : public GrGpuResource {
|
||||
public:
|
||||
@ -20,28 +20,32 @@ public:
|
||||
/**
|
||||
* Initialize to a path with a fixed stroke. Stroke must not be hairline.
|
||||
*/
|
||||
GrPath(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke)
|
||||
: INHERITED(gpu, kCached_LifeCycle),
|
||||
fSkPath(skPath),
|
||||
fStroke(stroke),
|
||||
fBounds(skPath.getBounds()) {
|
||||
GrPath(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke)
|
||||
: INHERITED(gpu, kCached_LifeCycle)
|
||||
, fBounds(skPath.getBounds())
|
||||
#ifdef SK_DEBUG
|
||||
, fSkPath(skPath)
|
||||
, fStroke(stroke)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
static void ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key);
|
||||
static uint64_t ComputeStrokeKey(const SkStrokeRec&);
|
||||
|
||||
bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
|
||||
return fSkPath == path && fStroke.hasEqualEffect(stroke);
|
||||
}
|
||||
static void ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key);
|
||||
|
||||
const SkRect& getBounds() const { return fBounds; }
|
||||
|
||||
const SkStrokeRec& getStroke() const { return fStroke; }
|
||||
#ifdef SK_DEBUG
|
||||
bool isEqualTo(const SkPath& path, const GrStrokeInfo& stroke) {
|
||||
return fSkPath == path && fStroke.hasEqualEffect(stroke);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
SkPath fSkPath;
|
||||
SkStrokeRec fStroke;
|
||||
SkRect fBounds;
|
||||
#ifdef SK_DEBUG
|
||||
SkPath fSkPath;
|
||||
GrStrokeInfo fStroke;
|
||||
#endif
|
||||
|
||||
private:
|
||||
typedef GrGpuResource INHERITED;
|
||||
|
@ -13,23 +13,19 @@ enum {
|
||||
};
|
||||
|
||||
GrPathRange::GrPathRange(GrGpu* gpu,
|
||||
PathGenerator* pathGenerator,
|
||||
const SkStrokeRec& stroke)
|
||||
PathGenerator* pathGenerator)
|
||||
: INHERITED(gpu, kCached_LifeCycle),
|
||||
fPathGenerator(SkRef(pathGenerator)),
|
||||
fNumPaths(fPathGenerator->getNumPaths()),
|
||||
fStroke(stroke) {
|
||||
fNumPaths(fPathGenerator->getNumPaths()) {
|
||||
const int numGroups = (fNumPaths + kPathsPerGroup - 1) / kPathsPerGroup;
|
||||
fGeneratedPaths.reset((numGroups + 7) / 8); // 1 bit per path group.
|
||||
memset(&fGeneratedPaths.front(), 0, fGeneratedPaths.count());
|
||||
}
|
||||
|
||||
GrPathRange::GrPathRange(GrGpu* gpu,
|
||||
int numPaths,
|
||||
const SkStrokeRec& stroke)
|
||||
int numPaths)
|
||||
: INHERITED(gpu, kCached_LifeCycle),
|
||||
fNumPaths(numPaths),
|
||||
fStroke(stroke) {
|
||||
fNumPaths(numPaths) {
|
||||
}
|
||||
|
||||
void GrPathRange::willDrawPaths(const void* indices, PathIndexType indexType, int count) const {
|
||||
|
@ -10,14 +10,13 @@
|
||||
|
||||
#include "GrGpuResource.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkStrokeRec.h"
|
||||
#include "SkTArray.h"
|
||||
|
||||
class SkPath;
|
||||
class SkDescriptor;
|
||||
|
||||
/**
|
||||
* Represents a contiguous range of GPU path objects, all with a common stroke.
|
||||
* Represents a contiguous range of GPU path objects.
|
||||
* This object is immutable with the exception that individual paths may be
|
||||
* initialized lazily.
|
||||
*/
|
||||
@ -50,7 +49,9 @@ public:
|
||||
public:
|
||||
virtual int getNumPaths() = 0;
|
||||
virtual void generatePath(int index, SkPath* out) = 0;
|
||||
#ifdef SK_DEBUG
|
||||
virtual bool isEqualTo(const SkDescriptor&) const { return false; }
|
||||
#endif
|
||||
virtual ~PathGenerator() {}
|
||||
};
|
||||
|
||||
@ -58,22 +59,22 @@ public:
|
||||
* Initialize a lazy-loaded path range. This class will generate an SkPath and call
|
||||
* onInitPath() for each path within the range before it is drawn for the first time.
|
||||
*/
|
||||
GrPathRange(GrGpu*, PathGenerator*, const SkStrokeRec& stroke);
|
||||
GrPathRange(GrGpu*, PathGenerator*);
|
||||
|
||||
/**
|
||||
* Initialize an eager-loaded path range. The subclass is responsible for ensuring all
|
||||
* the paths are initialized up front.
|
||||
*/
|
||||
GrPathRange(GrGpu*, int numPaths, const SkStrokeRec& stroke);
|
||||
GrPathRange(GrGpu*, int numPaths);
|
||||
|
||||
int getNumPaths() const { return fNumPaths; }
|
||||
const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
virtual bool isEqualTo(const SkDescriptor& desc) const {
|
||||
return NULL != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
|
||||
}
|
||||
|
||||
int getNumPaths() const { return fNumPaths; }
|
||||
const SkStrokeRec& getStroke() const { return fStroke; }
|
||||
const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }
|
||||
|
||||
#endif
|
||||
protected:
|
||||
// Initialize a path in the range before drawing. This is only called when
|
||||
// fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(),
|
||||
@ -89,7 +90,6 @@ private:
|
||||
mutable SkAutoTUnref<PathGenerator> fPathGenerator;
|
||||
mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths;
|
||||
const int fNumPaths;
|
||||
const SkStrokeRec fStroke;
|
||||
|
||||
typedef GrGpuResource INHERITED;
|
||||
};
|
||||
|
@ -15,13 +15,18 @@
|
||||
class GlyphGenerator : public GrPathRange::PathGenerator {
|
||||
public:
|
||||
GlyphGenerator(const SkTypeface& typeface, const SkDescriptor& desc)
|
||||
: fDesc(desc.copy()),
|
||||
fScalerContext(typeface.createScalerContext(fDesc)) {
|
||||
: fScalerContext(typeface.createScalerContext(&desc))
|
||||
#ifdef SK_DEBUG
|
||||
, fDesc(desc.copy())
|
||||
#endif
|
||||
{
|
||||
fFlipMatrix.setScale(1, -1);
|
||||
}
|
||||
|
||||
virtual ~GlyphGenerator() {
|
||||
#ifdef SK_DEBUG
|
||||
SkDescriptor::Free(fDesc);
|
||||
#endif
|
||||
}
|
||||
|
||||
int getNumPaths() override {
|
||||
@ -36,20 +41,22 @@ public:
|
||||
fScalerContext->getPath(skGlyph, out);
|
||||
out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction.
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
bool isEqualTo(const SkDescriptor& desc) const override {
|
||||
return fDesc->equals(desc);
|
||||
}
|
||||
|
||||
#endif
|
||||
private:
|
||||
SkDescriptor* const fDesc;
|
||||
const SkAutoTDelete<SkScalerContext> fScalerContext;
|
||||
SkMatrix fFlipMatrix;
|
||||
#ifdef SK_DEBUG
|
||||
SkDescriptor* const fDesc;
|
||||
#endif
|
||||
};
|
||||
|
||||
GrPathRange* GrPathRendering::createGlyphs(const SkTypeface* typeface,
|
||||
const SkDescriptor* desc,
|
||||
const SkStrokeRec& stroke) {
|
||||
const GrStrokeInfo& stroke) {
|
||||
if (NULL == typeface) {
|
||||
typeface = SkTypeface::GetDefaultTypeface();
|
||||
SkASSERT(NULL != typeface);
|
||||
|
@ -11,12 +11,12 @@
|
||||
#include "SkPath.h"
|
||||
#include "GrPathRange.h"
|
||||
|
||||
class SkStrokeRec;
|
||||
class SkDescriptor;
|
||||
class SkTypeface;
|
||||
class GrPath;
|
||||
class GrGpu;
|
||||
class GrStencilSettings;
|
||||
class GrStrokeInfo;
|
||||
|
||||
/**
|
||||
* Abstract class wrapping HW path rendering API.
|
||||
@ -84,17 +84,17 @@ public:
|
||||
* @param stroke the path stroke.
|
||||
* @return a new path.
|
||||
*/
|
||||
virtual GrPath* createPath(const SkPath&, const SkStrokeRec&) = 0;
|
||||
virtual GrPath* createPath(const SkPath&, const GrStrokeInfo&) = 0;
|
||||
|
||||
/**
|
||||
* Creates a range of gpu paths with a common stroke. The caller owns a ref on the
|
||||
* returned path range which must be balanced by a call to unref.
|
||||
*
|
||||
* @param PathGenerator class that generates SkPath objects for each path in the range.
|
||||
* @param SkStrokeRec the common stroke applied to each path in the range.
|
||||
* @param GrStrokeInfo the common stroke applied to each path in the range.
|
||||
* @return a new path range.
|
||||
*/
|
||||
virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, const SkStrokeRec&) = 0;
|
||||
virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, const GrStrokeInfo&) = 0;
|
||||
|
||||
/**
|
||||
* Creates a range of glyph paths, indexed by glyph id. The glyphs will have an
|
||||
@ -117,14 +117,15 @@ public:
|
||||
* including with the stroke information baked directly into
|
||||
* the outlines.
|
||||
*
|
||||
* @param SkStrokeRec Common stroke that the GPU will apply to every path. Note that
|
||||
* @param GrStrokeInfo Common stroke that the GPU will apply to every path. Note that
|
||||
* if the glyph outlines contain baked-in strokes from the font
|
||||
* descriptor, the GPU stroke will be applied on top of those
|
||||
* outlines.
|
||||
*
|
||||
* @return a new path range populated with glyphs.
|
||||
*/
|
||||
virtual GrPathRange* createGlyphs(const SkTypeface*, const SkDescriptor*, const SkStrokeRec&) = 0;
|
||||
virtual GrPathRange* createGlyphs(const SkTypeface*, const SkDescriptor*,
|
||||
const GrStrokeInfo&) = 0;
|
||||
|
||||
virtual void stencilPath(const GrPath*, const GrStencilSettings&) = 0;
|
||||
virtual void drawPath(const GrPath*, const GrStencilSettings&) = 0;
|
||||
|
@ -60,7 +60,6 @@ bool GrStencilAndCoverPathRenderer::canDrawPath(const GrDrawTarget* target,
|
||||
const GrStrokeInfo& stroke,
|
||||
bool antiAlias) const {
|
||||
return !stroke.isHairlineStyle() &&
|
||||
!stroke.isDashed() &&
|
||||
!antiAlias && // doesn't do per-path AA, relies on the target having MSAA
|
||||
pipelineBuilder->getStencil().isDisabled();
|
||||
}
|
||||
@ -73,15 +72,17 @@ GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
|
||||
return GrPathRenderer::kStencilOnly_StencilSupport;
|
||||
}
|
||||
|
||||
static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke) {
|
||||
static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke) {
|
||||
GrContext* ctx = gpu->getContext();
|
||||
GrUniqueKey key;
|
||||
GrPath::ComputeKey(skPath, stroke, &key);
|
||||
SkAutoTUnref<GrPath> path(
|
||||
static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
|
||||
if (NULL == path || !path->isEqualTo(skPath, stroke)) {
|
||||
if (NULL == path) {
|
||||
path.reset(gpu->pathRendering()->createPath(skPath, stroke));
|
||||
ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
|
||||
} else {
|
||||
SkASSERT(path->isEqualTo(skPath, stroke));
|
||||
}
|
||||
return path.detach();
|
||||
}
|
||||
@ -106,7 +107,6 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(GrDrawTarget* target,
|
||||
bool antiAlias) {
|
||||
SkASSERT(!antiAlias);
|
||||
SkASSERT(!stroke.isHairlineStyle());
|
||||
SkASSERT(!stroke.isDashed());
|
||||
SkASSERT(pipelineBuilder->getStencil().isDisabled());
|
||||
|
||||
SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke));
|
||||
|
@ -55,9 +55,11 @@ bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
|
||||
if (skPaint.getMaskFilter()) {
|
||||
return false;
|
||||
}
|
||||
if (skPaint.getPathEffect()) {
|
||||
if (SkPathEffect* pe = skPaint.getPathEffect()) {
|
||||
if (pe->asADash(NULL) != SkPathEffect::kDash_DashType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// No hairlines unless we can map the 1 px width to the object space.
|
||||
if (skPaint.getStyle() == SkPaint::kStroke_Style
|
||||
@ -220,26 +222,27 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
|
||||
static GrPathRange* get_gr_glyphs(GrContext* ctx,
|
||||
const SkTypeface* typeface,
|
||||
const SkDescriptor* desc,
|
||||
const SkStrokeRec& stroke) {
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey key;
|
||||
GrUniqueKey::Builder builder(&key, kDomain, 4);
|
||||
struct GlyphKey {
|
||||
uint32_t fChecksum;
|
||||
uint32_t fTypeface;
|
||||
uint64_t fStroke;
|
||||
};
|
||||
GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
|
||||
glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
|
||||
glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
|
||||
glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
|
||||
const GrStrokeInfo& stroke) {
|
||||
|
||||
static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
|
||||
int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
|
||||
GrUniqueKey glyphKey;
|
||||
GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
|
||||
reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
|
||||
reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
|
||||
if (strokeDataCount > 0) {
|
||||
stroke.asUniqueKeyFragment(&builder[2]);
|
||||
}
|
||||
builder.finish();
|
||||
|
||||
SkAutoTUnref<GrPathRange> glyphs(
|
||||
static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
|
||||
if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
|
||||
static_cast<GrPathRange*>(
|
||||
ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
|
||||
if (NULL == glyphs) {
|
||||
glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
|
||||
ctx->resourceProvider()->assignUniqueKeyToResource(key, glyphs);
|
||||
ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
|
||||
} else {
|
||||
SkASSERT(NULL == desc || glyphs->isEqualTo(*desc));
|
||||
}
|
||||
|
||||
return glyphs.detach();
|
||||
@ -273,7 +276,7 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
|
||||
|
||||
// The whole shape (including stroke) will be baked into the glyph outlines. Make
|
||||
// NVPR just fill the baked shapes.
|
||||
fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
|
||||
fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
|
||||
|
||||
fTextRatio = fTextInverseRatio = 1.0f;
|
||||
|
||||
@ -298,7 +301,7 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
|
||||
} else {
|
||||
// Don't bake strokes into the glyph outlines. We will stroke the glyphs
|
||||
// using the GPU instead. This is the fast path.
|
||||
fStroke = SkStrokeRec(fSkPaint);
|
||||
fStroke = GrStrokeInfo(fSkPaint);
|
||||
fSkPaint.setStyle(SkPaint::kFill_Style);
|
||||
|
||||
if (fStroke.isHairlineStyle()) {
|
||||
@ -327,8 +330,8 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
|
||||
}
|
||||
|
||||
bool canUseRawPaths;
|
||||
|
||||
if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
|
||||
if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
|
||||
kMaxPerformance_RenderMode == renderMode)) {
|
||||
// We can draw the glyphs from canonically sized paths.
|
||||
fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
|
||||
fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "GrTextContext.h"
|
||||
#include "GrDrawTarget.h"
|
||||
#include "SkStrokeRec.h"
|
||||
#include "GrStrokeInfo.h"
|
||||
|
||||
class GrTextStrike;
|
||||
class GrPath;
|
||||
@ -58,7 +58,7 @@ private:
|
||||
float fTextInverseRatio;
|
||||
SkGlyphCache* fGlyphCache;
|
||||
GrPathRange* fGlyphs;
|
||||
SkStrokeRec fStroke;
|
||||
GrStrokeInfo fStroke;
|
||||
uint16_t fGlyphIndices[kGlyphBufferSize];
|
||||
SkPoint fGlyphPositions[kGlyphBufferSize];
|
||||
int fQueuedGlyphCount;
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "GrStrokeInfo.h"
|
||||
|
||||
#include "GrResourceKey.h"
|
||||
#include "SkDashPathPriv.h"
|
||||
|
||||
bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
|
||||
@ -24,3 +24,55 @@ bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GrStrokeInfo::asUniqueKeyFragment(uint32_t* data) const {
|
||||
const int kSkScalarData32Cnt = sizeof(SkScalar) / sizeof(uint32_t);
|
||||
enum {
|
||||
kStyleBits = 2,
|
||||
kJoinBits = 2,
|
||||
kCapBits = 32 - kStyleBits - kJoinBits,
|
||||
|
||||
kJoinShift = kStyleBits,
|
||||
kCapShift = kJoinShift + kJoinBits,
|
||||
};
|
||||
|
||||
SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong);
|
||||
SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong);
|
||||
SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), cap_does_not_fit);
|
||||
uint32_t styleKey = this->getStyle();
|
||||
if (this->needToApply()) {
|
||||
styleKey |= this->getJoin() << kJoinShift;
|
||||
styleKey |= this->getCap() << kCapShift;
|
||||
}
|
||||
int i = 0;
|
||||
data[i++] = styleKey;
|
||||
|
||||
// Memcpy the scalar fields. Does not "reinterpret_cast<SkScalar&>(data[i]) = ..." due to
|
||||
// scalars having more strict alignment requirements than what data can guarantee. The
|
||||
// compiler should optimize memcpys to assignments.
|
||||
SkScalar scalar;
|
||||
scalar = this->getMiter();
|
||||
memcpy(&data[i], &scalar, sizeof(scalar));
|
||||
i += kSkScalarData32Cnt;
|
||||
|
||||
scalar = this->getWidth();
|
||||
memcpy(&data[i], &scalar, sizeof(scalar));
|
||||
i += kSkScalarData32Cnt;
|
||||
|
||||
if (this->isDashed()) {
|
||||
SkScalar phase = this->getDashPhase();
|
||||
memcpy(&data[i], &phase, sizeof(phase));
|
||||
i += kSkScalarData32Cnt;
|
||||
|
||||
int32_t count = this->getDashCount() & static_cast<int32_t>(~1);
|
||||
SkASSERT(count == this->getDashCount());
|
||||
const SkScalar* intervals = this->getDashIntervals();
|
||||
int intervalByteCnt = count * sizeof(SkScalar);
|
||||
memcpy(&data[i], intervals, intervalByteCnt);
|
||||
// Enable the line below if fields are added after dashing.
|
||||
SkDEBUGCODE(i += kSkScalarData32Cnt * count);
|
||||
}
|
||||
|
||||
SkASSERT(this->computeUniqueKeyFragmentData32Cnt() == i);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "SkStrokeRec.h"
|
||||
#include "SkPathEffect.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
|
||||
@ -60,6 +62,21 @@ public:
|
||||
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,
|
||||
@ -127,8 +144,31 @@ public:
|
||||
*/
|
||||
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, not implemented for GrStrokeInfos.
|
||||
// Prevent accidental usage, should use GrStrokeInfo::hasEqualEffect.
|
||||
bool hasEqualEffect(const SkStrokeRec& other) const;
|
||||
|
||||
void init(const SkPaint& paint) {
|
||||
|
@ -91,7 +91,8 @@ inline void points_to_coords(const SkPoint points[], size_t first_point, size_t
|
||||
void GrGLPath::InitPathObject(GrGLGpu* gpu,
|
||||
GrGLuint pathID,
|
||||
const SkPath& skPath,
|
||||
const SkStrokeRec& stroke) {
|
||||
const GrStrokeInfo& stroke) {
|
||||
SkASSERT(!stroke.isDashed());
|
||||
if (!skPath.isEmpty()) {
|
||||
int verbCnt = skPath.countVerbs();
|
||||
int pointCnt = skPath.countPoints();
|
||||
@ -182,16 +183,33 @@ void GrGLPath::InitPathObject(GrGLGpu* gpu,
|
||||
}
|
||||
}
|
||||
|
||||
GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& path, const SkStrokeRec& stroke)
|
||||
: INHERITED(gpu, path, stroke),
|
||||
GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke)
|
||||
: INHERITED(gpu, origSkPath, origStroke),
|
||||
fPathID(gpu->glPathRendering()->genPaths(1)) {
|
||||
// Convert a dashing to either a stroke or a fill.
|
||||
const SkPath* skPath = &origSkPath;
|
||||
SkTLazy<SkPath> tmpPath;
|
||||
const GrStrokeInfo* stroke = &origStroke;
|
||||
GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);
|
||||
|
||||
InitPathObject(gpu, fPathID, fSkPath, stroke);
|
||||
|
||||
if (stroke.needToApply()) {
|
||||
// FIXME: try to account for stroking, without rasterizing the stroke.
|
||||
fBounds.outset(stroke.getWidth(), stroke.getWidth());
|
||||
if (stroke->isDashed()) {
|
||||
if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
|
||||
skPath = tmpPath.get();
|
||||
stroke = &tmpStroke;
|
||||
}
|
||||
}
|
||||
|
||||
InitPathObject(gpu, fPathID, *skPath, *stroke);
|
||||
|
||||
fShouldStroke = stroke->needToApply();
|
||||
fShouldFill = stroke->isFillStyle() ||
|
||||
stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style;
|
||||
|
||||
if (fShouldStroke) {
|
||||
// FIXME: try to account for stroking, without rasterizing the stroke.
|
||||
fBounds.outset(stroke->getWidth(), stroke->getWidth());
|
||||
}
|
||||
|
||||
this->registerWithCache();
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,13 @@ public:
|
||||
static void InitPathObject(GrGLGpu*,
|
||||
GrGLuint pathID,
|
||||
const SkPath&,
|
||||
const SkStrokeRec&);
|
||||
const GrStrokeInfo&);
|
||||
|
||||
GrGLPath(GrGLGpu* gpu, const SkPath& path, const SkStrokeRec& stroke);
|
||||
GrGLPath(GrGLGpu* gpu, const SkPath& path, const GrStrokeInfo& stroke);
|
||||
GrGLuint pathID() const { return fPathID; }
|
||||
|
||||
bool shouldStroke() const { return fShouldStroke; }
|
||||
bool shouldFill() const { return fShouldFill; }
|
||||
protected:
|
||||
void onRelease() override;
|
||||
void onAbandon() override;
|
||||
@ -39,6 +41,8 @@ private:
|
||||
size_t onGpuMemorySize() const override { return 100; }
|
||||
|
||||
GrGLuint fPathID;
|
||||
bool fShouldStroke;
|
||||
bool fShouldFill;
|
||||
|
||||
typedef GrPath INHERITED;
|
||||
};
|
||||
|
@ -11,10 +11,12 @@
|
||||
#include "GrGLPathRendering.h"
|
||||
#include "GrGLGpu.h"
|
||||
|
||||
GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const SkStrokeRec& stroke)
|
||||
: INHERITED(gpu, pathGenerator, stroke),
|
||||
GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const GrStrokeInfo& stroke)
|
||||
: INHERITED(gpu, pathGenerator),
|
||||
fStroke(stroke),
|
||||
fBasePathID(gpu->glPathRendering()->genPaths(this->getNumPaths())),
|
||||
fGpuMemorySize(0) {
|
||||
this->init();
|
||||
this->registerWithCache();
|
||||
}
|
||||
|
||||
@ -22,14 +24,27 @@ GrGLPathRange::GrGLPathRange(GrGLGpu* gpu,
|
||||
GrGLuint basePathID,
|
||||
int numPaths,
|
||||
size_t gpuMemorySize,
|
||||
const SkStrokeRec& stroke)
|
||||
: INHERITED(gpu, numPaths, stroke),
|
||||
const GrStrokeInfo& stroke)
|
||||
: INHERITED(gpu, numPaths),
|
||||
fStroke(stroke),
|
||||
fBasePathID(basePathID),
|
||||
fGpuMemorySize(gpuMemorySize) {
|
||||
this->init();
|
||||
this->registerWithCache();
|
||||
}
|
||||
|
||||
void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
|
||||
void GrGLPathRange::init() {
|
||||
if (fStroke.isDashed()) {
|
||||
fShouldStroke = false;
|
||||
fShouldFill = true;
|
||||
} else {
|
||||
fShouldStroke = fStroke.needToApply();
|
||||
fShouldFill = fStroke.isFillStyle() ||
|
||||
fStroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style;
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRange::onInitPath(int index, const SkPath& origSkPath) const {
|
||||
GrGLGpu* gpu = static_cast<GrGLGpu*>(this->getGpu());
|
||||
if (NULL == gpu) {
|
||||
return;
|
||||
@ -41,7 +56,31 @@ void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
|
||||
GR_GL_CALL_RET(gpu->glInterface(), isPath, IsPath(fBasePathID + index)));
|
||||
SkASSERT(GR_GL_FALSE == isPath);
|
||||
|
||||
GrGLPath::InitPathObject(gpu, fBasePathID + index, skPath, this->getStroke());
|
||||
const SkPath* skPath = &origSkPath;
|
||||
SkTLazy<SkPath> tmpPath;
|
||||
const GrStrokeInfo* stroke = &fStroke;
|
||||
GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);
|
||||
|
||||
// Dashing must be applied to the path. However, if dashing is present,
|
||||
// we must convert all the paths to fills. The GrStrokeInfo::applyDash leaves
|
||||
// simple paths as strokes but converts other paths to fills.
|
||||
// Thus we must stroke the strokes here, so that all paths in the
|
||||
// path range are using the same style.
|
||||
if (fStroke.isDashed()) {
|
||||
if (!stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
|
||||
return;
|
||||
}
|
||||
skPath = tmpPath.get();
|
||||
stroke = &tmpStroke;
|
||||
if (tmpStroke.needToApply()) {
|
||||
if (!tmpStroke.applyToPath(tmpPath.get(), *tmpPath.get())) {
|
||||
return;
|
||||
}
|
||||
tmpStroke.setFillStyle();
|
||||
}
|
||||
}
|
||||
|
||||
GrGLPath::InitPathObject(gpu, fBasePathID + index, *skPath, *stroke);
|
||||
|
||||
// TODO: Use a better approximation for the individual path sizes.
|
||||
fGpuMemorySize += 100;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define GrGLPathRange_DEFINED
|
||||
|
||||
#include "../GrPathRange.h"
|
||||
#include "GrStrokeInfo.h"
|
||||
#include "gl/GrGLFunctions.h"
|
||||
|
||||
class GrGLGpu;
|
||||
@ -26,7 +27,7 @@ public:
|
||||
* Initialize a GL path range from a PathGenerator. This class will allocate
|
||||
* the GPU path objects and initialize them lazily.
|
||||
*/
|
||||
GrGLPathRange(GrGLGpu*, PathGenerator*, const SkStrokeRec&);
|
||||
GrGLPathRange(GrGLGpu*, PathGenerator*, const GrStrokeInfo&);
|
||||
|
||||
/**
|
||||
* Initialize a GL path range from an existing range of pre-initialized GPU
|
||||
@ -37,10 +38,13 @@ public:
|
||||
GrGLuint basePathID,
|
||||
int numPaths,
|
||||
size_t gpuMemorySize,
|
||||
const SkStrokeRec&);
|
||||
const GrStrokeInfo&);
|
||||
|
||||
GrGLuint basePathID() const { return fBasePathID; }
|
||||
|
||||
bool shouldStroke() const { return fShouldStroke; }
|
||||
bool shouldFill() const { return fShouldFill; }
|
||||
|
||||
protected:
|
||||
void onInitPath(int index, const SkPath&) const override;
|
||||
|
||||
@ -48,10 +52,14 @@ protected:
|
||||
void onAbandon() override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
size_t onGpuMemorySize() const override { return fGpuMemorySize; }
|
||||
|
||||
const GrStrokeInfo fStroke;
|
||||
GrGLuint fBasePathID;
|
||||
mutable size_t fGpuMemorySize;
|
||||
bool fShouldStroke;
|
||||
bool fShouldFill;
|
||||
|
||||
typedef GrPathRange INHERITED;
|
||||
};
|
||||
|
@ -91,19 +91,19 @@ void GrGLPathRendering::resetContext() {
|
||||
fHWPathStencilSettings.invalidate();
|
||||
}
|
||||
|
||||
GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const SkStrokeRec& stroke) {
|
||||
GrPath* GrGLPathRendering::createPath(const SkPath& inPath, const GrStrokeInfo& stroke) {
|
||||
return SkNEW_ARGS(GrGLPath, (fGpu, inPath, stroke));
|
||||
}
|
||||
|
||||
GrPathRange* GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator,
|
||||
const SkStrokeRec& stroke) {
|
||||
const GrStrokeInfo& stroke) {
|
||||
return SkNEW_ARGS(GrGLPathRange, (fGpu, pathGenerator, stroke));
|
||||
}
|
||||
|
||||
GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface,
|
||||
const SkDescriptor* desc,
|
||||
const SkStrokeRec& stroke) {
|
||||
if (NULL != desc || !caps().glyphLoadingSupport) {
|
||||
const GrStrokeInfo& stroke) {
|
||||
if (NULL != desc || !caps().glyphLoadingSupport || stroke.isDashed()) {
|
||||
return GrPathRendering::createGlyphs(typeface, desc, stroke);
|
||||
}
|
||||
|
||||
@ -152,44 +152,40 @@ GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface,
|
||||
}
|
||||
|
||||
void GrGLPathRendering::stencilPath(const GrPath* path, const GrStencilSettings& stencilSettings) {
|
||||
GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
|
||||
const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
|
||||
|
||||
this->flushPathStencilSettings(stencilSettings);
|
||||
SkASSERT(!fHWPathStencilSettings.isTwoSided());
|
||||
|
||||
const SkStrokeRec& stroke = path->getStroke();
|
||||
|
||||
GrGLenum fillMode =
|
||||
gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
|
||||
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
|
||||
|
||||
if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
|
||||
GL_CALL(StencilFillPath(id, fillMode, writeMask));
|
||||
if (glPath->shouldFill()) {
|
||||
GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
|
||||
}
|
||||
if (stroke.needToApply()) {
|
||||
GL_CALL(StencilStrokePath(id, 0xffff, writeMask));
|
||||
if (glPath->shouldStroke()) {
|
||||
GL_CALL(StencilStrokePath(glPath->pathID(), 0xffff, writeMask));
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLPathRendering::drawPath(const GrPath* path, const GrStencilSettings& stencilSettings) {
|
||||
GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
|
||||
const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
|
||||
|
||||
this->flushPathStencilSettings(stencilSettings);
|
||||
SkASSERT(!fHWPathStencilSettings.isTwoSided());
|
||||
|
||||
const SkStrokeRec& stroke = path->getStroke();
|
||||
|
||||
GrGLenum fillMode =
|
||||
gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
|
||||
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
|
||||
|
||||
if (stroke.needToApply()) {
|
||||
if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
|
||||
GL_CALL(StencilFillPath(id, fillMode, writeMask));
|
||||
if (glPath->shouldStroke()) {
|
||||
if (glPath->shouldFill()) {
|
||||
GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
|
||||
}
|
||||
this->stencilThenCoverStrokePath(id, 0xffff, writeMask, GR_GL_BOUNDING_BOX);
|
||||
this->stencilThenCoverStrokePath(glPath->pathID(), 0xffff, writeMask, GR_GL_BOUNDING_BOX);
|
||||
} else {
|
||||
this->stencilThenCoverFillPath(id, fillMode, writeMask, GR_GL_BOUNDING_BOX);
|
||||
this->stencilThenCoverFillPath(glPath->pathID(), fillMode, writeMask, GR_GL_BOUNDING_BOX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,32 +195,31 @@ void GrGLPathRendering::drawPaths(const GrPathRange* pathRange,
|
||||
int count, const GrStencilSettings& stencilSettings) {
|
||||
SkASSERT(fGpu->caps()->shaderCaps()->pathRenderingSupport());
|
||||
|
||||
GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID();
|
||||
const GrGLPathRange* glPathRange = static_cast<const GrGLPathRange*>(pathRange);
|
||||
|
||||
this->flushPathStencilSettings(stencilSettings);
|
||||
SkASSERT(!fHWPathStencilSettings.isTwoSided());
|
||||
|
||||
const SkStrokeRec& stroke = pathRange->getStroke();
|
||||
|
||||
GrGLenum fillMode =
|
||||
gr_stencil_op_to_gl_path_rendering_fill_mode(
|
||||
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
|
||||
GrGLint writeMask =
|
||||
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
|
||||
|
||||
if (stroke.needToApply()) {
|
||||
if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) {
|
||||
if (glPathRange->shouldStroke()) {
|
||||
if (glPathRange->shouldFill()) {
|
||||
GL_CALL(StencilFillPathInstanced(
|
||||
count, gIndexType2GLType[indexType], indices, baseID, fillMode,
|
||||
writeMask, gXformType2GLType[transformType], transformValues));
|
||||
count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
|
||||
fillMode, writeMask, gXformType2GLType[transformType],
|
||||
transformValues));
|
||||
}
|
||||
this->stencilThenCoverStrokePathInstanced(
|
||||
count, gIndexType2GLType[indexType], indices, baseID,
|
||||
count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
|
||||
0xffff, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
|
||||
gXformType2GLType[transformType], transformValues);
|
||||
} else {
|
||||
this->stencilThenCoverFillPathInstanced(
|
||||
count, gIndexType2GLType[indexType], indices, baseID,
|
||||
count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
|
||||
fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
|
||||
gXformType2GLType[transformType], transformValues);
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ public:
|
||||
virtual ~GrGLPathRendering();
|
||||
|
||||
// GrPathRendering implementations.
|
||||
GrPath* createPath(const SkPath&, const SkStrokeRec&) override;
|
||||
GrPath* createPath(const SkPath&, const GrStrokeInfo&) override;
|
||||
virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*,
|
||||
const SkStrokeRec&) override;
|
||||
const GrStrokeInfo&) override;
|
||||
virtual GrPathRange* createGlyphs(const SkTypeface*,
|
||||
const SkDescriptor*,
|
||||
const SkStrokeRec&) override;
|
||||
const GrStrokeInfo&) override;
|
||||
void stencilPath(const GrPath*, const GrStencilSettings&) override;
|
||||
void drawPath(const GrPath*, const GrStencilSettings&) override;
|
||||
virtual void drawPaths(const GrPathRange*, const void* indices, PathIndexType,
|
||||
|
Loading…
Reference in New Issue
Block a user