aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2017-12-15 11:41:09 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-12-15 17:06:08 +0000
commit0215e39d7e415d0530231df6ad20d5f215c72152 (patch)
tree91fec56dae447ceb15e1743117a8ff272c60a7c3
parent29c14a760682e2c449fa043b5e8b69937cb58f3a (diff)
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>
-rw-r--r--gm/dftext_blob_persp.cpp143
-rw-r--r--gn/gm.gni1
-rw-r--r--include/core/SkMatrix.h3
-rw-r--r--src/core/SkMatrix.cpp24
-rw-r--r--src/gpu/effects/GrDistanceFieldGeoProc.cpp150
-rw-r--r--src/gpu/effects/GrDistanceFieldGeoProc.h66
-rw-r--r--src/gpu/glsl/GrGLSLGeometryProcessor.cpp33
-rw-r--r--src/gpu/glsl/GrGLSLGeometryProcessor.h3
-rw-r--r--src/gpu/ops/GrAtlasTextOp.cpp106
-rw-r--r--src/gpu/ops/GrAtlasTextOp.h14
-rw-r--r--src/gpu/text/GrAtlasTextBlob.cpp79
-rw-r--r--src/gpu/text/GrAtlasTextBlob.h29
-rw-r--r--src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp8
-rw-r--r--src/gpu/text/GrAtlasTextContext.cpp3
14 files changed, 415 insertions, 247 deletions
diff --git a/gm/dftext_blob_persp.cpp b/gm/dftext_blob_persp.cpp
new file mode 100644
index 0000000000..cfa681d984
--- /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 4d4833f6bb..62be999944 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -101,6 +101,7 @@ gm_sources = [
"$_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 e88232834b..b527ab5ef9 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -1337,6 +1337,9 @@ public:
@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 d02524068e..4851aeacf6 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1037,32 +1037,48 @@ const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
///////////////////////////////////////////////////////////////////////////////
-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 2747f55119..b8548bb4db 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 @@ public:
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 @@ public:
}
#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 @@ public:
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 @@ public:
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 @@ private:
///////////////////////////////////////////////////////////////////////////////
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 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorT
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 @@ public:
}
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 @@ public:
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
// emit attributes
- varyingHandler->emitAttributes(dfTexEffect);
+ varyingHandler->emitAttributes(dfPathEffect);
const char* atlasSizeInvName;
fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag,
@@ -345,55 +329,55 @@ public:
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 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTes
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ private:
///////////////////////////////////////////////////////////////////////////////
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 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessor
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 a09938242f..f3fd67606b 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.h
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.h
@@ -19,24 +19,27 @@ class GrInvariantOutput;
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 @@ class GrDistanceFieldA8TextGeoProc : public GrGeometryProcessor {
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 @@ public:
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 @@ public:
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 @@ private:
const Attribute* fInPosition;
const Attribute* fInColor;
const Attribute* fInTextureCoords;
- bool fUsesLocalCoords;
+ SkMatrix fLocalMatrix;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST
@@ -129,6 +128,7 @@ class GrDistanceFieldPathGeoProc : public GrGeometryProcessor {
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 @@ public:
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 @@ public:
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 @@ public:
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 9c71042413..9a768c21b4 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -57,40 +57,43 @@ void GrGLSLGeometryProcessor::emitTransforms(GrGLSLVertexBuilder* vb,
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 de1e4fd02a..36cc546c4c 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.h
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.h
@@ -29,7 +29,8 @@ protected:
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 6743feaae6..d71cd92fde 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 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
// 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 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
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 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
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 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
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,19 +344,23 @@ bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
return false;
}
- if (!this->usesDistanceFields()) {
- if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
+ 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 (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+
+ if (fLuminanceColor != that->fLuminanceColor) {
return false;
}
} else {
- if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
- return false;
- }
-
- if (fLuminanceColor != that->fLuminanceColor) {
+ if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
return false;
}
}
@@ -358,20 +406,18 @@ bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
// 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 @@ sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
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 @@ sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
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 d31a3297ec..cb15c36fb8 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -87,16 +87,8 @@ public:
// 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 @@ private:
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 @@ private:
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 0b25a34ed1..02f3ab0a21 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -96,7 +96,13 @@ void GrAtlasTextBlob::appendGlyph(int runIndex,
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 @@ void GrAtlasTextBlob::appendGlyph(int runIndex,
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;
-
- // V1
- position = reinterpret_cast<SkPoint*>(vertex);
- position->set(positions.fLeft, positions.fBottom);
- colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
- *colorPtr = 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;
-
- // 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;
-
- // 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);
- }
+ // 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
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fLeft, positions.fBottom, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+ vertex += vertexStride;
+
+ // V2
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fRight, positions.fTop, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+ vertex += vertexStride;
+
+ // V3
+ *reinterpret_cast<SkPoint3*>(vertex) = {positions.fRight, positions.fBottom, 1.f};
+ *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+
subRun->appendVertices(vertexStride);
fGlyphs[subRun->glyphEndIndex()] = glyph;
subRun->glyphAppended();
@@ -185,6 +167,7 @@ bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint,
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 450e256d38..282fa98141 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 @@ public:
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 @@ public:
}
// 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 @@ public:
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 @@ public:
// 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 @@ private:
// 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 @@ private:
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 28b2e0f0c0..1be5ae330c 100644
--- a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
@@ -71,8 +71,8 @@ inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStri
// 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 @@ Regenerator::Result Regenerator::doRegen() {
}
}
+ 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 @@ Regenerator::Result Regenerator::regenerate() {
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 d617786944..3faa0f7b87 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -646,13 +646,14 @@ void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex,
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;