Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(145)

Unified Diff: src/gpu/GrAtlasTextContext.cpp

Issue 1082843002: Initial CL to add distance field support to GrAtlasTextContext (Closed) Base URL: https://skia.googlesource.com/skia.git@attryst2
Patch Set: windows warnigns Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/GrAtlasTextContext.h ('k') | src/gpu/GrContext.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/GrAtlasTextContext.cpp
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
index f2e993aa7eb04106083709af5b7305567b89a873..34c691a9d718687bb0ae8c5991005ed256f33acb 100644
--- a/src/gpu/GrAtlasTextContext.cpp
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -20,6 +20,8 @@
#include "SkAutoKern.h"
#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDistanceFieldGen.h"
#include "SkDraw.h"
#include "SkDrawFilter.h"
#include "SkDrawProcs.h"
@@ -33,7 +35,7 @@
#include "SkTextMapStateProc.h"
#include "effects/GrBitmapTextGeoProc.h"
-#include "effects/GrSimpleTextureEffect.h"
+#include "effects/GrDistanceFieldGeoProc.h"
namespace {
static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
@@ -43,6 +45,16 @@ static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
+static const int kMinDFFontSize = 18;
+static const int kSmallDFFontSize = 32;
+static const int kSmallDFFontLimit = 32;
+static const int kMediumDFFontSize = 72;
+static const int kMediumDFFontLimit = 72;
+static const int kLargeDFFontSize = 162;
+
+SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
+static const int kDistanceAdjustLumShift = 5;
+
static const int kVerticesPerGlyph = 4;
static const int kIndicesPerGlyph = 6;
@@ -57,30 +69,134 @@ static size_t get_vertex_stride(GrMaskFormat maskFormat) {
}
}
+static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
+ SkASSERT(maskFormat == kA8_GrMaskFormat);
+ if (useLCDText) {
+ return kLCDTextVASize;
+ } else {
+ return kGrayTextVASize;
+ }
+}
+
+static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
+ unsigned r = SkColorGetR(c);
+ unsigned g = SkColorGetG(c);
+ unsigned b = SkColorGetB(c);
+ return GrColorPackRGBA(r, g, b, 0xff);
+}
+
};
// TODO
-// Gamma slotting to preserve color
-// Better reuse on regeneration
-// Telemetry tests
-// possibly consider having a regeneration ratio on the textblob itself for animated textblobs
+// Distance field text in textblobs
GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
SkGpuDevice* gpuDevice,
- const SkDeviceProperties& properties)
- : INHERITED(context, gpuDevice, properties) {
+ const SkDeviceProperties& properties,
+ bool enableDistanceFields)
+ : INHERITED(context, gpuDevice, properties)
+ , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
// We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
// vertexStride
SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
vertex_attribute_changed);
fCurrStrike = NULL;
fCache = context->getTextBlobCache();
+
+#if SK_FORCE_DISTANCE_FIELD_TEXT
+ fEnableDFRendering = true;
+#else
+ fEnableDFRendering = enableDistanceFields;
+#endif
+}
+
+void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
+
+ // This is used for an approximation of the mask gamma hack, used by raster and bitmap
+ // text. The mask gamma hack is based off of guessing what the blend color is going to
+ // be, and adjusting the mask so that when run through the linear blend will
+ // produce the value closest to the desired result. However, in practice this means
+ // that the 'adjusted' mask is just increasing or decreasing the coverage of
+ // the mask depending on what it is thought it will blit against. For black (on
+ // assumed white) this means that coverages are decreased (on a curve). For white (on
+ // assumed black) this means that coverages are increased (on a a curve). At
+ // middle (perceptual) gray (which could be blit against anything) the coverages
+ // remain the same.
+ //
+ // The idea here is that instead of determining the initial (real) coverage and
+ // then adjusting that coverage, we determine an adjusted coverage directly by
+ // essentially manipulating the geometry (in this case, the distance to the glyph
+ // edge). So for black (on assumed white) this thins a bit; for white (on
+ // assumed black) this fake bolds the geometry a bit.
+ //
+ // The distance adjustment is calculated by determining the actual coverage value which
+ // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
+ // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
+ // actual edge. So by subtracting this distance adjustment and computing without the
+ // the coverage adjustment we should get 0.5 coverage at the same point.
+ //
+ // This has several implications:
+ // For non-gray lcd smoothed text, each subpixel essentially is using a
+ // slightly different geometry.
+ //
+ // For black (on assumed white) this may not cover some pixels which were
+ // previously covered; however those pixels would have been only slightly
+ // covered and that slight coverage would have been decreased anyway. Also, some pixels
+ // which were previously fully covered may no longer be fully covered.
+ //
+ // For white (on assumed black) this may cover some pixels which weren't
+ // previously covered at all.
+
+ int width, height;
+ size_t size;
+
+#ifdef SK_GAMMA_CONTRAST
+ SkScalar contrast = SK_GAMMA_CONTRAST;
+#else
+ SkScalar contrast = 0.5f;
+#endif
+ SkScalar paintGamma = gamma;
+ SkScalar deviceGamma = gamma;
+
+ size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
+ &width, &height);
+
+ SkASSERT(kExpectedDistanceAdjustTableSize == height);
+ fTable = SkNEW_ARRAY(SkScalar, height);
+
+ SkAutoTArray<uint8_t> data((int)size);
+ SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
+
+ // find the inverse points where we cross 0.5
+ // binsearch might be better, but we only need to do this once on creation
+ for (int row = 0; row < height; ++row) {
+ uint8_t* rowPtr = data.get() + row*width;
+ for (int col = 0; col < width - 1; ++col) {
+ if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
+ // compute point where a mask value will give us a result of 0.5
+ float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
+ float borderAlpha = (col + interp) / 255.f;
+
+ // compute t value for that alpha
+ // this is an approximate inverse for smoothstep()
+ float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
+
+ // compute distance which gives us that t value
+ const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
+ float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
+
+ fTable[row] = d;
+ break;
+ }
+ }
+ }
}
GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
SkGpuDevice* gpuDevice,
- const SkDeviceProperties& props) {
- return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props));
+ const SkDeviceProperties& props,
+ bool enableDistanceFields) {
+ return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
}
bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
@@ -88,7 +204,8 @@ bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
const GrPaint&,
const SkPaint& skPaint,
const SkMatrix& viewMatrix) {
- return !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
+ return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
+ !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
}
GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
@@ -203,8 +320,9 @@ bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTr
inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
const SkPaint& skPaint,
- const SkMatrix& viewMatrix) {
- skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, &viewMatrix, false);
+ const SkMatrix* viewMatrix,
+ bool noGamma) {
+ skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
}
@@ -285,6 +403,41 @@ void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
clip, viewMatrix, clipBounds, x, y, transX, transY);
}
+inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
+ const SkMatrix& viewMatrix) {
+ // TODO: support perspective (need getMaxScale replacement)
+ if (viewMatrix.hasPerspective()) {
+ return false;
+ }
+
+ SkScalar maxScale = viewMatrix.getMaxScale();
+ SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
+ // Hinted text looks far better at small resolutions
+ // Scaling up beyond 2x yields undesireable artifacts
+ if (scaledTextSize < kMinDFFontSize || scaledTextSize > 2 * kLargeDFFontSize) {
+ return false;
+ }
+
+ if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
+ scaledTextSize < kLargeDFFontSize) {
+ return false;
+ }
+
+ // rasterizers and mask filters modify alpha, which doesn't
+ // translate well to distance
+ if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
+ !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
+ return false;
+ }
+
+ // TODO: add some stroking support
+ if (skPaint.getStyle() != SkPaint::kFill_Style) {
+ return false;
+ }
+
+ return true;
+}
+
void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
const SkPaint& skPaint, GrColor color,
const SkMatrix& viewMatrix,
@@ -313,7 +466,8 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
- SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, viewMatrix);
+ SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
+ false);
// setup vertex / glyphIndex for the new run
if (run > 0) {
@@ -335,19 +489,19 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
switch (it.positioning()) {
case SkTextBlob::kDefault_Positioning:
- this->internalDrawText(cacheBlob, run, cache, runPaint, color, viewMatrix,
- (const char *)it.glyphs(), textLen,
- x + offset.x(), y + offset.y(), clipRect);
+ this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+ (const char *)it.glyphs(), textLen,
+ x + offset.x(), y + offset.y(), clipRect);
break;
case SkTextBlob::kHorizontal_Positioning:
- this->internalDrawPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
- (const char*)it.glyphs(), textLen, it.pos(), 1,
- SkPoint::Make(x, y + offset.y()), clipRect);
+ this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+ (const char*)it.glyphs(), textLen, it.pos(), 1,
+ SkPoint::Make(x, y + offset.y()), clipRect);
break;
case SkTextBlob::kFull_Positioning:
- this->internalDrawPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
- (const char*)it.glyphs(), textLen, it.pos(), 2,
- SkPoint::Make(x, y), clipRect);
+ this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+ (const char*)it.glyphs(), textLen, it.pos(), 2,
+ SkPoint::Make(x, y), clipRect);
break;
}
@@ -360,35 +514,165 @@ void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
}
}
+inline void GrAtlasTextContext::initDistanceFieldPaint(SkPaint* skPaint, SkScalar* textRatio,
+ const SkMatrix& viewMatrix) {
+ // getMaxScale doesn't support perspective, so neither do we at the moment
+ SkASSERT(!viewMatrix.hasPerspective());
+ SkScalar maxScale = viewMatrix.getMaxScale();
+ SkScalar textSize = skPaint->getTextSize();
+ SkScalar scaledTextSize = textSize;
+ // if we have non-unity scale, we need to choose our base text size
+ // based on the SkPaint's text size multiplied by the max scale factor
+ // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
+ if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
+ scaledTextSize *= maxScale;
+ }
+
+ if (scaledTextSize <= kSmallDFFontLimit) {
+ *textRatio = textSize / kSmallDFFontSize;
+ skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
+ } else if (scaledTextSize <= kMediumDFFontLimit) {
+ *textRatio = textSize / kMediumDFFontSize;
+ skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
+ } else {
+ *textRatio = textSize / kLargeDFFontSize;
+ skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
+ }
+
+ skPaint->setLCDRenderText(false);
+ skPaint->setAutohinted(false);
+ skPaint->setHinting(SkPaint::kNormal_Hinting);
+ skPaint->setSubpixelText(true);
+}
+
+inline void GrAtlasTextContext::fallbackDrawPosText(GrRenderTarget* rt, const GrClip& clip,
+ const GrPaint& paint,
+ const SkPaint& skPaint,
+ const SkMatrix& viewMatrix,
+ const SkTDArray<char>& fallbackTxt,
+ const SkTDArray<SkScalar>& fallbackPos,
+ int scalarsPerPosition,
+ const SkPoint& offset,
+ const SkIRect& clipRect) {
+ int glyphCount = fallbackTxt.count();
+ SkASSERT(glyphCount);
+ // TODO currently we have to create a whole new blob for fallback text. This is because
+ // they have a different descriptor and we currently only have one descriptor per run.
+ // We should fix this and allow an override descriptor on each subrun
+ SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+ blob->fViewMatrix = viewMatrix;
+
+ SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+ this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix,
+ fallbackTxt.begin(), fallbackTxt.count(),
+ fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
+ SkGlyphCache::AttachCache(cache);
+ this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+}
+
+inline GrAtlasTextContext::BitmapTextBlob*
+GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
+ const SkMatrix& viewMatrix, SkGlyphCache** cache,
+ SkPaint* dfPaint, SkScalar* textRatio) {
+ BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
+
+ *dfPaint = origPaint;
+ this->initDistanceFieldPaint(dfPaint, textRatio, viewMatrix);
+ blob->fViewMatrix = viewMatrix;
+ blob->fRuns[0].fSubRunInfo.back().fUseLCDText = origPaint.isLCDRenderText();
+ blob->fRuns[0].fSubRunInfo.back().fDrawAsDistanceFields = true;
+
+ *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
+ return blob;
+}
+
void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
const GrPaint& paint, const SkPaint& skPaint,
const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
int glyphCount = skPaint.countText(text, byteLength);
- SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
- blob->fViewMatrix = viewMatrix;
- blob->fX = x;
- blob->fY = y;
-
SkIRect clipRect;
clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
// setup cache
- SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
- this->internalDrawText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, byteLength,
- x, y, clipRect);
- SkGlyphCache::AttachCache(cache);
+ if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+ SkPaint dfPaint;
+ SkScalar textRatio;
+ SkGlyphCache* cache;
+ SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
+ &dfPaint, &textRatio));
+
+ SkTDArray<char> fallbackTxt;
+ SkTDArray<SkScalar> fallbackPos;
+ SkPoint offset;
+ this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+ byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
+ &offset, skPaint);
+ SkGlyphCache::AttachCache(cache);
+ this->flush(fContext->getTextTarget(), blob, rt, dfPaint, paint, clip);
+ if (fallbackTxt.count()) {
+ this->fallbackDrawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+ fallbackPos, 2, offset, clipRect);
+ }
+ } else {
+ SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+ blob->fViewMatrix = viewMatrix;
- this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
+ SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+ this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+ byteLength, x, y, clipRect);
+ SkGlyphCache::AttachCache(cache);
+ this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+ }
}
-void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
- SkGlyphCache* cache, const SkPaint& skPaint,
- GrColor color,
- const SkMatrix& viewMatrix,
- const char text[], size_t byteLength,
- SkScalar x, SkScalar y, const SkIRect& clipRect) {
+void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
+ const GrPaint& paint, const SkPaint& skPaint,
+ const SkMatrix& viewMatrix,
+ const char text[], size_t byteLength,
+ const SkScalar pos[], int scalarsPerPosition,
+ const SkPoint& offset, const SkIRect& regionClipBounds) {
+ int glyphCount = skPaint.countText(text, byteLength);
+
+ SkIRect clipRect;
+ clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
+
+ if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+ SkPaint dfPaint;
+ SkScalar textRatio;
+ SkGlyphCache* cache;
+ SkAutoTUnref<BitmapTextBlob> blob(this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache,
+ &dfPaint, &textRatio));
+
+ SkTDArray<char> fallbackTxt;
+ SkTDArray<SkScalar> fallbackPos;
+ this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+ byteLength, pos, scalarsPerPosition, offset, clipRect,
+ textRatio, &fallbackTxt, &fallbackPos);
+ SkGlyphCache::AttachCache(cache);
+ this->flush(fContext->getTextTarget(), blob, rt, dfPaint, paint, clip);
+ if (fallbackTxt.count()) {
+ this->fallbackDrawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+ fallbackPos, scalarsPerPosition, offset, clipRect);
+ }
+ } else {
+ SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
+ blob->fViewMatrix = viewMatrix;
+ SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+ this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+ byteLength, pos, scalarsPerPosition, offset, clipRect);
+ SkGlyphCache::AttachCache(cache);
+ this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip);
+ }
+}
+
+void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
+ SkGlyphCache* cache, const SkPaint& skPaint,
+ GrColor color,
+ const SkMatrix& viewMatrix,
+ const char text[], size_t byteLength,
+ SkScalar x, SkScalar y, const SkIRect& clipRect) {
SkASSERT(byteLength == 0 || text != NULL);
// nothing to draw
@@ -456,17 +740,17 @@ void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
fx += autokern.adjust(glyph);
if (glyph.fWidth) {
- this->appendGlyph(blob,
- runIndex,
- GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed(),
- GrGlyph::kCoverage_MaskStyle),
- Sk48Dot16FloorToInt(fx),
- Sk48Dot16FloorToInt(fy),
- color,
- fontScaler,
- clipRect);
+ this->bmpAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kCoverage_MaskStyle),
+ Sk48Dot16FloorToInt(fx),
+ Sk48Dot16FloorToInt(fy),
+ color,
+ fontScaler,
+ clipRect);
}
fx += glyph.fAdvanceX;
@@ -474,35 +758,13 @@ void GrAtlasTextContext::internalDrawText(BitmapTextBlob* blob, int runIndex,
}
}
-void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
- const GrPaint& paint, const SkPaint& skPaint,
- const SkMatrix& viewMatrix,
- const char text[], size_t byteLength,
- const SkScalar pos[], int scalarsPerPosition,
- const SkPoint& offset, const SkIRect& regionClipBounds) {
- int glyphCount = skPaint.countText(text, byteLength);
- SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
- blob->fViewMatrix = viewMatrix;
-
- SkIRect clipRect;
- clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
-
- // setup cache
- SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix);
- this->internalDrawPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
- byteLength, pos, scalarsPerPosition, offset, clipRect);
- SkGlyphCache::AttachCache(cache);
-
- this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, viewMatrix);
-}
-
-void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
- SkGlyphCache* cache, const SkPaint& skPaint,
- GrColor color,
- const SkMatrix& viewMatrix,
- const char text[], size_t byteLength,
- const SkScalar pos[], int scalarsPerPosition,
- const SkPoint& offset, const SkIRect& clipRect) {
+void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
+ SkGlyphCache* cache, const SkPaint& skPaint,
+ GrColor color,
+ const SkMatrix& viewMatrix,
+ const char text[], size_t byteLength,
+ const SkScalar pos[], int scalarsPerPosition,
+ const SkPoint& offset, const SkIRect& clipRect) {
SkASSERT(byteLength == 0 || text != NULL);
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
@@ -548,17 +810,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
fx & fxMask, fy & fyMask);
if (glyph.fWidth) {
- this->appendGlyph(blob,
- runIndex,
- GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed(),
- GrGlyph::kCoverage_MaskStyle),
- Sk48Dot16FloorToInt(fx),
- Sk48Dot16FloorToInt(fy),
- color,
- fontScaler,
- clipRect);
+ this->bmpAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kCoverage_MaskStyle),
+ Sk48Dot16FloorToInt(fx),
+ Sk48Dot16FloorToInt(fy),
+ color,
+ fontScaler,
+ clipRect);
}
pos += scalarsPerPosition;
}
@@ -586,17 +848,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
SkASSERT(prevAdvY == glyph.fAdvanceY);
SkASSERT(glyph.fWidth);
- this->appendGlyph(blob,
- runIndex,
- GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed(),
- GrGlyph::kCoverage_MaskStyle),
- Sk48Dot16FloorToInt(fx),
- Sk48Dot16FloorToInt(fy),
- color,
- fontScaler,
- clipRect);
+ this->bmpAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kCoverage_MaskStyle),
+ Sk48Dot16FloorToInt(fx),
+ Sk48Dot16FloorToInt(fy),
+ color,
+ fontScaler,
+ clipRect);
}
pos += scalarsPerPosition;
}
@@ -614,17 +876,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
- this->appendGlyph(blob,
- runIndex,
- GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed(),
- GrGlyph::kCoverage_MaskStyle),
- Sk48Dot16FloorToInt(fx),
- Sk48Dot16FloorToInt(fy),
- color,
- fontScaler,
- clipRect);
+ this->bmpAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kCoverage_MaskStyle),
+ Sk48Dot16FloorToInt(fx),
+ Sk48Dot16FloorToInt(fy),
+ color,
+ fontScaler,
+ clipRect);
}
pos += scalarsPerPosition;
}
@@ -642,17 +904,17 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
- this->appendGlyph(blob,
- runIndex,
- GrGlyph::Pack(glyph.getGlyphID(),
- glyph.getSubXFixed(),
- glyph.getSubYFixed(),
- GrGlyph::kCoverage_MaskStyle),
- Sk48Dot16FloorToInt(fx),
- Sk48Dot16FloorToInt(fy),
- color,
- fontScaler,
- clipRect);
+ this->bmpAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kCoverage_MaskStyle),
+ Sk48Dot16FloorToInt(fx),
+ Sk48Dot16FloorToInt(fy),
+ color,
+ fontScaler,
+ clipRect);
}
pos += scalarsPerPosition;
}
@@ -660,15 +922,184 @@ void GrAtlasTextContext::internalDrawPosText(BitmapTextBlob* blob, int runIndex,
}
}
-void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph::PackedID packed,
- int vx, int vy, GrColor color, GrFontScaler* scaler,
- const SkIRect& clipRect) {
- if (NULL == fCurrStrike) {
+
+void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
+ SkGlyphCache* cache, const SkPaint& skPaint,
+ GrColor color,
+ const SkMatrix& viewMatrix,
+ const char text[], size_t byteLength,
+ SkScalar x, SkScalar y, const SkIRect& clipRect,
+ SkScalar textRatio,
+ SkTDArray<char>* fallbackTxt,
+ SkTDArray<SkScalar>* fallbackPos,
+ SkPoint* offset,
+ const SkPaint& origPaint) {
+ SkASSERT(byteLength == 0 || text != NULL);
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0) {
+ return;
+ }
+
+ SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
+ SkAutoDescriptor desc;
+ origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
+ SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
+ desc.getDesc());
+
+ SkTArray<SkScalar> positions;
+
+ const char* textPtr = text;
+ SkFixed stopX = 0;
+ SkFixed stopY = 0;
+ SkFixed origin = 0;
+ switch (origPaint.getTextAlign()) {
+ case SkPaint::kRight_Align: origin = SK_Fixed1; break;
+ case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
+ case SkPaint::kLeft_Align: origin = 0; break;
+ }
+
+ SkAutoKern autokern;
+ const char* stop = text + byteLength;
+ while (textPtr < stop) {
+ // don't need x, y here, since all subpixel variants will have the
+ // same advance
+ const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
+
+ SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
+ positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
+
+ SkFixed height = glyph.fAdvanceY;
+ positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
+
+ stopX += width;
+ stopY += height;
+ }
+ SkASSERT(textPtr == stop);
+
+ // now adjust starting point depending on alignment
+ SkScalar alignX = SkFixedToScalar(stopX);
+ SkScalar alignY = SkFixedToScalar(stopY);
+ if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
+ alignX = SkScalarHalf(alignX);
+ alignY = SkScalarHalf(alignY);
+ } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
+ alignX = 0;
+ alignY = 0;
+ }
+ x -= alignX;
+ y -= alignY;
+ *offset = SkPoint::Make(x, y);
+
+ this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
+ positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
+ fallbackPos);
+ SkGlyphCache::AttachCache(origPaintCache);
+}
+
+void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
+ SkGlyphCache* cache, const SkPaint& skPaint,
+ GrColor color,
+ const SkMatrix& viewMatrix,
+ const char text[], size_t byteLength,
+ const SkScalar pos[], int scalarsPerPosition,
+ const SkPoint& offset, const SkIRect& clipRect,
+ SkScalar textRatio,
+ SkTDArray<char>* fallbackTxt,
+ SkTDArray<SkScalar>* fallbackPos) {
+
+ SkASSERT(byteLength == 0 || text != NULL);
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0) {
+ return;
+ }
+
+ fCurrStrike = NULL;
+
+ SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+ GrFontScaler* fontScaler = GetGrFontScaler(cache);
+
+ const char* stop = text + byteLength;
+
+ if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
+ while (text < stop) {
+ const char* lastText = text;
+ // the last 2 parameters are ignored
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ SkScalar x = offset.x() + pos[0];
+ SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+ if (!this->dfAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kDistance_MaskStyle),
+ x, y, color, fontScaler, clipRect,
+ textRatio, viewMatrix)) {
+ // couldn't append, send to fallback
+ fallbackTxt->append(SkToInt(text-lastText), lastText);
+ *fallbackPos->append() = pos[0];
+ if (2 == scalarsPerPosition) {
+ *fallbackPos->append() = pos[1];
+ }
+ }
+ }
+ pos += scalarsPerPosition;
+ }
+ } else {
+ SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
+ : SK_Scalar1;
+ while (text < stop) {
+ const char* lastText = text;
+ // the last 2 parameters are ignored
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ SkScalar x = offset.x() + pos[0];
+ SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+ SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
+ SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
+
+ if (!this->dfAppendGlyph(blob,
+ runIndex,
+ GrGlyph::Pack(glyph.getGlyphID(),
+ glyph.getSubXFixed(),
+ glyph.getSubYFixed(),
+ GrGlyph::kDistance_MaskStyle),
+ x - advanceX, y - advanceY, color,
+ fontScaler,
+ clipRect,
+ textRatio,
+ viewMatrix)) {
+ // couldn't append, send to fallback
+ fallbackTxt->append(SkToInt(text-lastText), lastText);
+ *fallbackPos->append() = pos[0];
+ if (2 == scalarsPerPosition) {
+ *fallbackPos->append() = pos[1];
+ }
+ }
+ }
+ pos += scalarsPerPosition;
+ }
+ }
+}
+
+void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
+ GrGlyph::PackedID packed,
+ int vx, int vy, GrColor color, GrFontScaler* scaler,
+ const SkIRect& clipRect) {
+ if (!fCurrStrike) {
fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
}
GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
- if (NULL == glyph || glyph->fBounds.isEmpty()) {
+ if (!glyph || glyph->fBounds.isEmpty()) {
return;
}
@@ -693,17 +1124,7 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
// If the glyph is too large we fall back to paths
if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
- if (NULL == glyph->fPath) {
- SkPath* path = SkNEW(SkPath);
- if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
- // flag the glyph as being dead?
- SkDELETE(path);
- return;
- }
- glyph->fPath = path;
- }
- SkASSERT(glyph->fPath);
- blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, vy));
+ this->appendGlyphPath(blob, glyph, scaler, vx, vy);
return;
}
@@ -724,8 +1145,6 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
}
run.fInitialized = true;
- subRun->fMaskFormat = format;
- blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
size_t vertexStride = get_vertex_stride(format);
@@ -734,24 +1153,118 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
r.fTop = SkIntToScalar(y);
r.fRight = r.fLeft + SkIntToScalar(width);
r.fBottom = r.fTop + SkIntToScalar(height);
+ subRun->fMaskFormat = format;
+ this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
+ packed);
+}
+
+bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
+ GrGlyph::PackedID packed,
+ SkScalar sx, SkScalar sy, GrColor color,
+ GrFontScaler* scaler,
+ const SkIRect& clipRect,
+ SkScalar textRatio, const SkMatrix& viewMatrix) {
+ if (!fCurrStrike) {
+ fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
+ }
+
+ GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+ if (!glyph || glyph->fBounds.isEmpty()) {
+ return true;
+ }
+
+ // fallback to color glyph support
+ if (kA8_GrMaskFormat != glyph->fMaskFormat) {
+ return false;
+ }
+
+ SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
+ SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
+ SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
+ SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
+
+ SkScalar scale = textRatio;
+ dx *= scale;
+ dy *= scale;
+ width *= scale;
+ height *= scale;
+ sx += dx;
+ sy += dy;
+ SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
+
+#if 0
+ // check if we clipped out
+ SkRect dstRect;
+ viewMatrix.mapRect(&dstRect, glyphRect);
+ if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
+ SkScalarTruncToInt(dstRect.top()),
+ SkScalarTruncToInt(dstRect.right()),
+ SkScalarTruncToInt(dstRect.bottom()))) {
+ return true;
+ }
+#endif
+
+ // TODO combine with the above
+ // If the glyph is too large we fall back to paths
+ if (fCurrStrike->glyphTooLargeForAtlas(glyph)) {
+ this->appendGlyphPath(blob, glyph, scaler, SkScalarRoundToInt(sx - dx),
+ SkScalarRoundToInt(sy - dy));
+ return true;
+ }
+
+ Run& run = blob->fRuns[runIndex];
+
+ PerSubRunInfo* subRun = &run.fSubRunInfo.back();
+ SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
+ subRun->fMaskFormat = kA8_GrMaskFormat;
+
+ size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
+
+ bool useColorVerts = !subRun->fUseLCDText;
+ this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
+ packed);
+ return true;
+}
+
+inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
+ GrFontScaler* scaler, int x, int y) {
+ if (NULL == glyph->fPath) {
+ SkPath* path = SkNEW(SkPath);
+ if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+ // flag the glyph as being dead?
+ SkDELETE(path);
+ return;
+ }
+ glyph->fPath = path;
+ }
+ SkASSERT(glyph->fPath);
+ blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
+}
- run.fVertexBounds.joinNonEmptyArg(r);
- run.fColor = color;
+inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
+ Run::SubRunInfo* subRun,
+ const SkRect& positions, GrColor color,
+ size_t vertexStride, bool useVertexColor,
+ GrGlyph::PackedID packed) {
+ blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed;
+ run->fVertexBounds.joinNonEmptyArg(positions);
+ run->fColor = color;
intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
// V0
SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
- position->set(r.fLeft, r.fTop);
- if (kA8_GrMaskFormat == format) {
+ position->set(positions.fLeft, positions.fTop);
+ if (useVertexColor) {
SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
*colorPtr = color;
}
vertex += vertexStride;
+
// V1
position = reinterpret_cast<SkPoint*>(vertex);
- position->set(r.fLeft, r.fBottom);
- if (kA8_GrMaskFormat == format) {
+ position->set(positions.fLeft, positions.fBottom);
+ if (useVertexColor) {
SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
*colorPtr = color;
}
@@ -759,8 +1272,8 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
// V2
position = reinterpret_cast<SkPoint*>(vertex);
- position->set(r.fRight, r.fBottom);
- if (kA8_GrMaskFormat == format) {
+ position->set(positions.fRight, positions.fBottom);
+ if (useVertexColor) {
SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
*colorPtr = color;
}
@@ -768,8 +1281,8 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
// V3
position = reinterpret_cast<SkPoint*>(vertex);
- position->set(r.fRight, r.fTop);
- if (kA8_GrMaskFormat == format) {
+ position->set(positions.fRight, positions.fTop);
+ if (useVertexColor) {
SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
*colorPtr = color;
}
@@ -780,6 +1293,7 @@ void GrAtlasTextContext::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGlyph
class BitmapTextBatch : public GrBatch {
public:
+ typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
typedef GrAtlasTextContext::BitmapTextBlob Blob;
typedef Blob::Run Run;
typedef Run::SubRunInfo TextInfo;
@@ -797,6 +1311,15 @@ public:
return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
}
+ static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
+ GrBatchFontCache* fontCache,
+ DistanceAdjustTable* distanceAdjustTable,
+ SkColor filteredColor, bool useLCDText,
+ bool useBGR, float gamma) {
+ return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
+ filteredColor, useLCDText, useBGR, gamma));
+ }
+
const char* name() const override { return "BitmapTextBatch"; }
void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
@@ -808,18 +1331,29 @@ public:
}
void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
- if (kARGB_GrMaskFormat != fMaskFormat) {
- if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
+ if (!fUseDistanceFields) {
+ // Bitmap Text
+ if (kARGB_GrMaskFormat != fMaskFormat) {
+ if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
+ out->setUnknownSingleComponent();
+ } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
+ out->setUnknownOpaqueFourComponents();
+ out->setUsingLCDCoverage();
+ } else {
+ out->setUnknownFourComponents();
+ out->setUsingLCDCoverage();
+ }
+ } else {
+ out->setKnownSingleComponent(0xff);
+ }
+ } else {
+ // Distance fields
+ if (!fUseLCDText) {
out->setUnknownSingleComponent();
- } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
- out->setUnknownOpaqueFourComponents();
- out->setUsingLCDCoverage();
} else {
out->setUnknownFourComponents();
out->setUsingLCDCoverage();
}
- } else {
- out->setKnownSingleComponent(0xff);
}
}
@@ -852,20 +1386,27 @@ public:
return;
}
- GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
-
- // This will be ignored in the non A8 case
- bool opaqueVertexColors = GrColorIsOpaque(this->color());
- SkAutoTUnref<const GrGeometryProcessor> gp(
- GrBitmapTextGeoProc::Create(this->color(),
- texture,
- params,
- fMaskFormat,
- opaqueVertexColors,
- localMatrix));
+ SkAutoTUnref<const GrGeometryProcessor> gp;
+ if (fUseDistanceFields) {
+ gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
+ texture));
+ } else {
+ GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
+
+ // This will be ignored in the non A8 case
+ bool opaqueVertexColors = GrColorIsOpaque(this->color());
+ gp.reset(GrBitmapTextGeoProc::Create(this->color(),
+ texture,
+ params,
+ fMaskFormat,
+ opaqueVertexColors,
+ localMatrix));
+ }
size_t vertexStride = gp->getVertexStride();
- SkASSERT(vertexStride == get_vertex_stride(fMaskFormat));
+ SkASSERT(vertexStride == (fUseDistanceFields ?
+ get_vertex_stride_df(fMaskFormat, fUseLCDText) :
+ get_vertex_stride(fMaskFormat)));
this->initDraw(batchTarget, gp, pipeline);
@@ -908,7 +1449,12 @@ public:
uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
- bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
+ bool regenerateColors;
+ if (fUseDistanceFields) {
+ regenerateColors = fUseLCDText && run.fColor != args.fColor;
+ } else {
+ regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
+ }
bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
@@ -1029,15 +1575,34 @@ public:
}
private:
- BitmapTextBatch(GrMaskFormat maskFormat,
- int glyphCount, GrBatchFontCache* fontCache)
+ BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
+ : fMaskFormat(maskFormat)
+ , fPixelConfig(fontCache->getPixelConfig(maskFormat))
+ , fFontCache(fontCache)
+ , fUseDistanceFields(false) {
+ this->initClassID<BitmapTextBatch>();
+ fBatch.fNumGlyphs = glyphCount;
+ fInstanceCount = 1;
+ fAllocatedCount = kMinAllocated;
+ }
+
+ BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
+ DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
+ bool useLCDText, bool useBGR, float gamma)
: fMaskFormat(maskFormat)
, fPixelConfig(fontCache->getPixelConfig(maskFormat))
- , fFontCache(fontCache) {
+ , fFontCache(fontCache)
+ , fDistanceAdjustTable(SkRef(distanceAdjustTable))
+ , fFilteredColor(filteredColor)
+ , fUseDistanceFields(true)
+ , fUseLCDText(useLCDText)
+ , fUseBGR(useBGR)
+ , fGamma(gamma) {
this->initClassID<BitmapTextBatch>();
fBatch.fNumGlyphs = glyphCount;
fInstanceCount = 1;
fAllocatedCount = kMinAllocated;
+ SkASSERT(fMaskFormat == kA8_GrMaskFormat);
}
~BitmapTextBatch() {
@@ -1049,13 +1614,20 @@ private:
void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
int width = glyph->fBounds.width();
int height = glyph->fBounds.height();
- int u0 = glyph->fAtlasLocation.fX;
- int v0 = glyph->fAtlasLocation.fY;
- int u1 = u0 + width;
- int v1 = v0 + height;
- // we assume texture coords are the last vertex attribute, this is a bit fragile.
- // TODO pass in this offset or something
+ int u0, v0, u1, v1;
+ if (fUseDistanceFields) {
+ u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
+ v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
+ u1 = u0 + width - 2 * SK_DistanceFieldInset;
+ v1 = v0 + height - 2 * SK_DistanceFieldInset;
+ } else {
+ u0 = glyph->fAtlasLocation.fX;
+ v0 = glyph->fAtlasLocation.fY;
+ u1 = u0 + width;
+ v1 = v0 + height;
+ }
+
SkIPoint16* textureCoords;
// V0
textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
@@ -1133,16 +1705,54 @@ private:
bool onCombineIfPossible(GrBatch* t) override {
BitmapTextBatch* that = t->cast<BitmapTextBatch>();
- if (this->fMaskFormat != that->fMaskFormat) {
+ if (fUseDistanceFields != that->fUseDistanceFields) {
return false;
}
- if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
- return false;
- }
+ if (!fUseDistanceFields) {
+ // Bitmap Text
+ if (fMaskFormat != that->fMaskFormat) {
+ return false;
+ }
- if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
- return false;
+ // TODO we can often batch across LCD text if we have dual source blending and don't
+ // have to use the blend constant
+ if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
+ return false;
+ }
+
+ if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+ } else {
+ // Distance Fields
+ SkASSERT(this->fMaskFormat == that->fMaskFormat &&
+ this->fMaskFormat == kA8_GrMaskFormat);
+
+ if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+ return false;
+ }
+
+ if (fFilteredColor != that->fFilteredColor) {
+ return false;
+ }
+
+ if (fUseLCDText != that->fUseLCDText) {
+ return false;
+ }
+
+ if (fUseBGR != that->fUseBGR) {
+ return false;
+ }
+
+ if (fGamma != that->fGamma) {
+ return false;
+ }
+
+ // TODO see note above
+ if (fUseLCDText && this->color() != that->color()) {
+ return false;
+ }
}
fBatch.fNumGlyphs += that->numGlyphs();
@@ -1168,6 +1778,66 @@ private:
return true;
}
+ // TODO just use class params
+ // TODO trying to figure out why lcd is so whack
+ GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
+ GrColor color, GrTexture* texture) {
+ GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
+
+ // set up any flags
+ uint32_t flags = 0;
+ flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+ flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
+ flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
+ kRectToRect_DistanceFieldEffectFlag : 0;
+ flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
+
+ // see if we need to create a new effect
+ if (fUseLCDText) {
+ GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
+
+ float redCorrection =
+ (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
+ float greenCorrection =
+ (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
+ float blueCorrection =
+ (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
+ GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
+ GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
+ greenCorrection,
+ blueCorrection);
+
+ return GrDistanceFieldLCDTextGeoProc::Create(color,
+ viewMatrix,
+ texture,
+ params,
+ widthAdjust,
+ flags);
+ } else {
+ flags |= kColorAttr_DistanceFieldEffectFlag;
+ bool opaque = GrColorIsOpaque(color);
+#ifdef SK_GAMMA_APPLY_TO_A8
+ U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
+ float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
+ return GrDistanceFieldA8TextGeoProc::Create(color,
+ viewMatrix,
+ texture,
+ params,
+ correction,
+ flags,
+ opaque);
+#else
+ return GrDistanceFieldA8TextGeoProc::Create(color,
+ viewMatrix,
+ texture,
+ params,
+ flags,
+ opaque);
+#endif
+ }
+
+ }
+
struct BatchTracker {
GrColor fColor;
SkMatrix fViewMatrix;
@@ -1184,6 +1854,14 @@ private:
GrMaskFormat fMaskFormat;
GrPixelConfig fPixelConfig;
GrBatchFontCache* fFontCache;
+
+ // Distance field properties
+ SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
+ SkColor fFilteredColor;
+ bool fUseDistanceFields;
+ bool fUseLCDText;
+ bool fUseBGR;
+ float fGamma;
};
void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
@@ -1221,7 +1899,7 @@ void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, cons
inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
BitmapTextBlob* cacheBlob, int run, GrColor color,
- uint8_t paintAlpha, SkScalar transX, SkScalar transY) {
+ SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
@@ -1230,12 +1908,32 @@ inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder
}
GrMaskFormat format = info.fMaskFormat;
- GrColor subRunColor = kARGB_GrMaskFormat == format ?
- SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha) :
- color;
+ GrColor subRunColor;
+ if (kARGB_GrMaskFormat == format) {
+ uint8_t paintAlpha = skPaint.getAlpha();
+ subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
+ } else {
+ subRunColor = color;
+ }
- SkAutoTUnref<BitmapTextBatch> batch(BitmapTextBatch::Create(format, glyphCount,
- fContext->getBatchFontCache()));
+ SkAutoTUnref<BitmapTextBatch> batch;
+ if (info.fDrawAsDistanceFields) {
+ SkColor filteredColor;
+ SkColorFilter* colorFilter = skPaint.getColorFilter();
+ if (colorFilter) {
+ filteredColor = colorFilter->filterColor(skPaint.getColor());
+ } else {
+ filteredColor = skPaint.getColor();
+ }
+ bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
+ float gamma = fDeviceProperties.gamma();
+ batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
+ fDistanceAdjustTable, filteredColor,
+ info.fUseLCDText, useBGR,
+ gamma));
+ } else {
+ batch.reset(BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache()));
+ }
BitmapTextBatch::Geometry& geometry = batch->geometry();
geometry.fBlob = SkRef(cacheBlob);
geometry.fRun = run;
@@ -1284,7 +1982,6 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
pipelineBuilder.setFromPaint(grPaint, rt, clip);
GrColor color = grPaint.getColor();
- uint8_t paintAlpha = skPaint.getAlpha();
SkTextBlob::RunIterator it(blob);
for (int run = 0; !it.done(); it.next(), run++) {
@@ -1293,7 +1990,7 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
continue;
}
cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
- this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, transX, transY);
+ this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
}
// Now flush big glyphs
@@ -1305,15 +2002,13 @@ void GrAtlasTextContext::flush(GrDrawTarget* target,
GrRenderTarget* rt,
const SkPaint& skPaint,
const GrPaint& grPaint,
- const GrClip& clip,
- const SkMatrix& viewMatrix) {
+ const GrClip& clip) {
GrPipelineBuilder pipelineBuilder;
pipelineBuilder.setFromPaint(grPaint, rt, clip);
GrColor color = grPaint.getColor();
- uint8_t paintAlpha = skPaint.getAlpha();
for (int run = 0; run < cacheBlob->fRunCount; run++) {
- this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, paintAlpha, 0, 0);
+ this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
}
// Now flush big glyphs
« no previous file with comments | « src/gpu/GrAtlasTextContext.h ('k') | src/gpu/GrContext.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698