diff --git a/src/gpu/GrPath.cpp b/src/gpu/GrPath.cpp index 3a66865c6e..e76bdf2466 100644 --- a/src/gpu/GrPath.cpp +++ b/src/gpu/GrPath.cpp @@ -7,49 +7,14 @@ #include "GrPath.h" -template static uint64_t get_top_n_float_bits(float f) { - char* floatData = reinterpret_cast(&f); - uint32_t floatBits = *reinterpret_cast(floatData); - return floatBits >> (32 - NumBits); -} - -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(&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; +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]); } - - uint64_t key = stroke.getStyle(); - key |= stroke.getJoin() << kJoinShift; - key |= stroke.getCap() << kCapShift; - key |= get_top_n_float_bits(stroke.getWidth()) << kWidthShift; - key |= get_top_n_float_bits(stroke.getMiter()) << kMiterShift; - - return key; } + diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h index ab8c51f2af..a535e697ca 100644 --- a/src/gpu/GrPath.h +++ b/src/gpu/GrPath.h @@ -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; diff --git a/src/gpu/GrPathRange.cpp b/src/gpu/GrPathRange.cpp index 8bac750b44..5e71014ce8 100644 --- a/src/gpu/GrPathRange.cpp +++ b/src/gpu/GrPathRange.cpp @@ -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 { diff --git a/src/gpu/GrPathRange.h b/src/gpu/GrPathRange.h index 86883e3c27..035837fc36 100644 --- a/src/gpu/GrPathRange.h +++ b/src/gpu/GrPathRange.h @@ -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 fPathGenerator; mutable SkTArray fGeneratedPaths; const int fNumPaths; - const SkStrokeRec fStroke; typedef GrGpuResource INHERITED; }; diff --git a/src/gpu/GrPathRendering.cpp b/src/gpu/GrPathRendering.cpp index fba55d5e1e..e97d754a46 100644 --- a/src/gpu/GrPathRendering.cpp +++ b/src/gpu/GrPathRendering.cpp @@ -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 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); diff --git a/src/gpu/GrPathRendering.h b/src/gpu/GrPathRendering.h index 3e2cfc6c9a..e198d694db 100644 --- a/src/gpu/GrPathRendering.h +++ b/src/gpu/GrPathRendering.h @@ -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; diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp index bfb0f04155..e60bea215a 100644 --- a/src/gpu/GrStencilAndCoverPathRenderer.cpp +++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp @@ -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 path( static_cast(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 p(get_gr_path(fGpu, path, stroke)); diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp index a2abfa7fa9..34319516d5 100644 --- a/src/gpu/GrStencilAndCoverTextContext.cpp +++ b/src/gpu/GrStencilAndCoverTextContext.cpp @@ -55,8 +55,10 @@ bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt, if (skPaint.getMaskFilter()) { return false; } - if (skPaint.getPathEffect()) { - return false; + 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. @@ -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(&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(builder[0]) = desc ? desc->getChecksum() : 0; + reinterpret_cast(builder[1]) = typeface ? typeface->uniqueID() : 0; + if (strokeDataCount > 0) { + stroke.asUniqueKeyFragment(&builder[2]); + } builder.finish(); SkAutoTUnref glyphs( - static_cast(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key))); - if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) { + static_cast( + 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(); diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h index d3d2470f26..0cb55535c2 100644 --- a/src/gpu/GrStencilAndCoverTextContext.h +++ b/src/gpu/GrStencilAndCoverTextContext.h @@ -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; diff --git a/src/gpu/GrStrokeInfo.cpp b/src/gpu/GrStrokeInfo.cpp index eb3008bb37..0ad4179add 100644 --- a/src/gpu/GrStrokeInfo.cpp +++ b/src/gpu/GrStrokeInfo.cpp @@ -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(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(~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); +} + diff --git a/src/gpu/GrStrokeInfo.h b/src/gpu/GrStrokeInfo.h index 5ba3cfe91d..1cc2dfe11b 100644 --- a/src/gpu/GrStrokeInfo.h +++ b/src/gpu/GrStrokeInfo.h @@ -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) { diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp index 037134aad4..80c6356cdd 100644 --- a/src/gpu/gl/GrGLPath.cpp +++ b/src/gpu/gl/GrGLPath.cpp @@ -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 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(); } diff --git a/src/gpu/gl/GrGLPath.h b/src/gpu/gl/GrGLPath.h index f02f7052d5..b394050277 100644 --- a/src/gpu/gl/GrGLPath.h +++ b/src/gpu/gl/GrGLPath.h @@ -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; }; diff --git a/src/gpu/gl/GrGLPathRange.cpp b/src/gpu/gl/GrGLPathRange.cpp index d7ccf1bcc7..b577d89619 100644 --- a/src/gpu/gl/GrGLPathRange.cpp +++ b/src/gpu/gl/GrGLPathRange.cpp @@ -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(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 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; diff --git a/src/gpu/gl/GrGLPathRange.h b/src/gpu/gl/GrGLPathRange.h index 5cba9c79d7..22dd1c0572 100644 --- a/src/gpu/gl/GrGLPathRange.h +++ b/src/gpu/gl/GrGLPathRange.h @@ -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; }; diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp index 8a0b35c661..3ceb87692a 100644 --- a/src/gpu/gl/GrGLPathRendering.cpp +++ b/src/gpu/gl/GrGLPathRendering.cpp @@ -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(path)->pathID(); + const GrGLPath* glPath = static_cast(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(path)->pathID(); + const GrGLPath* glPath = static_cast(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(pathRange)->basePathID(); + const GrGLPathRange* glPathRange = static_cast(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); } diff --git a/src/gpu/gl/GrGLPathRendering.h b/src/gpu/gl/GrGLPathRendering.h index 25e8ef03ef..84bac6c117 100644 --- a/src/gpu/gl/GrGLPathRendering.h +++ b/src/gpu/gl/GrGLPathRendering.h @@ -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,