Index: src/gpu/GrBitmapTextContext.cpp |
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp |
index 6e33d90d07fb53f0af25139664a20fb7dbffbf87..1a0d843db7163038cedbff96a48ad018d61260fe 100755 |
--- a/src/gpu/GrBitmapTextContext.cpp |
+++ b/src/gpu/GrBitmapTextContext.cpp |
@@ -18,16 +18,19 @@ |
#include "SkStrokeRec.h" |
#include "effects/GrCustomCoordsTextureEffect.h" |
+#include "SkAutoKern.h" |
+#include "SkGlyphCache.h" |
+#include "SkGpuDevice.h" |
+#include "SkGr.h" |
+ |
static const int kGlyphCoordsAttributeIndex = 1; |
SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, |
"Dump the contents of the font cache before every purge."); |
-GrBitmapTextContext::GrBitmapTextContext(GrContext* context, const GrPaint& paint, |
+GrBitmapTextContext::GrBitmapTextContext(SkGpuDevice* device, const GrPaint& paint, |
const SkPaint& skPaint) : |
- GrTextContext(context, paint, skPaint) { |
- fAutoMatrix.setIdentity(fContext, &fPaint); |
- |
+ GrTextContext(device, paint, skPaint) { |
fStrike = NULL; |
fCurrTexture = NULL; |
@@ -53,9 +56,11 @@ void GrBitmapTextContext::flushGlyphs() { |
return; |
} |
+ GrContext* context = fDevice->context(); |
+ |
GrDrawState* drawState = fDrawTarget->drawState(); |
GrDrawState::AutoRestoreEffects are(drawState); |
- drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget()); |
+ drawState->setFromPaint(fPaint, SkMatrix::I(), context->getRenderTarget()); |
if (fCurrVertex > 0) { |
// setup our sampler state for our text texture/atlas |
@@ -94,7 +99,7 @@ void GrBitmapTextContext::flushGlyphs() { |
} |
int nGlyphs = fCurrVertex / 4; |
- fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); |
+ fDrawTarget->setIndexSourceToBuffer(context->getQuadIndexBuffer()); |
fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, |
nGlyphs, |
4, 6); |
@@ -107,6 +112,358 @@ void GrBitmapTextContext::flushGlyphs() { |
} |
} |
+void GrBitmapTextContext::drawText(const char text[], size_t byteLength, |
+ SkScalar x, SkScalar y) { |
+ SkASSERT(byteLength == 0 || text != NULL); |
+ |
+ // nothing to draw |
+ if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { |
+ return; |
+ } |
+ |
+ SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
+ |
+ GrContext* context = fDevice->context(); |
+ SkAutoGlyphCache autoCache(fSkPaint, &fDevice->getDeviceProperties(), &context->getMatrix()); |
+ SkGlyphCache* cache = autoCache.getCache(); |
+ GrFontScaler* fontScaler = GetGrFontScaler(cache); |
+ |
+ // transform our starting point |
+ { |
+ SkPoint loc; |
+ context->getMatrix().mapXY(x, y, &loc); |
+ x = loc.fX; |
+ y = loc.fY; |
+ } |
+ |
+ // need to measure first |
+ if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { |
+ SkVector stop; |
+ |
+ MeasureText(cache, glyphCacheProc, text, byteLength, &stop); |
+ |
+ SkScalar stopX = stop.fX; |
+ SkScalar stopY = stop.fY; |
+ |
+ if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { |
+ stopX = SkScalarHalf(stopX); |
+ stopY = SkScalarHalf(stopY); |
+ } |
+ x -= stopX; |
+ y -= stopY; |
+ } |
+ |
+ const char* stop = text + byteLength; |
+ |
+ SkAutoKern autokern; |
+ |
+ SkFixed fxMask = ~0; |
+ SkFixed fyMask = ~0; |
+ SkFixed halfSampleX, halfSampleY; |
+ if (cache->isSubpixel()) { |
+ halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits); |
+ SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(context->getMatrix()); |
+ if (kX_SkAxisAlignment == baseline) { |
+ fyMask = 0; |
+ halfSampleY = SK_FixedHalf; |
+ } else if (kY_SkAxisAlignment == baseline) { |
+ fxMask = 0; |
+ halfSampleX = SK_FixedHalf; |
+ } |
+ } else { |
+ halfSampleX = halfSampleY = SK_FixedHalf; |
+ } |
+ |
+ SkFixed fx = SkScalarToFixed(x) + halfSampleX; |
+ SkFixed fy = SkScalarToFixed(y) + halfSampleY; |
+ |
+ GrContext::AutoMatrix autoMatrix; |
+ autoMatrix.setIdentity(fDevice->context(), &fPaint); |
+ |
+ while (text < stop) { |
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); |
+ |
+ fx += autokern.adjust(glyph); |
+ |
+ if (glyph.fWidth) { |
+ this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
+ glyph.getSubXFixed(), |
+ glyph.getSubYFixed()), |
+ SkFixedFloorToFixed(fx), |
+ SkFixedFloorToFixed(fy), |
+ fontScaler); |
+ } |
+ |
+ fx += glyph.fAdvanceX; |
+ fy += glyph.fAdvanceY; |
+ } |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+// Copied from SkDraw |
+ |
+// last parameter is interpreted as SkFixed [x, y] |
+// return the fixed position, which may be rounded or not by the caller |
+// e.g. subpixel doesn't round |
+typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*); |
+ |
+static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) { |
+ dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY)); |
+} |
+ |
+static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) { |
+ dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1), |
+ SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1)); |
+} |
+ |
+static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) { |
+ dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX, |
+ SkScalarToFixed(loc.fY) - glyph.fAdvanceY); |
+} |
+ |
+static AlignProc pick_align_proc(SkPaint::Align align) { |
+ static const AlignProc gProcs[] = { |
+ leftAlignProc, centerAlignProc, rightAlignProc |
+ }; |
+ |
+ SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); |
+ |
+ return gProcs[align]; |
+} |
+ |
+typedef void (*AlignProc_scalar)(const SkPoint&, const SkGlyph&, SkPoint*); |
+ |
+static void leftAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) { |
+ dst->set(loc.fX, loc.fY); |
+} |
+ |
+static void centerAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) { |
+ dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX >> 1), |
+ loc.fY - SkFixedToScalar(glyph.fAdvanceY >> 1)); |
+} |
+ |
+static void rightAlignProc_scalar(const SkPoint& loc, const SkGlyph& glyph, SkPoint* dst) { |
+ dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX), |
+ loc.fY - SkFixedToScalar(glyph.fAdvanceY)); |
+} |
+ |
+static AlignProc_scalar pick_align_proc_scalar(SkPaint::Align align) { |
+ static const AlignProc_scalar gProcs[] = { |
+ leftAlignProc_scalar, centerAlignProc_scalar, rightAlignProc_scalar |
+ }; |
+ |
+ SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); |
+ |
+ return gProcs[align]; |
+} |
+ |
+class BitmapTextMapState { |
+public: |
+ mutable SkPoint fLoc; |
+ |
+ BitmapTextMapState(const SkMatrix& matrix, SkScalar y) |
+ : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {} |
+ |
+ typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]); |
+ |
+ Proc pickProc(int scalarsPerPosition); |
+ |
+private: |
+ const SkMatrix& fMatrix; |
+ SkMatrix::MapXYProc fProc; |
+ SkScalar fY; // ignored by MapXYProc |
+ // these are only used by Only... procs |
+ SkScalar fScaleX, fTransX, fTransformedY; |
+ |
+ static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) { |
+ state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc); |
+ } |
+ |
+ static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) { |
+ state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc); |
+ } |
+ |
+ static void MapOnlyScaleXProc(const BitmapTextMapState& state, |
+ const SkScalar pos[]) { |
+ state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX, |
+ state.fTransformedY); |
+ } |
+ |
+ static void MapOnlyTransXProc(const BitmapTextMapState& state, |
+ const SkScalar pos[]) { |
+ state.fLoc.set(*pos + state.fTransX, state.fTransformedY); |
+ } |
+}; |
+ |
+BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) { |
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); |
+ |
+ if (1 == scalarsPerPosition) { |
+ unsigned mtype = fMatrix.getType(); |
+ if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) { |
+ return MapXProc; |
+ } else { |
+ fScaleX = fMatrix.getScaleX(); |
+ fTransX = fMatrix.getTranslateX(); |
+ fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) + |
+ fMatrix.getTranslateY(); |
+ return (mtype & SkMatrix::kScale_Mask) ? |
+ MapOnlyScaleXProc : MapOnlyTransXProc; |
+ } |
+ } else { |
+ return MapXYProc; |
+ } |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+void GrBitmapTextContext::drawPosText(const char text[], size_t byteLength, |
+ const SkScalar pos[], SkScalar constY, |
+ int scalarsPerPosition) { |
+ SkASSERT(byteLength == 0 || text != NULL); |
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); |
+ |
+ // nothing to draw |
+ if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) { |
+ return; |
+ } |
+ |
+ SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
+ |
+ GrContext* context = fDevice->context(); |
+ SkAutoGlyphCache autoCache(fSkPaint, &fDevice->getDeviceProperties(), &context->getMatrix()); |
+ SkGlyphCache* cache = autoCache.getCache(); |
+ GrFontScaler* fontScaler = GetGrFontScaler(cache); |
+ |
+ // store original matrix before we reset, so we can use it to transform positions |
+ SkMatrix ctm = context->getMatrix(); |
+ GrContext::AutoMatrix autoMatrix; |
+ autoMatrix.setIdentity(fDevice->context(), &fPaint); |
+ |
+ const char* stop = text + byteLength; |
+ AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign()); |
+ BitmapTextMapState tms(ctm, constY); |
+ BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); |
+ SkFixed halfSampleX = 0, halfSampleY = 0; |
+ |
+ if (cache->isSubpixel()) { |
+ // maybe we should skip the rounding if linearText is set |
+ SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm); |
+ |
+ SkFixed fxMask = ~0; |
+ SkFixed fyMask = ~0; |
+ if (kX_SkAxisAlignment == baseline) { |
+ fyMask = 0; |
+#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX |
+ halfSampleY = SK_FixedHalf; |
+#endif |
+ } else if (kY_SkAxisAlignment == baseline) { |
+ fxMask = 0; |
+#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX |
+ halfSampleX = SK_FixedHalf; |
+#endif |
+ } |
+ |
+ if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { |
+ while (text < stop) { |
+ tmsProc(tms, pos); |
+ SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX; |
+ SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY; |
+ |
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, |
+ fx & fxMask, fy & fyMask); |
+ |
+ if (glyph.fWidth) { |
+ this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
+ glyph.getSubXFixed(), |
+ glyph.getSubYFixed()), |
+ SkFixedFloorToFixed(fx), |
+ SkFixedFloorToFixed(fy), |
+ fontScaler); |
+ } |
+ pos += scalarsPerPosition; |
+ } |
+ } else { |
+ while (text < stop) { |
+ const char* currentText = text; |
+ const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0); |
+ |
+ if (metricGlyph.fWidth) { |
+ SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;) |
+ SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;) |
+ |
+ tmsProc(tms, pos); |
+ SkIPoint fixedLoc; |
+ alignProc(tms.fLoc, metricGlyph, &fixedLoc); |
+ |
+ SkFixed fx = fixedLoc.fX + halfSampleX; |
+ SkFixed fy = fixedLoc.fY + halfSampleY; |
+ |
+ // have to call again, now that we've been "aligned" |
+ const SkGlyph& glyph = glyphCacheProc(cache, ¤tText, |
+ fx & fxMask, fy & fyMask); |
+ // the assumption is that the metrics haven't changed |
+ SkASSERT(prevAdvX == glyph.fAdvanceX); |
+ SkASSERT(prevAdvY == glyph.fAdvanceY); |
+ SkASSERT(glyph.fWidth); |
+ |
+ this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
+ glyph.getSubXFixed(), |
+ glyph.getSubYFixed()), |
+ SkFixedFloorToFixed(fx), |
+ SkFixedFloorToFixed(fy), |
+ fontScaler); |
+ } |
+ pos += scalarsPerPosition; |
+ } |
+ } |
+ } else { // not subpixel |
+ |
+ if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { |
+ while (text < stop) { |
+ // the last 2 parameters are ignored |
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
+ |
+ if (glyph.fWidth) { |
+ tmsProc(tms, pos); |
+ |
+ SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX; |
+ SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY; |
+ this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
+ glyph.getSubXFixed(), |
+ glyph.getSubYFixed()), |
+ SkFixedFloorToFixed(fx), |
+ SkFixedFloorToFixed(fy), |
+ fontScaler); |
+ } |
+ pos += scalarsPerPosition; |
+ } |
+ } else { |
+ while (text < stop) { |
+ // the last 2 parameters are ignored |
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
+ |
+ if (glyph.fWidth) { |
+ tmsProc(tms, pos); |
+ |
+ SkIPoint fixedLoc; |
+ alignProc(tms.fLoc, glyph, &fixedLoc); |
+ |
+ SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX; |
+ SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY; |
+ this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), |
+ glyph.getSubXFixed(), |
+ glyph.getSubYFixed()), |
+ SkFixedFloorToFixed(fx), |
+ SkFixedFloorToFixed(fy), |
+ fontScaler); |
+ } |
+ pos += scalarsPerPosition; |
+ } |
+ } |
+ } |
+} |
+ |
namespace { |
// position + texture coord |
@@ -123,11 +480,14 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed, |
if (NULL == fDrawTarget) { |
return; |
} |
+ |
+ GrContext* context = fDevice->context(); |
+ |
if (NULL == fStrike) { |
#if SK_DISTANCEFIELD_FONTS |
- fStrike = fContext->getFontCache()->getStrike(scaler, false); |
+ fStrike = context->getFontCache()->getStrike(scaler, false); |
#else |
- fStrike = fContext->getFontCache()->getStrike(scaler); |
+ fStrike = context->getFontCache()->getStrike(scaler); |
#endif |
} |
@@ -159,23 +519,23 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed, |
} |
// try to clear out an unused plot before we flush |
- fContext->getFontCache()->freePlotExceptFor(fStrike); |
+ context->getFontCache()->freePlotExceptFor(fStrike); |
if (fStrike->getGlyphAtlas(glyph, scaler)) { |
goto HAS_ATLAS; |
} |
if (c_DumpFontCache) { |
#ifdef SK_DEVELOPER |
- fContext->getFontCache()->dump(); |
+ context->getFontCache()->dump(); |
#endif |
} |
// before we purge the cache, we must flush any accumulated draws |
this->flushGlyphs(); |
- fContext->flush(); |
+ context->flush(); |
// try to purge |
- fContext->getFontCache()->purgeExceptFor(fStrike); |
+ context->getFontCache()->purgeExceptFor(fStrike); |
// need to use new flush count here |
if (fStrike->getGlyphAtlas(glyph, scaler)) { |
goto HAS_ATLAS; |
@@ -196,9 +556,9 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed, |
translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)), |
SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop))); |
GrPaint tmpPaint(fPaint); |
- am.setPreConcat(fContext, translate, &tmpPaint); |
+ am.setPreConcat(context, translate, &tmpPaint); |
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
- fContext->drawPath(tmpPaint, *glyph->fPath, stroke); |
+ context->drawPath(tmpPaint, *glyph->fPath, stroke); |
return; |
} |
@@ -229,7 +589,7 @@ HAS_ATLAS: |
bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); |
if (flush) { |
this->flushGlyphs(); |
- fContext->flush(); |
+ context->flush(); |
fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( |
SK_ARRAY_COUNT(gTextVertexAttribs)); |
} |
@@ -237,7 +597,7 @@ HAS_ATLAS: |
// ignore return, no point in flushing again. |
fDrawTarget->geometryHints(&fMaxVertices, NULL); |
- int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); |
+ int maxQuadVertices = 4 * context->getQuadIndexBuffer()->maxQuads(); |
if (fMaxVertices < kMinRequestedVerts) { |
fMaxVertices = kDefaultRequestedVerts; |
} else if (fMaxVertices > maxQuadVertices) { |