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:
kkinnunen 2015-05-18 23:02:07 -07:00 committed by Commit bot
parent 54b8511189
commit 50b58e6fbc
17 changed files with 304 additions and 172 deletions

View File

@ -7,49 +7,14 @@
#include "GrPath.h" #include "GrPath.h"
template<int NumBits> static uint64_t get_top_n_float_bits(float f) { void GrPath::ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key) {
char* floatData = reinterpret_cast<char*>(&f); static const GrUniqueKey::Domain kPathDomain = GrUniqueKey::GenerateDomain();
uint32_t floatBits = *reinterpret_cast<uint32_t*>(floatData); int strokeDataCnt = stroke.computeUniqueKeyFragmentData32Cnt();
return floatBits >> (32 - NumBits); GrUniqueKey::Builder builder(key, kPathDomain, 2 + strokeDataCnt);
} builder[0] = path.getGenerationID();
builder[1] = path.getFillType();
void GrPath::ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key) { if (strokeDataCnt > 0) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); stroke.asUniqueKeyFragment(&builder[2]);
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;
} }

View File

@ -9,9 +9,9 @@
#define GrPath_DEFINED #define GrPath_DEFINED
#include "GrGpuResource.h" #include "GrGpuResource.h"
#include "GrStrokeInfo.h"
#include "SkPath.h" #include "SkPath.h"
#include "SkRect.h" #include "SkRect.h"
#include "SkStrokeRec.h"
class GrPath : public GrGpuResource { class GrPath : public GrGpuResource {
public: public:
@ -20,28 +20,32 @@ public:
/** /**
* Initialize to a path with a fixed stroke. Stroke must not be hairline. * Initialize to a path with a fixed stroke. Stroke must not be hairline.
*/ */
GrPath(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke) GrPath(GrGpu* gpu, const SkPath& skPath, const GrStrokeInfo& stroke)
: INHERITED(gpu, kCached_LifeCycle), : INHERITED(gpu, kCached_LifeCycle)
fSkPath(skPath), , fBounds(skPath.getBounds())
fStroke(stroke), #ifdef SK_DEBUG
fBounds(skPath.getBounds()) { , fSkPath(skPath)
, fStroke(stroke)
#endif
{
} }
static void ComputeKey(const SkPath& path, const SkStrokeRec& stroke, GrUniqueKey* key); static void ComputeKey(const SkPath& path, const GrStrokeInfo& stroke, GrUniqueKey* key);
static uint64_t ComputeStrokeKey(const SkStrokeRec&);
bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
return fSkPath == path && fStroke.hasEqualEffect(stroke);
}
const SkRect& getBounds() const { return fBounds; } 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: protected:
SkPath fSkPath;
SkStrokeRec fStroke;
SkRect fBounds; SkRect fBounds;
#ifdef SK_DEBUG
SkPath fSkPath;
GrStrokeInfo fStroke;
#endif
private: private:
typedef GrGpuResource INHERITED; typedef GrGpuResource INHERITED;

View File

@ -13,23 +13,19 @@ enum {
}; };
GrPathRange::GrPathRange(GrGpu* gpu, GrPathRange::GrPathRange(GrGpu* gpu,
PathGenerator* pathGenerator, PathGenerator* pathGenerator)
const SkStrokeRec& stroke)
: INHERITED(gpu, kCached_LifeCycle), : INHERITED(gpu, kCached_LifeCycle),
fPathGenerator(SkRef(pathGenerator)), fPathGenerator(SkRef(pathGenerator)),
fNumPaths(fPathGenerator->getNumPaths()), fNumPaths(fPathGenerator->getNumPaths()) {
fStroke(stroke) {
const int numGroups = (fNumPaths + kPathsPerGroup - 1) / kPathsPerGroup; const int numGroups = (fNumPaths + kPathsPerGroup - 1) / kPathsPerGroup;
fGeneratedPaths.reset((numGroups + 7) / 8); // 1 bit per path group. fGeneratedPaths.reset((numGroups + 7) / 8); // 1 bit per path group.
memset(&fGeneratedPaths.front(), 0, fGeneratedPaths.count()); memset(&fGeneratedPaths.front(), 0, fGeneratedPaths.count());
} }
GrPathRange::GrPathRange(GrGpu* gpu, GrPathRange::GrPathRange(GrGpu* gpu,
int numPaths, int numPaths)
const SkStrokeRec& stroke)
: INHERITED(gpu, kCached_LifeCycle), : INHERITED(gpu, kCached_LifeCycle),
fNumPaths(numPaths), fNumPaths(numPaths) {
fStroke(stroke) {
} }
void GrPathRange::willDrawPaths(const void* indices, PathIndexType indexType, int count) const { void GrPathRange::willDrawPaths(const void* indices, PathIndexType indexType, int count) const {

View File

@ -10,14 +10,13 @@
#include "GrGpuResource.h" #include "GrGpuResource.h"
#include "SkRefCnt.h" #include "SkRefCnt.h"
#include "SkStrokeRec.h"
#include "SkTArray.h" #include "SkTArray.h"
class SkPath; class SkPath;
class SkDescriptor; 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 * This object is immutable with the exception that individual paths may be
* initialized lazily. * initialized lazily.
*/ */
@ -50,7 +49,9 @@ public:
public: public:
virtual int getNumPaths() = 0; virtual int getNumPaths() = 0;
virtual void generatePath(int index, SkPath* out) = 0; virtual void generatePath(int index, SkPath* out) = 0;
#ifdef SK_DEBUG
virtual bool isEqualTo(const SkDescriptor&) const { return false; } virtual bool isEqualTo(const SkDescriptor&) const { return false; }
#endif
virtual ~PathGenerator() {} virtual ~PathGenerator() {}
}; };
@ -58,22 +59,22 @@ public:
* Initialize a lazy-loaded path range. This class will generate an SkPath and call * 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. * 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 * Initialize an eager-loaded path range. The subclass is responsible for ensuring all
* the paths are initialized up front. * 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 { virtual bool isEqualTo(const SkDescriptor& desc) const {
return NULL != fPathGenerator.get() && fPathGenerator->isEqualTo(desc); return NULL != fPathGenerator.get() && fPathGenerator->isEqualTo(desc);
} }
#endif
int getNumPaths() const { return fNumPaths; }
const SkStrokeRec& getStroke() const { return fStroke; }
const PathGenerator* getPathGenerator() const { return fPathGenerator.get(); }
protected: protected:
// Initialize a path in the range before drawing. This is only called when // Initialize a path in the range before drawing. This is only called when
// fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(), // fPathGenerator is non-null. The child class need not call didChangeGpuMemorySize(),
@ -89,7 +90,6 @@ private:
mutable SkAutoTUnref<PathGenerator> fPathGenerator; mutable SkAutoTUnref<PathGenerator> fPathGenerator;
mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths; mutable SkTArray<uint8_t, true /*MEM_COPY*/> fGeneratedPaths;
const int fNumPaths; const int fNumPaths;
const SkStrokeRec fStroke;
typedef GrGpuResource INHERITED; typedef GrGpuResource INHERITED;
}; };

View File

@ -15,13 +15,18 @@
class GlyphGenerator : public GrPathRange::PathGenerator { class GlyphGenerator : public GrPathRange::PathGenerator {
public: public:
GlyphGenerator(const SkTypeface& typeface, const SkDescriptor& desc) GlyphGenerator(const SkTypeface& typeface, const SkDescriptor& desc)
: fDesc(desc.copy()), : fScalerContext(typeface.createScalerContext(&desc))
fScalerContext(typeface.createScalerContext(fDesc)) { #ifdef SK_DEBUG
, fDesc(desc.copy())
#endif
{
fFlipMatrix.setScale(1, -1); fFlipMatrix.setScale(1, -1);
} }
virtual ~GlyphGenerator() { virtual ~GlyphGenerator() {
#ifdef SK_DEBUG
SkDescriptor::Free(fDesc); SkDescriptor::Free(fDesc);
#endif
} }
int getNumPaths() override { int getNumPaths() override {
@ -36,20 +41,22 @@ public:
fScalerContext->getPath(skGlyph, out); fScalerContext->getPath(skGlyph, out);
out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction. out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction.
} }
#ifdef SK_DEBUG
bool isEqualTo(const SkDescriptor& desc) const override { bool isEqualTo(const SkDescriptor& desc) const override {
return fDesc->equals(desc); return fDesc->equals(desc);
} }
#endif
private: private:
SkDescriptor* const fDesc;
const SkAutoTDelete<SkScalerContext> fScalerContext; const SkAutoTDelete<SkScalerContext> fScalerContext;
SkMatrix fFlipMatrix; SkMatrix fFlipMatrix;
#ifdef SK_DEBUG
SkDescriptor* const fDesc;
#endif
}; };
GrPathRange* GrPathRendering::createGlyphs(const SkTypeface* typeface, GrPathRange* GrPathRendering::createGlyphs(const SkTypeface* typeface,
const SkDescriptor* desc, const SkDescriptor* desc,
const SkStrokeRec& stroke) { const GrStrokeInfo& stroke) {
if (NULL == typeface) { if (NULL == typeface) {
typeface = SkTypeface::GetDefaultTypeface(); typeface = SkTypeface::GetDefaultTypeface();
SkASSERT(NULL != typeface); SkASSERT(NULL != typeface);

View File

@ -11,12 +11,12 @@
#include "SkPath.h" #include "SkPath.h"
#include "GrPathRange.h" #include "GrPathRange.h"
class SkStrokeRec;
class SkDescriptor; class SkDescriptor;
class SkTypeface; class SkTypeface;
class GrPath; class GrPath;
class GrGpu; class GrGpu;
class GrStencilSettings; class GrStencilSettings;
class GrStrokeInfo;
/** /**
* Abstract class wrapping HW path rendering API. * Abstract class wrapping HW path rendering API.
@ -84,17 +84,17 @@ public:
* @param stroke the path stroke. * @param stroke the path stroke.
* @return a new path. * @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 * 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. * 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 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. * @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 * 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 * including with the stroke information baked directly into
* the outlines. * 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 * if the glyph outlines contain baked-in strokes from the font
* descriptor, the GPU stroke will be applied on top of those * descriptor, the GPU stroke will be applied on top of those
* outlines. * outlines.
* *
* @return a new path range populated with glyphs. * @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 stencilPath(const GrPath*, const GrStencilSettings&) = 0;
virtual void drawPath(const GrPath*, const GrStencilSettings&) = 0; virtual void drawPath(const GrPath*, const GrStencilSettings&) = 0;

View File

@ -60,7 +60,6 @@ bool GrStencilAndCoverPathRenderer::canDrawPath(const GrDrawTarget* target,
const GrStrokeInfo& stroke, const GrStrokeInfo& stroke,
bool antiAlias) const { bool antiAlias) const {
return !stroke.isHairlineStyle() && return !stroke.isHairlineStyle() &&
!stroke.isDashed() &&
!antiAlias && // doesn't do per-path AA, relies on the target having MSAA !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
pipelineBuilder->getStencil().isDisabled(); pipelineBuilder->getStencil().isDisabled();
} }
@ -73,15 +72,17 @@ GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
return GrPathRenderer::kStencilOnly_StencilSupport; 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(); GrContext* ctx = gpu->getContext();
GrUniqueKey key; GrUniqueKey key;
GrPath::ComputeKey(skPath, stroke, &key); GrPath::ComputeKey(skPath, stroke, &key);
SkAutoTUnref<GrPath> path( SkAutoTUnref<GrPath> path(
static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key))); static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
if (NULL == path || !path->isEqualTo(skPath, stroke)) { if (NULL == path) {
path.reset(gpu->pathRendering()->createPath(skPath, stroke)); path.reset(gpu->pathRendering()->createPath(skPath, stroke));
ctx->resourceProvider()->assignUniqueKeyToResource(key, path); ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
} else {
SkASSERT(path->isEqualTo(skPath, stroke));
} }
return path.detach(); return path.detach();
} }
@ -106,7 +107,6 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(GrDrawTarget* target,
bool antiAlias) { bool antiAlias) {
SkASSERT(!antiAlias); SkASSERT(!antiAlias);
SkASSERT(!stroke.isHairlineStyle()); SkASSERT(!stroke.isHairlineStyle());
SkASSERT(!stroke.isDashed());
SkASSERT(pipelineBuilder->getStencil().isDisabled()); SkASSERT(pipelineBuilder->getStencil().isDisabled());
SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke)); SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke));

View File

@ -55,8 +55,10 @@ bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
if (skPaint.getMaskFilter()) { if (skPaint.getMaskFilter()) {
return false; return false;
} }
if (skPaint.getPathEffect()) { if (SkPathEffect* pe = skPaint.getPathEffect()) {
return false; if (pe->asADash(NULL) != SkPathEffect::kDash_DashType) {
return false;
}
} }
// No hairlines unless we can map the 1 px width to the object space. // No hairlines unless we can map the 1 px width to the object space.
@ -220,26 +222,27 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
static GrPathRange* get_gr_glyphs(GrContext* ctx, static GrPathRange* get_gr_glyphs(GrContext* ctx,
const SkTypeface* typeface, const SkTypeface* typeface,
const SkDescriptor* desc, const SkDescriptor* desc,
const SkStrokeRec& stroke) { const GrStrokeInfo& stroke) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key; static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&key, kDomain, 4); int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
struct GlyphKey { GrUniqueKey glyphKey;
uint32_t fChecksum; GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
uint32_t fTypeface; reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
uint64_t fStroke; reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
}; if (strokeDataCount > 0) {
GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]); stroke.asUniqueKeyFragment(&builder[2]);
glyphKey->fChecksum = desc ? desc->getChecksum() : 0; }
glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
builder.finish(); builder.finish();
SkAutoTUnref<GrPathRange> glyphs( SkAutoTUnref<GrPathRange> glyphs(
static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key))); static_cast<GrPathRange*>(
if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) { ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
if (NULL == glyphs) {
glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke)); 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(); 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 // The whole shape (including stroke) will be baked into the glyph outlines. Make
// NVPR just fill the baked shapes. // NVPR just fill the baked shapes.
fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle); fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
fTextRatio = fTextInverseRatio = 1.0f; fTextRatio = fTextInverseRatio = 1.0f;
@ -298,7 +301,7 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
} else { } else {
// Don't bake strokes into the glyph outlines. We will stroke the glyphs // Don't bake strokes into the glyph outlines. We will stroke the glyphs
// using the GPU instead. This is the fast path. // using the GPU instead. This is the fast path.
fStroke = SkStrokeRec(fSkPaint); fStroke = GrStrokeInfo(fSkPaint);
fSkPaint.setStyle(SkPaint::kFill_Style); fSkPaint.setStyle(SkPaint::kFill_Style);
if (fStroke.isHairlineStyle()) { if (fStroke.isHairlineStyle()) {
@ -327,8 +330,8 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
} }
bool canUseRawPaths; bool canUseRawPaths;
if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) { kMaxPerformance_RenderMode == renderMode)) {
// We can draw the glyphs from canonically sized paths. // We can draw the glyphs from canonically sized paths.
fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize(); fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();

View File

@ -10,7 +10,7 @@
#include "GrTextContext.h" #include "GrTextContext.h"
#include "GrDrawTarget.h" #include "GrDrawTarget.h"
#include "SkStrokeRec.h" #include "GrStrokeInfo.h"
class GrTextStrike; class GrTextStrike;
class GrPath; class GrPath;
@ -58,7 +58,7 @@ private:
float fTextInverseRatio; float fTextInverseRatio;
SkGlyphCache* fGlyphCache; SkGlyphCache* fGlyphCache;
GrPathRange* fGlyphs; GrPathRange* fGlyphs;
SkStrokeRec fStroke; GrStrokeInfo fStroke;
uint16_t fGlyphIndices[kGlyphBufferSize]; uint16_t fGlyphIndices[kGlyphBufferSize];
SkPoint fGlyphPositions[kGlyphBufferSize]; SkPoint fGlyphPositions[kGlyphBufferSize];
int fQueuedGlyphCount; int fQueuedGlyphCount;

View File

@ -6,7 +6,7 @@
*/ */
#include "GrStrokeInfo.h" #include "GrStrokeInfo.h"
#include "GrResourceKey.h"
#include "SkDashPathPriv.h" #include "SkDashPathPriv.h"
bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo, bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
@ -24,3 +24,55 @@ bool GrStrokeInfo::applyDashToPath(SkPath* dst, GrStrokeInfo* dstStrokeInfo,
} }
return false; 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);
}

View File

@ -11,6 +11,8 @@
#include "SkStrokeRec.h" #include "SkStrokeRec.h"
#include "SkPathEffect.h" #include "SkPathEffect.h"
class GrUniqueKey;
/* /*
* GrStrokeInfo encapsulates all the pertinent infomation regarding the stroke. The SkStrokeRec * 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 * which holds information on fill style, width, miter, cap, and join. It also holds information
@ -60,6 +62,21 @@ public:
return *this; 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 * 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, * 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; 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: private:
// Prevent accidental usage, not implemented for GrStrokeInfos. // Prevent accidental usage, should use GrStrokeInfo::hasEqualEffect.
bool hasEqualEffect(const SkStrokeRec& other) const; bool hasEqualEffect(const SkStrokeRec& other) const;
void init(const SkPaint& paint) { void init(const SkPaint& paint) {

View File

@ -91,7 +91,8 @@ inline void points_to_coords(const SkPoint points[], size_t first_point, size_t
void GrGLPath::InitPathObject(GrGLGpu* gpu, void GrGLPath::InitPathObject(GrGLGpu* gpu,
GrGLuint pathID, GrGLuint pathID,
const SkPath& skPath, const SkPath& skPath,
const SkStrokeRec& stroke) { const GrStrokeInfo& stroke) {
SkASSERT(!stroke.isDashed());
if (!skPath.isEmpty()) { if (!skPath.isEmpty()) {
int verbCnt = skPath.countVerbs(); int verbCnt = skPath.countVerbs();
int pointCnt = skPath.countPoints(); int pointCnt = skPath.countPoints();
@ -182,16 +183,33 @@ void GrGLPath::InitPathObject(GrGLGpu* gpu,
} }
} }
GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& path, const SkStrokeRec& stroke) GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke)
: INHERITED(gpu, path, stroke), : INHERITED(gpu, origSkPath, origStroke),
fPathID(gpu->glPathRendering()->genPaths(1)) { 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->isDashed()) {
if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
if (stroke.needToApply()) { skPath = tmpPath.get();
// FIXME: try to account for stroking, without rasterizing the stroke. stroke = &tmpStroke;
fBounds.outset(stroke.getWidth(), stroke.getWidth()); }
} }
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(); this->registerWithCache();
} }

View File

@ -25,11 +25,13 @@ public:
static void InitPathObject(GrGLGpu*, static void InitPathObject(GrGLGpu*,
GrGLuint pathID, GrGLuint pathID,
const SkPath&, 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; } GrGLuint pathID() const { return fPathID; }
bool shouldStroke() const { return fShouldStroke; }
bool shouldFill() const { return fShouldFill; }
protected: protected:
void onRelease() override; void onRelease() override;
void onAbandon() override; void onAbandon() override;
@ -39,6 +41,8 @@ private:
size_t onGpuMemorySize() const override { return 100; } size_t onGpuMemorySize() const override { return 100; }
GrGLuint fPathID; GrGLuint fPathID;
bool fShouldStroke;
bool fShouldFill;
typedef GrPath INHERITED; typedef GrPath INHERITED;
}; };

View File

@ -11,10 +11,12 @@
#include "GrGLPathRendering.h" #include "GrGLPathRendering.h"
#include "GrGLGpu.h" #include "GrGLGpu.h"
GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const SkStrokeRec& stroke) GrGLPathRange::GrGLPathRange(GrGLGpu* gpu, PathGenerator* pathGenerator, const GrStrokeInfo& stroke)
: INHERITED(gpu, pathGenerator, stroke), : INHERITED(gpu, pathGenerator),
fStroke(stroke),
fBasePathID(gpu->glPathRendering()->genPaths(this->getNumPaths())), fBasePathID(gpu->glPathRendering()->genPaths(this->getNumPaths())),
fGpuMemorySize(0) { fGpuMemorySize(0) {
this->init();
this->registerWithCache(); this->registerWithCache();
} }
@ -22,14 +24,27 @@ GrGLPathRange::GrGLPathRange(GrGLGpu* gpu,
GrGLuint basePathID, GrGLuint basePathID,
int numPaths, int numPaths,
size_t gpuMemorySize, size_t gpuMemorySize,
const SkStrokeRec& stroke) const GrStrokeInfo& stroke)
: INHERITED(gpu, numPaths, stroke), : INHERITED(gpu, numPaths),
fStroke(stroke),
fBasePathID(basePathID), fBasePathID(basePathID),
fGpuMemorySize(gpuMemorySize) { fGpuMemorySize(gpuMemorySize) {
this->init();
this->registerWithCache(); 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()); GrGLGpu* gpu = static_cast<GrGLGpu*>(this->getGpu());
if (NULL == gpu) { if (NULL == gpu) {
return; return;
@ -41,7 +56,31 @@ void GrGLPathRange::onInitPath(int index, const SkPath& skPath) const {
GR_GL_CALL_RET(gpu->glInterface(), isPath, IsPath(fBasePathID + index))); GR_GL_CALL_RET(gpu->glInterface(), isPath, IsPath(fBasePathID + index)));
SkASSERT(GR_GL_FALSE == isPath); 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. // TODO: Use a better approximation for the individual path sizes.
fGpuMemorySize += 100; fGpuMemorySize += 100;

View File

@ -10,6 +10,7 @@
#define GrGLPathRange_DEFINED #define GrGLPathRange_DEFINED
#include "../GrPathRange.h" #include "../GrPathRange.h"
#include "GrStrokeInfo.h"
#include "gl/GrGLFunctions.h" #include "gl/GrGLFunctions.h"
class GrGLGpu; class GrGLGpu;
@ -26,7 +27,7 @@ public:
* Initialize a GL path range from a PathGenerator. This class will allocate * Initialize a GL path range from a PathGenerator. This class will allocate
* the GPU path objects and initialize them lazily. * 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 * Initialize a GL path range from an existing range of pre-initialized GPU
@ -37,10 +38,13 @@ public:
GrGLuint basePathID, GrGLuint basePathID,
int numPaths, int numPaths,
size_t gpuMemorySize, size_t gpuMemorySize,
const SkStrokeRec&); const GrStrokeInfo&);
GrGLuint basePathID() const { return fBasePathID; } GrGLuint basePathID() const { return fBasePathID; }
bool shouldStroke() const { return fShouldStroke; }
bool shouldFill() const { return fShouldFill; }
protected: protected:
void onInitPath(int index, const SkPath&) const override; void onInitPath(int index, const SkPath&) const override;
@ -48,10 +52,14 @@ protected:
void onAbandon() override; void onAbandon() override;
private: private:
void init();
size_t onGpuMemorySize() const override { return fGpuMemorySize; } size_t onGpuMemorySize() const override { return fGpuMemorySize; }
const GrStrokeInfo fStroke;
GrGLuint fBasePathID; GrGLuint fBasePathID;
mutable size_t fGpuMemorySize; mutable size_t fGpuMemorySize;
bool fShouldStroke;
bool fShouldFill;
typedef GrPathRange INHERITED; typedef GrPathRange INHERITED;
}; };

View File

@ -91,19 +91,19 @@ void GrGLPathRendering::resetContext() {
fHWPathStencilSettings.invalidate(); 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)); return SkNEW_ARGS(GrGLPath, (fGpu, inPath, stroke));
} }
GrPathRange* GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator, GrPathRange* GrGLPathRendering::createPathRange(GrPathRange::PathGenerator* pathGenerator,
const SkStrokeRec& stroke) { const GrStrokeInfo& stroke) {
return SkNEW_ARGS(GrGLPathRange, (fGpu, pathGenerator, stroke)); return SkNEW_ARGS(GrGLPathRange, (fGpu, pathGenerator, stroke));
} }
GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface, GrPathRange* GrGLPathRendering::createGlyphs(const SkTypeface* typeface,
const SkDescriptor* desc, const SkDescriptor* desc,
const SkStrokeRec& stroke) { const GrStrokeInfo& stroke) {
if (NULL != desc || !caps().glyphLoadingSupport) { if (NULL != desc || !caps().glyphLoadingSupport || stroke.isDashed()) {
return GrPathRendering::createGlyphs(typeface, desc, stroke); 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) { 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); this->flushPathStencilSettings(stencilSettings);
SkASSERT(!fHWPathStencilSettings.isTwoSided()); 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); GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
if (stroke.isFillStyle() || SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { if (glPath->shouldFill()) {
GL_CALL(StencilFillPath(id, fillMode, writeMask)); GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
} }
if (stroke.needToApply()) { if (glPath->shouldStroke()) {
GL_CALL(StencilStrokePath(id, 0xffff, writeMask)); GL_CALL(StencilStrokePath(glPath->pathID(), 0xffff, writeMask));
} }
} }
void GrGLPathRendering::drawPath(const GrPath* path, const GrStencilSettings& stencilSettings) { 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); this->flushPathStencilSettings(stencilSettings);
SkASSERT(!fHWPathStencilSettings.isTwoSided()); 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); GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
if (stroke.needToApply()) { if (glPath->shouldStroke()) {
if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { if (glPath->shouldFill()) {
GL_CALL(StencilFillPath(id, fillMode, writeMask)); 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 { } 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) { int count, const GrStencilSettings& stencilSettings) {
SkASSERT(fGpu->caps()->shaderCaps()->pathRenderingSupport()); SkASSERT(fGpu->caps()->shaderCaps()->pathRenderingSupport());
GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID(); const GrGLPathRange* glPathRange = static_cast<const GrGLPathRange*>(pathRange);
this->flushPathStencilSettings(stencilSettings); this->flushPathStencilSettings(stencilSettings);
SkASSERT(!fHWPathStencilSettings.isTwoSided()); SkASSERT(!fHWPathStencilSettings.isTwoSided());
const SkStrokeRec& stroke = pathRange->getStroke();
GrGLenum fillMode = GrGLenum fillMode =
gr_stencil_op_to_gl_path_rendering_fill_mode( gr_stencil_op_to_gl_path_rendering_fill_mode(
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face)); fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
GrGLint writeMask = GrGLint writeMask =
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face); fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
if (stroke.needToApply()) { if (glPathRange->shouldStroke()) {
if (SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()) { if (glPathRange->shouldFill()) {
GL_CALL(StencilFillPathInstanced( GL_CALL(StencilFillPathInstanced(
count, gIndexType2GLType[indexType], indices, baseID, fillMode, count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
writeMask, gXformType2GLType[transformType], transformValues)); fillMode, writeMask, gXformType2GLType[transformType],
transformValues));
} }
this->stencilThenCoverStrokePathInstanced( this->stencilThenCoverStrokePathInstanced(
count, gIndexType2GLType[indexType], indices, baseID, count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
0xffff, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES, 0xffff, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
gXformType2GLType[transformType], transformValues); gXformType2GLType[transformType], transformValues);
} else { } else {
this->stencilThenCoverFillPathInstanced( this->stencilThenCoverFillPathInstanced(
count, gIndexType2GLType[indexType], indices, baseID, count, gIndexType2GLType[indexType], indices, glPathRange->basePathID(),
fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES, fillMode, writeMask, GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
gXformType2GLType[transformType], transformValues); gXformType2GLType[transformType], transformValues);
} }

View File

@ -33,12 +33,12 @@ public:
virtual ~GrGLPathRendering(); virtual ~GrGLPathRendering();
// GrPathRendering implementations. // GrPathRendering implementations.
GrPath* createPath(const SkPath&, const SkStrokeRec&) override; GrPath* createPath(const SkPath&, const GrStrokeInfo&) override;
virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*, virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*,
const SkStrokeRec&) override; const GrStrokeInfo&) override;
virtual GrPathRange* createGlyphs(const SkTypeface*, virtual GrPathRange* createGlyphs(const SkTypeface*,
const SkDescriptor*, const SkDescriptor*,
const SkStrokeRec&) override; const GrStrokeInfo&) override;
void stencilPath(const GrPath*, const GrStencilSettings&) override; void stencilPath(const GrPath*, const GrStencilSettings&) override;
void drawPath(const GrPath*, const GrStencilSettings&) override; void drawPath(const GrPath*, const GrStencilSettings&) override;
virtual void drawPaths(const GrPathRange*, const void* indices, PathIndexType, virtual void drawPaths(const GrPathRange*, const void* indices, PathIndexType,