Transform vertices for distance field glyphs on CPU.
This allows batching of DF draws with different view matrices.
For perspective matrices this means the transformed position vertex
attribute must have w values. Currently, non-perspective DF draws still
use 2 component positions, though this could be changed in the future.
Consequently, perspective draws can batch with other perspective draws
but not non-perspective draws.
Adds a GM to test batching and reusing the same blobs with both perspective
and non-perspective matrices.
Change-Id: I0e42c5449ebf3a5a54025dbcdec824d904d5bd9e
Reviewed-on: https://skia-review.googlesource.com/79900
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/gm/dftext_blob_persp.cpp b/gm/dftext_blob_persp.cpp
new file mode 100644
index 0000000..cfa681d
--- /dev/null
+++ b/gm/dftext_blob_persp.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "Resources.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+#include "sk_tool_utils.h"
+
+/**
+ * This GM tests reusing the same text blobs with distance fields rendering using various
+ * combinations of perspective and non-perspetive matrices, scissor clips, and different x,y params
+ * passed to the draw.
+ */
+class DFTextBlobPerspGM : public skiagm::GM {
+public:
+ DFTextBlobPerspGM() { this->setBGColor(0xFFFFFFFF); }
+
+protected:
+ SkString onShortName() override {
+ SkString name("dftext_blob_persp");
+ name.append(sk_tool_utils::platform_font_manager());
+ return name;
+ }
+
+ SkISize onISize() override { return SkISize::Make(900, 350); }
+
+ void onOnceBeforeDraw() override {
+ for (int i = 0; i < 3; ++i) {
+ SkPaint paint;
+ paint.setTextSize(32);
+ paint.setAntiAlias(i > 0);
+ paint.setLCDRenderText(i > 1);
+ paint.setSubpixelText(true);
+ SkTextBlobBuilder builder;
+ sk_tool_utils::add_to_text_blob(&builder, "SkiaText", paint, 0, 0);
+ fBlobs.emplace_back(builder.make());
+ }
+ }
+
+ virtual void onDraw(SkCanvas* inputCanvas) override {
+ // set up offscreen rendering with distance field text
+#if SK_SUPPORT_GPU
+ GrContext* ctx = inputCanvas->getGrContext();
+ SkISize size = this->onISize();
+ if (!inputCanvas->getBaseLayerSize().isEmpty()) {
+ size = inputCanvas->getBaseLayerSize();
+ }
+ SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType,
+ inputCanvas->imageInfo().refColorSpace());
+ SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
+ SkSurfaceProps::kLegacyFontHost_InitType);
+ auto surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props);
+ SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas;
+ // init our new canvas with the old canvas's matrix
+ canvas->setMatrix(inputCanvas->getTotalMatrix());
+#else
+ SkCanvas* canvas = inputCanvas;
+#endif
+ SkScalar x = 0, y = 0;
+ SkScalar maxH = 0;
+ for (auto twm : {TranslateWithMatrix::kNo, TranslateWithMatrix::kYes}) {
+ for (auto pm : {PerspMode::kNone, PerspMode::kX, PerspMode::kY, PerspMode::kXY}) {
+ for (auto& blob : fBlobs) {
+ for (bool clip : {false, true}) {
+ canvas->save();
+ SkScalar w = blob->bounds().width();
+ SkScalar h = blob->bounds().height();
+ if (clip) {
+ auto rect =
+ SkRect::MakeXYWH(x + 5, y + 5, w * 3.f / 4.f, h * 3.f / 4.f);
+ canvas->clipRect(rect, false);
+ }
+ this->drawBlob(canvas, blob.get(), SK_ColorBLACK, x, y + h, pm, twm);
+ x += w + 20.f;
+ maxH = SkTMax(h, maxH);
+ canvas->restore();
+ }
+ }
+ x = 0;
+ y += maxH + 20.f;
+ maxH = 0;
+ }
+ }
+#if SK_SUPPORT_GPU
+ // render offscreen buffer
+ if (surface) {
+ SkAutoCanvasRestore acr(inputCanvas, true);
+ // since we prepended this matrix already, we blit using identity
+ inputCanvas->resetMatrix();
+ inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr);
+ }
+#endif
+ }
+
+private:
+ enum class PerspMode { kNone, kX, kY, kXY };
+
+ enum class TranslateWithMatrix : bool { kNo, kYes };
+
+ void drawBlob(SkCanvas* canvas, SkTextBlob* blob, SkColor color, SkScalar x, SkScalar y,
+ PerspMode perspMode, TranslateWithMatrix translateWithMatrix) {
+ canvas->save();
+ SkMatrix persp = SkMatrix::I();
+ switch (perspMode) {
+ case PerspMode::kNone:
+ break;
+ case PerspMode::kX:
+ persp.setPerspX(0.005f);
+ break;
+ case PerspMode::kY:
+ persp.setPerspY(00.005f);
+ break;
+ case PerspMode::kXY:
+ persp.setPerspX(-0.001f);
+ persp.setPerspY(-0.0015f);
+ break;
+ }
+ persp = SkMatrix::Concat(persp, SkMatrix::MakeTrans(-x, -y));
+ persp = SkMatrix::Concat(SkMatrix::MakeTrans(x, y), persp);
+ canvas->concat(persp);
+ if (TranslateWithMatrix::kYes == translateWithMatrix) {
+ canvas->translate(x, y);
+ x = 0;
+ y = 0;
+ }
+ SkPaint paint;
+ paint.setColor(color);
+ canvas->drawTextBlob(blob, x, y, paint);
+ canvas->restore();
+ }
+
+ SkTArray<sk_sp<SkTextBlob>, true> fBlobs;
+ typedef skiagm::GM INHERITED;
+};
+
+DEF_GM(return new DFTextBlobPerspGM;)
diff --git a/gn/gm.gni b/gn/gm.gni
index 4d4833f..62be999 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -101,6 +101,7 @@
"$_gm/dashing.cpp",
"$_gm/degeneratesegments.cpp",
"$_gm/dftext.cpp",
+ "$_gm/dftext_blob_persp.cpp",
"$_gm/discard.cpp",
"$_gm/displacement.cpp",
"$_gm/distantclip.cpp",
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index e882328..b527ab5 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -1337,6 +1337,9 @@
@param count items in SkPoint3 array to transform
*/
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const;
+ /** Same as above but with a variable offset between successive points. */
+ void mapHomogeneousPointsWithStride(SkPoint3 dst[], const SkPoint3 src[], size_t stride,
+ int count) const;
/** Maps SkPoint (x, y) to result. SkPoint is mapped by multiplying by SkMatrix. Given:
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index d025240..4851aea 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1037,32 +1037,48 @@
///////////////////////////////////////////////////////////////////////////////
-void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const {
+void SkMatrix::mapHomogeneousPointsWithStride(SkPoint3 dst[], const SkPoint3 src[], size_t stride,
+ int count) const {
SkASSERT((dst && src && count > 0) || 0 == count);
// no partial overlap
SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
if (count > 0) {
if (this->isIdentity()) {
- memcpy(dst, src, count * sizeof(SkPoint3));
+ if (src != dst) {
+ if (stride == sizeof(SkPoint3)) {
+ memcpy(dst, src, count * sizeof(SkPoint3));
+ } else {
+ for (int i = 0; i < count; ++i) {
+ *dst = *src;
+ dst = reinterpret_cast<SkPoint3*>(reinterpret_cast<char*>(dst) + stride);
+ src = reinterpret_cast<const SkPoint3*>(reinterpret_cast<const char*>(src) +
+ stride);
+ }
+ }
+ }
return;
}
do {
SkScalar sx = src->fX;
SkScalar sy = src->fY;
SkScalar sw = src->fZ;
- src++;
+ src = reinterpret_cast<const SkPoint3*>(reinterpret_cast<const char*>(src) + stride);
SkScalar x = sdot(sx, fMat[kMScaleX], sy, fMat[kMSkewX], sw, fMat[kMTransX]);
SkScalar y = sdot(sx, fMat[kMSkewY], sy, fMat[kMScaleY], sw, fMat[kMTransY]);
SkScalar w = sdot(sx, fMat[kMPersp0], sy, fMat[kMPersp1], sw, fMat[kMPersp2]);
dst->set(x, y, w);
- dst++;
+ dst = reinterpret_cast<SkPoint3*>(reinterpret_cast<char*>(dst) + stride);
} while (--count);
}
}
+void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const {
+ this->mapHomogeneousPointsWithStride(dst, src, sizeof(SkPoint3), count);
+}
+
///////////////////////////////////////////////////////////////////////////////
void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index 2747f55..b8548bb 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -6,7 +6,6 @@
*/
#include "GrDistanceFieldGeoProc.h"
-
#include "GrAtlasedShaderHelpers.h"
#include "GrTexture.h"
#include "SkDistanceFieldGen.h"
@@ -23,13 +22,7 @@
class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor {
public:
- GrGLDistanceFieldA8TextGeoProc()
- : fViewMatrix(SkMatrix::InvalidMatrix())
- #ifdef SK_GAMMA_APPLY_TO_A8
- , fDistanceAdjust(-1.0f)
- #endif
- , fAtlasSize({0,0}) {
- }
+ GrGLDistanceFieldA8TextGeoProc() = default;
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
const GrDistanceFieldA8TextGeoProc& dfTexEffect =
@@ -61,18 +54,14 @@
varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
// Setup position
- this->writeOutputPosition(vertBuilder,
- uniformHandler,
- gpArgs,
- dfTexEffect.inPosition()->fName,
- dfTexEffect.viewMatrix(),
- &fViewMatrixUniform);
+ gpArgs->fPositionVar = dfTexEffect.inPosition()->asShaderVar();
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
dfTexEffect.inPosition()->asShaderVar(),
+ dfTexEffect.localMatrix(),
args.fFPCoordTransformHandler);
// add varyings
@@ -182,13 +171,6 @@
}
#endif
- if (!dfa8gp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfa8gp.viewMatrix())) {
- fViewMatrix = dfa8gp.viewMatrix();
- float viewMatrix[3 * 3];
- GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
- pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
- }
-
SkASSERT(dfa8gp.numTextureSamplers() >= 1);
GrTexture* atlas = dfa8gp.textureSampler(0).peekTexture();
SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
@@ -198,7 +180,7 @@
pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height());
}
- this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, &transformIter);
}
static inline void GenKey(const GrGeometryProcessor& gp,
@@ -206,19 +188,16 @@
GrProcessorKeyBuilder* b) {
const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>();
uint32_t key = dfTexEffect.getFlags();
- key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
b->add32(key);
b->add32(dfTexEffect.numTextureSamplers());
}
private:
- SkMatrix fViewMatrix;
- UniformHandle fViewMatrixUniform;
#ifdef SK_GAMMA_APPLY_TO_A8
- float fDistanceAdjust;
+ float fDistanceAdjust = -1.f;
UniformHandle fDistanceAdjustUni;
#endif
- SkISize fAtlasSize;
+ SkISize fAtlasSize = {0, 0};
UniformHandle fAtlasSizeInvUniform;
typedef GrGLSLGeometryProcessor INHERITED;
@@ -227,26 +206,28 @@
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(
- GrColor color,
- const SkMatrix& viewMatrix,
- const sk_sp<GrTextureProxy> proxies[kMaxTextures],
- const GrSamplerState& params,
+ GrColor color,
+ const sk_sp<GrTextureProxy> proxies[kMaxTextures],
+ const GrSamplerState& params,
#ifdef SK_GAMMA_APPLY_TO_A8
- float distanceAdjust,
+ float distanceAdjust,
#endif
- uint32_t flags,
- bool usesLocalCoords)
+ uint32_t flags,
+ const SkMatrix& localMatrix)
: INHERITED(kGrDistanceFieldA8TextGeoProc_ClassID)
, fColor(color)
- , fViewMatrix(viewMatrix)
#ifdef SK_GAMMA_APPLY_TO_A8
, fDistanceAdjust(distanceAdjust)
#endif
, fFlags(flags & kNonLCD_DistanceFieldEffectMask)
, fInColor(nullptr)
- , fUsesLocalCoords(usesLocalCoords) {
+ , fLocalMatrix(localMatrix) {
SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
- fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
+ if (flags & kPerspective_DistanceFieldEffectFlag) {
+ fInPosition = &this->addVertexAttrib("inPosition", kFloat3_GrVertexAttribType);
+ } else {
+ fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
+ }
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
@@ -303,14 +284,16 @@
if (flags & kSimilarity_DistanceFieldEffectFlag) {
flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
}
-
- return GrDistanceFieldA8TextGeoProc::Make(GrRandomColor(d->fRandom),
- GrTest::TestMatrix(d->fRandom), proxies,
+ SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
+ GrColor color = GrRandomColor(d->fRandom);
+ float lum = d->fRandom->nextF();
+ return GrDistanceFieldA8TextGeoProc::Make(color,
+ proxies,
samplerState,
#ifdef SK_GAMMA_APPLY_TO_A8
- d->fRandom->nextF(),
+ lum,
#endif
- flags, d->fRandom->nextBool());
+ flags, localMatrix);
}
#endif
@@ -324,7 +307,8 @@
}
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
- const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>();
+ const GrDistanceFieldPathGeoProc& dfPathEffect =
+ args.fGP.cast<GrDistanceFieldPathGeoProc>();
GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
@@ -333,7 +317,7 @@
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
// emit attributes
- varyingHandler->emitAttributes(dfTexEffect);
+ varyingHandler->emitAttributes(dfPathEffect);
const char* atlasSizeInvName;
fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag,
@@ -345,55 +329,55 @@
GrGLSLVarying uv(kFloat2_GrSLType);
GrGLSLVarying texIdx(kHalf_GrSLType);
GrGLSLVarying st(kFloat2_GrSLType);
- append_index_uv_varyings(args, dfTexEffect.inTextureCoords()->fName, atlasSizeInvName,
- &uv, &texIdx, &st);
+ append_index_uv_varyings(args, dfPathEffect.inTextureCoords()->fName, atlasSizeInvName, &uv,
+ &texIdx, &st);
// setup pass through color
- varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
+ varyingHandler->addPassThroughAttribute(dfPathEffect.inColor(), args.fOutputColor);
- if (dfTexEffect.matrix().hasPerspective()) {
+ if (dfPathEffect.matrix().hasPerspective()) {
// Setup position
this->writeOutputPosition(vertBuilder,
uniformHandler,
gpArgs,
- dfTexEffect.inPosition()->fName,
- dfTexEffect.matrix(),
+ dfPathEffect.inPosition()->fName,
+ dfPathEffect.matrix(),
&fMatrixUniform);
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
- dfTexEffect.inPosition()->asShaderVar(),
+ dfPathEffect.inPosition()->asShaderVar(),
args.fFPCoordTransformHandler);
} else {
// Setup position
- this->writeOutputPosition(vertBuilder, gpArgs, dfTexEffect.inPosition()->fName);
+ this->writeOutputPosition(vertBuilder, gpArgs, dfPathEffect.inPosition()->fName);
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
- dfTexEffect.inPosition()->asShaderVar(),
- dfTexEffect.matrix(),
+ dfPathEffect.inPosition()->asShaderVar(),
+ dfPathEffect.matrix(),
args.fFPCoordTransformHandler);
}
// Use highp to work around aliasing issues
fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn());
fragBuilder->codeAppend("half4 texColor;");
- append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
- texIdx, "uv", "texColor");
+ append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv",
+ "texColor");
fragBuilder->codeAppend("half distance = "
SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");");
fragBuilder->codeAppend("half afwidth;");
- bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
- kUniformScale_DistanceFieldEffectMask;
- bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
+ bool isUniformScale = (dfPathEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) ==
+ kUniformScale_DistanceFieldEffectMask;
+ bool isSimilarity = SkToBool(dfPathEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
bool isGammaCorrect =
- SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
+ SkToBool(dfPathEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag);
if (isUniformScale) {
// For uniform scale, we adjust for the effect of the transformation on the distance
// by using the length of the gradient of the t coordinate in the y direction.
@@ -589,9 +573,7 @@
class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor {
public:
- GrGLDistanceFieldLCDTextGeoProc()
- : fViewMatrix(SkMatrix::InvalidMatrix())
- , fAtlasSize({0,0}) {
+ GrGLDistanceFieldLCDTextGeoProc() : fAtlasSize({0, 0}) {
fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
}
@@ -619,18 +601,14 @@
varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor);
// Setup position
- this->writeOutputPosition(vertBuilder,
- uniformHandler,
- gpArgs,
- dfTexEffect.inPosition()->fName,
- dfTexEffect.viewMatrix(),
- &fViewMatrixUniform);
+ gpArgs->fPositionVar = dfTexEffect.inPosition()->asShaderVar();
// emit transforms
this->emitTransforms(vertBuilder,
varyingHandler,
uniformHandler,
dfTexEffect.inPosition()->asShaderVar(),
+ dfTexEffect.localMatrix(),
args.fFPCoordTransformHandler);
// set up varyings
@@ -778,13 +756,6 @@
fDistanceAdjust = wa;
}
- if (!dflcd.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dflcd.viewMatrix())) {
- fViewMatrix = dflcd.viewMatrix();
- float viewMatrix[3 * 3];
- GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
- pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
- }
-
SkASSERT(dflcd.numTextureSamplers() >= 1);
GrTexture* atlas = dflcd.textureSampler(0).peekTexture();
SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));
@@ -793,8 +764,7 @@
fAtlasSize.set(atlas->width(), atlas->height());
pdman.set2f(fAtlasSizeInvUniform, 1.0f / atlas->width(), 1.0f / atlas->height());
}
-
- this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+ this->setTransformDataHelper(dflcd.localMatrix(), pdman, &transformIter);
}
static inline void GenKey(const GrGeometryProcessor& gp,
@@ -803,15 +773,11 @@
const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>();
uint32_t key = dfTexEffect.getFlags();
- key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16;
b->add32(key);
b->add32(dfTexEffect.numTextureSamplers());
}
private:
- SkMatrix fViewMatrix;
- UniformHandle fViewMatrixUniform;
-
GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
UniformHandle fDistanceAdjustUni;
@@ -824,19 +790,22 @@
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
GrColor color,
- const SkMatrix& viewMatrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params,
DistanceAdjust distanceAdjust,
- uint32_t flags, bool usesLocalCoords)
+ uint32_t flags,
+ const SkMatrix& localMatrix)
: INHERITED(kGrDistanceFieldLCDTextGeoProc_ClassID)
, fColor(color)
- , fViewMatrix(viewMatrix)
, fDistanceAdjust(distanceAdjust)
, fFlags(flags & kLCD_DistanceFieldEffectMask)
- , fUsesLocalCoords(usesLocalCoords) {
+ , fLocalMatrix(localMatrix) {
SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
- fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
+ if (fFlags & kPerspective_DistanceFieldEffectFlag) {
+ fInPosition = &this->addVertexAttrib("inPosition", kFloat3_GrVertexAttribType);
+ } else {
+ fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
+ }
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
@@ -893,8 +862,9 @@
flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
}
flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
- return GrDistanceFieldLCDTextGeoProc::Make(GrRandomColor(d->fRandom),
- GrTest::TestMatrix(d->fRandom), proxies,
- samplerState, wa, flags, d->fRandom->nextBool());
+ GrColor color = GrRandomColor(d->fRandom);
+ SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
+ return GrDistanceFieldLCDTextGeoProc::Make(color, proxies, samplerState, wa, flags,
+ localMatrix);
}
#endif
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.h b/src/gpu/effects/GrDistanceFieldGeoProc.h
index a099382..f3fd676 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.h
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.h
@@ -19,24 +19,27 @@
enum GrDistanceFieldEffectFlags {
kSimilarity_DistanceFieldEffectFlag = 0x01, // ctm is similarity matrix
kScaleOnly_DistanceFieldEffectFlag = 0x02, // ctm has only scale and translate
- kUseLCD_DistanceFieldEffectFlag = 0x04, // use lcd text
- kBGR_DistanceFieldEffectFlag = 0x08, // lcd display has bgr order
- kPortrait_DistanceFieldEffectFlag = 0x10, // lcd display is in portrait mode (not used yet)
- kGammaCorrect_DistanceFieldEffectFlag = 0x20, // assume gamma-correct output (linear blending)
- kAliased_DistanceFieldEffectFlag = 0x40, // monochrome output
+ kPerspective_DistanceFieldEffectFlag = 0x04, // ctm has perspective (and positions are x,y,w)
+ kUseLCD_DistanceFieldEffectFlag = 0x08, // use lcd text
+ kBGR_DistanceFieldEffectFlag = 0x10, // lcd display has bgr order
+ kPortrait_DistanceFieldEffectFlag = 0x20, // lcd display is in portrait mode (not used yet)
+ kGammaCorrect_DistanceFieldEffectFlag = 0x40, // assume gamma-correct output (linear blending)
+ kAliased_DistanceFieldEffectFlag = 0x80, // monochrome output
- kInvalid_DistanceFieldEffectFlag = 0x80, // invalid state (for initialization)
+ kInvalid_DistanceFieldEffectFlag = 0x100, // invalid state (for initialization)
kUniformScale_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
kScaleOnly_DistanceFieldEffectFlag,
// The subset of the flags relevant to GrDistanceFieldA8TextGeoProc
kNonLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
kScaleOnly_DistanceFieldEffectFlag |
+ kPerspective_DistanceFieldEffectFlag |
kGammaCorrect_DistanceFieldEffectFlag |
kAliased_DistanceFieldEffectFlag,
// The subset of the flags relevant to GrDistanceFieldLCDTextGeoProc
kLCD_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
kScaleOnly_DistanceFieldEffectFlag |
+ kPerspective_DistanceFieldEffectFlag |
kUseLCD_DistanceFieldEffectFlag |
kBGR_DistanceFieldEffectFlag |
kGammaCorrect_DistanceFieldEffectFlag,
@@ -52,23 +55,22 @@
public:
static constexpr int kMaxTextures = 4;
+ /** The local matrix should be identity if local coords are not required by the GrPipeline. */
#ifdef SK_GAMMA_APPLY_TO_A8
- static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& viewMatrix,
+ static sk_sp<GrGeometryProcessor> Make(GrColor color,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params, float lum, uint32_t flags,
- bool usesLocalCoords) {
- return sk_sp<GrGeometryProcessor>(
- new GrDistanceFieldA8TextGeoProc(color, viewMatrix, proxies,
- params, lum, flags, usesLocalCoords));
+ const SkMatrix& localMatrixIfUsesLocalCoords) {
+ return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
+ color, proxies, params, lum, flags, localMatrixIfUsesLocalCoords));
}
#else
- static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& viewMatrix,
+ static sk_sp<GrGeometryProcessor> Make(GrColor color,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params, uint32_t flags,
- bool usesLocalCoords) {
- return sk_sp<GrGeometryProcessor>(
- new GrDistanceFieldA8TextGeoProc(color, viewMatrix, proxies,
- params, flags, usesLocalCoords));
+ const SkMatrix& localMatrixIfUsesLocalCoords) {
+ return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
+ color, proxies, params, flags, localMatrixIfUsesLocalCoords));
}
#endif
@@ -80,8 +82,7 @@
const Attribute* inColor() const { return fInColor; }
const Attribute* inTextureCoords() const { return fInTextureCoords; }
GrColor color() const { return fColor; }
- const SkMatrix& viewMatrix() const { return fViewMatrix; }
- bool usesLocalCoords() const { return fUsesLocalCoords; }
+ const SkMatrix& localMatrix() const { return fLocalMatrix; }
#ifdef SK_GAMMA_APPLY_TO_A8
float getDistanceAdjust() const { return fDistanceAdjust; }
#endif
@@ -94,16 +95,14 @@
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
- GrDistanceFieldA8TextGeoProc(GrColor, const SkMatrix& viewMatrix,
- const sk_sp<GrTextureProxy> proxies[kMaxTextures],
+ GrDistanceFieldA8TextGeoProc(GrColor, const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params,
#ifdef SK_GAMMA_APPLY_TO_A8
float distanceAdjust,
#endif
- uint32_t flags, bool usesLocalCoords);
+ uint32_t flags, const SkMatrix& localMatrix);
GrColor fColor;
- SkMatrix fViewMatrix;
TextureSampler fTextureSamplers[kMaxTextures];
#ifdef SK_GAMMA_APPLY_TO_A8
float fDistanceAdjust;
@@ -112,7 +111,7 @@
const Attribute* fInPosition;
const Attribute* fInColor;
const Attribute* fInTextureCoords;
- bool fUsesLocalCoords;
+ SkMatrix fLocalMatrix;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST
@@ -129,6 +128,7 @@
public:
static constexpr int kMaxTextures = 4;
+ /** The local matrix should be identity if local coords are not required by the GrPipeline. */
static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params, uint32_t flags) {
@@ -197,16 +197,13 @@
static constexpr int kMaxTextures = 4;
static sk_sp<GrGeometryProcessor> Make(GrColor color,
- const SkMatrix& viewMatrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params,
DistanceAdjust distanceAdjust,
uint32_t flags,
- bool usesLocalCoords) {
- return sk_sp<GrGeometryProcessor>(
- new GrDistanceFieldLCDTextGeoProc(color, viewMatrix, proxies,
- params, distanceAdjust,
- flags, usesLocalCoords));
+ const SkMatrix& localMatrixIfUsesLocalCoords) {
+ return sk_sp<GrGeometryProcessor>(new GrDistanceFieldLCDTextGeoProc(
+ color, proxies, params, distanceAdjust, flags, localMatrixIfUsesLocalCoords));
}
~GrDistanceFieldLCDTextGeoProc() override {}
@@ -218,9 +215,8 @@
const Attribute* inTextureCoords() const { return fInTextureCoords; }
DistanceAdjust getDistanceAdjust() const { return fDistanceAdjust; }
GrColor color() const { return fColor; }
- const SkMatrix& viewMatrix() const { return fViewMatrix; }
uint32_t getFlags() const { return fFlags; }
- bool usesLocalCoords() const { return fUsesLocalCoords; }
+ const SkMatrix& localMatrix() const { return fLocalMatrix; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
@@ -229,20 +225,18 @@
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
- GrDistanceFieldLCDTextGeoProc(GrColor, const SkMatrix& viewMatrix,
- const sk_sp<GrTextureProxy> proxies[kMaxTextures],
+ GrDistanceFieldLCDTextGeoProc(GrColor, const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const GrSamplerState& params, DistanceAdjust wa, uint32_t flags,
- bool usesLocalCoords);
+ const SkMatrix& localMatrix);
GrColor fColor;
- SkMatrix fViewMatrix;
TextureSampler fTextureSamplers[kMaxTextures];
DistanceAdjust fDistanceAdjust;
uint32_t fFlags;
const Attribute* fInPosition;
const Attribute* fInColor;
const Attribute* fInTextureCoords;
- bool fUsesLocalCoords;
+ const SkMatrix fLocalMatrix;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index 9c71042..9a768c2 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -57,40 +57,43 @@
const SkMatrix& localMatrix,
FPCoordTransformHandler* handler) {
SkASSERT(GrSLTypeIsFloatType(localCoordsVar.getType()));
- SkASSERT(2 == GrSLTypeVecLength(localCoordsVar.getType()));
+ SkASSERT(2 == GrSLTypeVecLength(localCoordsVar.getType()) ||
+ 3 == GrSLTypeVecLength(localCoordsVar.getType()));
+ bool threeComponentLocalCoords = 3 == GrSLTypeVecLength(localCoordsVar.getType());
+ SkString localCoords;
+ if (threeComponentLocalCoords) {
+ localCoords = localCoordsVar.getName();
+ } else {
+ localCoords.printf("float3(%s, 1)", localCoordsVar.c_str());
+ }
int i = 0;
while (const GrCoordTransform* coordTransform = handler->nextCoordTransform()) {
SkString strUniName;
strUniName.printf("CoordTransformMatrix_%d", i);
- GrSLType varyingType;
-
- uint32_t type = coordTransform->getMatrix().getType();
- type |= localMatrix.getType();
-
- varyingType = SkToBool(SkMatrix::kPerspective_Mask & type) ? kFloat3_GrSLType :
- kFloat2_GrSLType;
const char* uniName;
-
-
fInstalledTransforms.push_back().fHandle = uniformHandler->addUniform(kVertex_GrShaderFlag,
kFloat3x3_GrSLType,
strUniName.c_str(),
&uniName).toIndex();
+ GrSLType varyingType = kFloat2_GrSLType;
+ if (localMatrix.hasPerspective() || coordTransform->getMatrix().hasPerspective()) {
+ varyingType = kFloat3_GrSLType;
+ }
SkString strVaryingName;
strVaryingName.printf("TransformedCoords_%d", i);
-
GrGLSLVarying v(varyingType);
varyingHandler->addVarying(strVaryingName.c_str(), &v);
- SkASSERT(kFloat2_GrSLType == varyingType || kFloat3_GrSLType == varyingType);
handler->specifyCoordsForCurrCoordTransform(SkString(v.fsIn()), varyingType);
if (kFloat2_GrSLType == varyingType) {
- vb->codeAppendf("%s = (%s * float3(%s, 1)).xy;", v.vsOut(), uniName,
- localCoordsVar.c_str());
+ vb->codeAppendf("%s = (%s * %s).xy;", v.vsOut(), uniName, localCoords.c_str());
+ if (threeComponentLocalCoords) {
+ vb->codeAppendf("%s /= %s.z;", v.vsOut(), localCoords.c_str());
+ }
} else {
- vb->codeAppendf("%s = %s * float3(%s, 1);", v.vsOut(), uniName, localCoordsVar.c_str());
+ vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, localCoords.c_str());
}
++i;
}
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.h b/src/gpu/glsl/GrGLSLGeometryProcessor.h
index de1e4fd..36cc546 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.h
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.h
@@ -29,7 +29,8 @@
FPCoordTransformIter*);
// Emit transformed local coords from the vertex shader as a uniform matrix and varying per
- // coord-transform.
+ // coord-transform. localCoordsVar must be a 2- or 3-component vector. If it is 3 then it is
+ // assumed to be a 2D homogeneous coordinate.
void emitTransforms(GrGLSLVertexBuilder*,
GrGLSLVaryingHandler*,
GrGLSLUniformHandler*,
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 6743fea..d71cd92 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -6,14 +6,12 @@
*/
#include "GrAtlasTextOp.h"
-
#include "GrContext.h"
#include "GrOpFlushState.h"
#include "GrResourceProvider.h"
-
#include "SkGlyphCache.h"
#include "SkMathPriv.h"
-
+#include "SkPoint3.h"
#include "effects/GrBitmapTextGeoProc.h"
#include "effects/GrDistanceFieldGeoProc.h"
#include "text/GrAtlasGlyphCache.h"
@@ -22,6 +20,35 @@
static const int kDistanceAdjustLumShift = 5;
+void GrAtlasTextOp::init() {
+ const Geometry& geo = fGeoData[0];
+ fColor = geo.fColor;
+ SkRect bounds;
+ geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX, geo.fY);
+ // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
+ // we treat this as a set of non-AA rects rendered with a texture.
+ this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
+ if (this->usesDistanceFields()) {
+ bool isLCD = this->isLCD();
+
+ const SkMatrix& viewMatrix = geo.fViewMatrix;
+
+ fDFGPFlags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+ fDFGPFlags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
+ fDFGPFlags |= viewMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
+ fDFGPFlags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
+ fDFGPFlags |= (kAliasedDistanceField_MaskType == fMaskType)
+ ? kAliased_DistanceFieldEffectFlag
+ : 0;
+
+ if (isLCD) {
+ fDFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
+ fDFGPFlags |=
+ (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
+ }
+ }
+}
+
SkString GrAtlasTextOp::dumpInfo() const {
SkString str;
@@ -183,7 +210,7 @@
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
// TODO actually only invert if we don't have RGBA
SkMatrix localMatrix;
- if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
+ if (this->usesLocalCoords() && !fGeoData[0].fViewMatrix.invert(&localMatrix)) {
SkDebugf("Cannot invert viewmatrix\n");
return;
}
@@ -200,8 +227,10 @@
FlushInfo flushInfo;
flushInfo.fPipeline =
target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip());
+ SkDEBUGCODE(bool dfPerspective = false);
if (this->usesDistanceFields()) {
flushInfo.fGeometryProcessor = this->setupDfProcessor();
+ SkDEBUGCODE(dfPerspective = fGeoData[0].fViewMatrix.hasPerspective());
} else {
flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat,
@@ -210,7 +239,7 @@
flushInfo.fGlyphsToFlush = 0;
size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
- SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
+ SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat, dfPerspective));
int glyphCount = this->numGlyphs();
const GrBuffer* vertexBuffer;
@@ -242,9 +271,24 @@
if (args.fClipRect.isEmpty()) {
memcpy(currVertex, result.fFirstVertex, vertexBytes);
} else {
+ SkASSERT(!dfPerspective);
clip_quads(args.fClipRect, currVertex, result.fFirstVertex, vertexStride,
result.fGlyphsRegenerated);
}
+ if (this->usesDistanceFields() && !args.fViewMatrix.isIdentity()) {
+ // We always do the distance field view matrix transformation after copying rather
+ // than during blob vertex generation time in the blob as handling successive
+ // arbitrary transformations would be complicated and accumulate error.
+ if (args.fViewMatrix.hasPerspective()) {
+ auto* pos = reinterpret_cast<SkPoint3*>(currVertex);
+ args.fViewMatrix.mapHomogeneousPointsWithStride(
+ pos, pos, vertexStride, result.fGlyphsRegenerated * kVerticesPerGlyph);
+ } else {
+ auto* pos = reinterpret_cast<SkPoint*>(currVertex);
+ args.fViewMatrix.mapPointsWithStride(
+ pos, vertexStride, result.fGlyphsRegenerated * kVerticesPerGlyph);
+ }
+ }
flushInfo.fGlyphsToFlush += result.fGlyphsRegenerated;
if (!result.fFinished) {
this->flush(target, &flushInfo);
@@ -300,21 +344,25 @@
return false;
}
- if (!this->usesDistanceFields()) {
- if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
- return false;
- }
- if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
- return false;
- }
- } else {
- if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ const SkMatrix& thisFirstMatrix = fGeoData[0].fViewMatrix;
+ const SkMatrix& thatFirstMatrix = that->fGeoData[0].fViewMatrix;
+
+ if (this->usesLocalCoords() && !thisFirstMatrix.cheapEqualTo(thatFirstMatrix)) {
+ return false;
+ }
+
+ if (this->usesDistanceFields()) {
+ if (fDFGPFlags != that->fDFGPFlags) {
return false;
}
if (fLuminanceColor != that->fLuminanceColor) {
return false;
}
+ } else {
+ if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
+ return false;
+ }
}
// Keep the batch vertex buffer size below 32K so we don't have to create a special one
@@ -358,20 +406,18 @@
// TODO trying to figure out why lcd is so whack
// (see comments in GrAtlasTextContext::ComputeCanonicalColor)
sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
- const SkMatrix& viewMatrix = this->viewMatrix();
const sk_sp<GrTextureProxy>* p = fFontCache->getProxies(this->maskFormat());
bool isLCD = this->isLCD();
- // set up any flags
- uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
- flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
- flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
- flags |= (kAliasedDistanceField_MaskType == fMaskType) ? kAliased_DistanceFieldEffectFlag : 0;
+
+ SkMatrix localMatrix = SkMatrix::I();
+ if (this->usesLocalCoords()) {
+ // If this fails we'll just use I().
+ bool result = fGeoData[0].fViewMatrix.invert(&localMatrix);
+ (void)result;
+ }
// see if we need to create a new effect
if (isLCD) {
- flags |= kUseLCD_DistanceFieldEffectFlag;
- flags |= (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
-
float redCorrection = fDistanceAdjustTable->getAdjustment(
SkColorGetR(fLuminanceColor) >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
@@ -384,10 +430,8 @@
GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
redCorrection, greenCorrection, blueCorrection);
-
- return GrDistanceFieldLCDTextGeoProc::Make(this->color(), viewMatrix, p,
- GrSamplerState::ClampBilerp(), widthAdjust,
- flags, this->usesLocalCoords());
+ return GrDistanceFieldLCDTextGeoProc::Make(this->color(), p, GrSamplerState::ClampBilerp(),
+ widthAdjust, fDFGPFlags, localMatrix);
} else {
#ifdef SK_GAMMA_APPLY_TO_A8
float correction = 0;
@@ -397,13 +441,11 @@
correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
}
- return GrDistanceFieldA8TextGeoProc::Make(this->color(), viewMatrix, p,
- GrSamplerState::ClampBilerp(), correction, flags,
- this->usesLocalCoords());
+ return GrDistanceFieldA8TextGeoProc::Make(this->color(), p, GrSamplerState::ClampBilerp(),
+ correction, fDFGPFlags, localMatrix);
#else
- return GrDistanceFieldA8TextGeoProc::Make(this->color(), viewMatrix, p,
- GrSamplerState::ClampBilerp(), flags,
- this->usesLocalCoords());
+ return GrDistanceFieldA8TextGeoProc::Make(this->color(), p, GrSamplerState::ClampBilerp(),
+ fDFGPFlags, localMatrix);
#endif
}
}
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index d31a329..cb15c36 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -87,16 +87,8 @@
// init() so the op can initialize itself
Geometry& geometry() { return fGeoData[0]; }
- void init() {
- const Geometry& geo = fGeoData[0];
- fColor = geo.fColor;
- SkRect bounds;
- geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX,
- geo.fY);
- // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
- // we treat this as a set of non-AA rects rendered with a texture.
- this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
- }
+ /** Called after this->geometry() has been configured. */
+ void init();
const char* name() const override { return "AtlasTextOp"; }
@@ -187,7 +179,6 @@
inline void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const;
GrColor color() const { return fColor; }
- const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bool usesLocalCoords() const { return fUsesLocalCoords; }
int numGlyphs() const { return fNumGlyphs; }
@@ -212,6 +203,7 @@
sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
SkColor fLuminanceColor;
bool fUseGammaCorrectDistanceTable;
+ uint32_t fDFGPFlags = 0;
typedef GrMeshDrawOp INHERITED;
};
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 0b25a34..02f3ab0 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -96,7 +96,13 @@
run.fInitialized = true;
- size_t vertexStride = GetVertexStride(format);
+ bool hasW = subRun->hasWCoord();
+ // DF glyphs drawn in perspective must always have a w coord.
+ SkASSERT(hasW || !subRun->drawAsDistanceFields() || !fInitialViewMatrix.hasPerspective());
+ // Non-DF glyphs should never have a w coord.
+ SkASSERT(!hasW || subRun->drawAsDistanceFields());
+
+ size_t vertexStride = GetVertexStride(format, hasW);
subRun->setMaskFormat(format);
@@ -105,53 +111,29 @@
intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
- if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
- // V0
- SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fLeft, positions.fTop);
- SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
- *colorPtr = color;
- vertex += vertexStride;
+ // We always write the third position component used by SDFs. If it is unused it gets
+ // overwritten. Similarly, we always write the color and the blob will later overwrite it
+ // with texture coords if it is unused.
+ size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
+ // V0
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fLeft, positions.fTop, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+ vertex += vertexStride;
- // V1
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fLeft, positions.fBottom);
- colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
- *colorPtr = color;
- vertex += vertexStride;
+ // V1
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fLeft, positions.fBottom, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+ vertex += vertexStride;
- // V2
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fRight, positions.fTop);
- colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
- *colorPtr = color;
- vertex += vertexStride;
+ // V2
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fRight, positions.fTop, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+ vertex += vertexStride;
- // V3
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fRight, positions.fBottom);
- colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
- *colorPtr = color;
- } else {
- // V0
- SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fLeft, positions.fTop);
- vertex += vertexStride;
+ // V3
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fRight, positions.fBottom, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
- // V1
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fLeft, positions.fBottom);
- vertex += vertexStride;
-
- // V2
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fRight, positions.fTop);
- vertex += vertexStride;
-
- // V3
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fRight, positions.fBottom);
- }
subRun->appendVertices(vertexStride);
fGlyphs[subRun->glyphEndIndex()] = glyph;
subRun->glyphAppended();
@@ -185,6 +167,7 @@
return true;
}
+ /** This could be relaxed for blobs with only distance field glyphs. */
if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) {
return true;
}
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index 450e256..282fa98 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -17,6 +17,7 @@
#include "SkMaskFilter.h"
#include "SkOpts.h"
#include "SkPathEffect.h"
+#include "SkPoint3.h"
#include "SkRasterizer.h"
#include "SkSurfaceProps.h"
#include "SkTInternalLList.h"
@@ -53,6 +54,12 @@
static sk_sp<GrAtlasTextBlob> Make(GrMemoryPool* pool, int glyphCount, int runCount);
+ /**
+ * We currently force regeneration of a blob if old or new matrix differ in having perspective.
+ * If we ever change that then the key must contain the perspectiveness when there are distance
+ * fields as perspective distance field use 3 component vertex positions and non-perspective
+ * uses 2.
+ */
struct Key {
Key() {
sk_bzero(this, sizeof(Key));
@@ -126,12 +133,13 @@
}
// sets the last subrun of runIndex to use distance field text
- void setSubRunHasDistanceFields(int runIndex, bool hasLCD, bool isAntiAlias) {
+ void setSubRunHasDistanceFields(int runIndex, bool hasLCD, bool isAntiAlias, bool hasWCoord) {
Run& run = fRuns[runIndex];
Run::SubRunInfo& subRun = run.fSubRunInfo.back();
subRun.setUseLCDText(hasLCD);
subRun.setAntiAliased(isAntiAlias);
subRun.setDrawAsDistanceFields();
+ subRun.setHasWCoord(hasWCoord);
}
void setRunDrawAsPaths(int runIndex) {
@@ -169,13 +177,15 @@
SkGlyphCache*, const SkGlyph& skGlyph,
SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP);
- static size_t GetVertexStride(GrMaskFormat maskFormat) {
+ static size_t GetVertexStride(GrMaskFormat maskFormat, bool isDistanceFieldWithWCoord) {
switch (maskFormat) {
case kA8_GrMaskFormat:
- return kGrayTextVASize;
+ return isDistanceFieldWithWCoord ? kGrayTextDFPerspectiveVASize : kGrayTextVASize;
case kARGB_GrMaskFormat:
+ SkASSERT(!isDistanceFieldWithWCoord);
return kColorTextVASize;
default:
+ SkASSERT(!isDistanceFieldWithWCoord);
return kLCDTextVASize;
}
}
@@ -232,8 +242,10 @@
// position + local coord
static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
+ static const size_t kGrayTextDFPerspectiveVASize =
+ sizeof(SkPoint3) + sizeof(GrColor) + sizeof(SkIPoint16);
static const size_t kLCDTextVASize = kGrayTextVASize;
- static const size_t kMaxVASize = kGrayTextVASize;
+ static const size_t kMaxVASize = kGrayTextDFPerspectiveVASize;
static const int kVerticesPerGlyph = 4;
static void AssertEqual(const GrAtlasTextBlob&, const GrAtlasTextBlob&);
@@ -419,7 +431,7 @@
// This function assumes the translation will be applied before it is called again
void computeTranslation(const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
- SkScalar*transX, SkScalar* transY);
+ SkScalar* transX, SkScalar* transY);
// df properties
void setDrawAsDistanceFields() { fFlags |= kDrawAsSDF_Flag; }
@@ -432,12 +444,17 @@
fFlags = antiAliased ? fFlags | kAntiAliased_Flag : fFlags & ~kAntiAliased_Flag;
}
bool isAntiAliased() const { return SkToBool(fFlags & kAntiAliased_Flag); }
+ void setHasWCoord(bool hasW) {
+ fFlags = hasW ? (fFlags | kHasWCoord_Flag) : fFlags & ~kHasWCoord_Flag;
+ }
+ bool hasWCoord() const { return SkToBool(fFlags & kHasWCoord_Flag); }
private:
enum Flag {
kDrawAsSDF_Flag = 0x1,
kUseLCDText_Flag = 0x2,
- kAntiAliased_Flag = 0x4
+ kAntiAliased_Flag = 0x4,
+ kHasWCoord_Flag = 0x8
};
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
diff --git a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
index 28b2e0f..1be5ae3 100644
--- a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
@@ -71,8 +71,8 @@
// This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
// vertices, hence vertexStride - sizeof(SkIPoint16)
- intptr_t colorOffset = sizeof(SkPoint);
intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
+ intptr_t colorOffset = texCoordOffset - sizeof(GrColor);
// V0
if (regenPos) {
@@ -211,8 +211,9 @@
}
}
+ bool hasW = fSubRun->hasWCoord();
Result result;
- auto vertexStride = GetVertexStride(fSubRun->maskFormat());
+ auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
fCurrGlyph * kVerticesPerGlyph * vertexStride;
result.fFirstVertex = currVertex;
@@ -300,7 +301,8 @@
return this->doRegen<false, true, true, true>();
case kNoRegen: {
Result result;
- auto vertexStride = GetVertexStride(fSubRun->maskFormat());
+ bool hasW = fSubRun->hasWCoord();
+ auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
result.fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
result.fFirstVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
fCurrGlyph * kVerticesPerGlyph * vertexStride;
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index d617786..3faa0f7 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -646,13 +646,14 @@
SkTDArray<char> fallbackTxt;
SkTDArray<SkScalar> fallbackPos;
+ bool hasWCoord = viewMatrix.hasPerspective();
// Setup distance field paint and text ratio
SkScalar textRatio;
SkPaint dfPaint(paint);
this->initDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
blob->setHasDistanceField();
blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
- paint.skPaint().isAntiAlias());
+ paint.skPaint().isAntiAlias(), hasWCoord);
GrAtlasTextStrike* currStrike = nullptr;