| 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
|
|
|