aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/ops/GrDrawPathOp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/ops/GrDrawPathOp.cpp')
-rw-r--r--src/gpu/ops/GrDrawPathOp.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
new file mode 100644
index 0000000000..43e1c38b88
--- /dev/null
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDrawPathOp.h"
+
+#include "GrRenderTargetPriv.h"
+
+static void pre_translate_transform_values(const float* xforms,
+ GrPathRendering::PathTransformType type, int count,
+ SkScalar x, SkScalar y, float* dst);
+
+void GrDrawPathOpBase::onPrepare(GrOpFlushState*) {
+ const GrRenderTargetPriv& rtPriv = this->pipeline()->getRenderTarget()->renderTargetPriv();
+ fStencilPassSettings.reset(GrPathRendering::GetStencilPassSettings(fFillType),
+ this->pipeline()->hasStencilClip(), rtPriv.numStencilBits());
+}
+
+SkString GrDrawPathOp::dumpInfo() const {
+ SkString string;
+ string.printf("PATH: 0x%p", fPath.get());
+ string.append(DumpPipelineInfo(*this->pipeline()));
+ string.append(INHERITED::dumpInfo());
+ return string;
+}
+
+void GrDrawPathOp::onDraw(GrOpFlushState* state, const SkRect& bounds) {
+ GrProgramDesc desc;
+
+ sk_sp<GrPathProcessor> pathProc(
+ GrPathProcessor::Create(this->color(), this->overrides(), this->viewMatrix()));
+ state->gpu()->pathRendering()->drawPath(*this->pipeline(), *pathProc,
+ this->stencilPassSettings(), fPath.get());
+}
+
+SkString GrDrawPathRangeOp::dumpInfo() const {
+ SkString string;
+ string.printf("RANGE: 0x%p COUNTS: [", fPathRange.get());
+ for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) {
+ string.appendf("%d, ", iter.get()->fInstanceData->count());
+ }
+ string.remove(string.size() - 2, 2);
+ string.append("]");
+ string.append(DumpPipelineInfo(*this->pipeline()));
+ string.append(INHERITED::dumpInfo());
+ return string;
+}
+
+GrDrawPathRangeOp::GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
+ SkScalar y, GrColor color, GrPathRendering::FillType fill,
+ GrPathRange* range, const InstanceData* instanceData,
+ const SkRect& bounds)
+ : INHERITED(ClassID(), viewMatrix, color, fill)
+ , fPathRange(range)
+ , fTotalPathCount(instanceData->count())
+ , fScale(scale) {
+ fDraws.addToHead()->set(instanceData, x, y);
+ this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
+}
+
+bool GrDrawPathRangeOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
+ GrDrawPathRangeOp* that = t->cast<GrDrawPathRangeOp>();
+ if (this->fPathRange.get() != that->fPathRange.get() ||
+ this->transformType() != that->transformType() || this->fScale != that->fScale ||
+ this->color() != that->color() || !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+ if (!GrPipeline::AreEqual(*this->pipeline(), *that->pipeline())) {
+ return false;
+ }
+ switch (fDraws.head()->fInstanceData->transformType()) {
+ case GrPathRendering::kNone_PathTransformType:
+ if (this->fDraws.head()->fX != that->fDraws.head()->fX ||
+ this->fDraws.head()->fY != that->fDraws.head()->fY) {
+ return false;
+ }
+ break;
+ case GrPathRendering::kTranslateX_PathTransformType:
+ if (this->fDraws.head()->fY != that->fDraws.head()->fY) {
+ return false;
+ }
+ break;
+ case GrPathRendering::kTranslateY_PathTransformType:
+ if (this->fDraws.head()->fX != that->fDraws.head()->fX) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ // TODO: Check some other things here. (winding, opaque, pathProc color, vm, ...)
+ // Try to combine this call with the previous DrawPaths. We do this by stenciling all the
+ // paths together and then covering them in a single pass. This is not equivalent to two
+ // separate draw calls, so we can only do it if there is no blending (no overlap would also
+ // work). Note that it's also possible for overlapping paths to cancel each other's winding
+ // numbers, and we only partially account for this by not allowing even/odd paths to be
+ // combined. (Glyphs in the same font tend to wind the same direction so it works out OK.)
+ if (GrPathRendering::kWinding_FillType != this->fillType() ||
+ GrPathRendering::kWinding_FillType != that->fillType() ||
+ this->overrides().willColorBlendWithDst()) {
+ return false;
+ }
+ SkASSERT(!that->overrides().willColorBlendWithDst());
+ fTotalPathCount += that->fTotalPathCount;
+ while (Draw* head = that->fDraws.head()) {
+ Draw* draw = fDraws.addToTail();
+ draw->fInstanceData.reset(head->fInstanceData.release());
+ draw->fX = head->fX;
+ draw->fY = head->fY;
+ that->fDraws.popHead();
+ }
+ this->joinBounds(*that);
+ return true;
+}
+
+void GrDrawPathRangeOp::onDraw(GrOpFlushState* state, const SkRect& bounds) {
+ const Draw& head = *fDraws.head();
+
+ SkMatrix drawMatrix(this->viewMatrix());
+ drawMatrix.preScale(fScale, fScale);
+ drawMatrix.preTranslate(head.fX, head.fY);
+
+ SkMatrix localMatrix;
+ localMatrix.setScale(fScale, fScale);
+ localMatrix.preTranslate(head.fX, head.fY);
+
+ sk_sp<GrPathProcessor> pathProc(
+ GrPathProcessor::Create(this->color(), this->overrides(), drawMatrix, localMatrix));
+
+ if (fDraws.count() == 1) {
+ const InstanceData& instances = *head.fInstanceData;
+ state->gpu()->pathRendering()->drawPaths(*this->pipeline(),
+ *pathProc,
+ this->stencilPassSettings(),
+ fPathRange.get(),
+ instances.indices(),
+ GrPathRange::kU16_PathIndexType,
+ instances.transformValues(),
+ instances.transformType(),
+ instances.count());
+ } else {
+ int floatsPerTransform = GrPathRendering::PathTransformSize(this->transformType());
+ SkAutoSTMalloc<4096, float> transformStorage(floatsPerTransform * fTotalPathCount);
+ SkAutoSTMalloc<2048, uint16_t> indexStorage(fTotalPathCount);
+ int idx = 0;
+ for (DrawList::Iter iter(fDraws); iter.get(); iter.next()) {
+ const Draw& draw = *iter.get();
+ const InstanceData& instances = *draw.fInstanceData;
+ memcpy(&indexStorage[idx], instances.indices(), instances.count() * sizeof(uint16_t));
+ pre_translate_transform_values(instances.transformValues(), this->transformType(),
+ instances.count(), draw.fX - head.fX, draw.fY - head.fY,
+ &transformStorage[floatsPerTransform * idx]);
+ idx += instances.count();
+
+ // TODO: Support mismatched transform types if we start using more types other than 2D.
+ SkASSERT(instances.transformType() == this->transformType());
+ }
+ SkASSERT(idx == fTotalPathCount);
+
+ state->gpu()->pathRendering()->drawPaths(*this->pipeline(),
+ *pathProc,
+ this->stencilPassSettings(),
+ fPathRange.get(),
+ indexStorage,
+ GrPathRange::kU16_PathIndexType,
+ transformStorage,
+ this->transformType(),
+ fTotalPathCount);
+ }
+}
+
+inline void pre_translate_transform_values(const float* xforms,
+ GrPathRendering::PathTransformType type, int count,
+ SkScalar x, SkScalar y, float* dst) {
+ if (0 == x && 0 == y) {
+ memcpy(dst, xforms, count * GrPathRendering::PathTransformSize(type) * sizeof(float));
+ return;
+ }
+ switch (type) {
+ case GrPathRendering::kNone_PathTransformType:
+ SkFAIL("Cannot pre-translate kNone_PathTransformType.");
+ break;
+ case GrPathRendering::kTranslateX_PathTransformType:
+ SkASSERT(0 == y);
+ for (int i = 0; i < count; i++) {
+ dst[i] = xforms[i] + x;
+ }
+ break;
+ case GrPathRendering::kTranslateY_PathTransformType:
+ SkASSERT(0 == x);
+ for (int i = 0; i < count; i++) {
+ dst[i] = xforms[i] + y;
+ }
+ break;
+ case GrPathRendering::kTranslate_PathTransformType:
+ for (int i = 0; i < 2 * count; i += 2) {
+ dst[i] = xforms[i] + x;
+ dst[i + 1] = xforms[i + 1] + y;
+ }
+ break;
+ case GrPathRendering::kAffine_PathTransformType:
+ for (int i = 0; i < 6 * count; i += 6) {
+ dst[i] = xforms[i];
+ dst[i + 1] = xforms[i + 1];
+ dst[i + 2] = xforms[i] * x + xforms[i + 1] * y + xforms[i + 2];
+ dst[i + 3] = xforms[i + 3];
+ dst[i + 4] = xforms[i + 4];
+ dst[i + 5] = xforms[i + 3] * x + xforms[i + 4] * y + xforms[i + 5];
+ }
+ break;
+ default:
+ SkFAIL("Unknown transform type.");
+ break;
+ }
+}