#include "GrDrawTarget.h"
#include "GrFontScaler.h"
#include "GrIndexBuffer.h"
+#include "GrStrokeInfo.h"
#include "GrTextStrike.h"
#include "GrTextStrike_impl.h"
#include "SkColorPriv.h"
#include "SkAutoKern.h"
#include "SkDraw.h"
+#include "SkDrawProcs.h"
#include "SkGlyphCache.h"
#include "SkGpuDevice.h"
#include "SkGr.h"
-
-static const int kGlyphCoordsAttributeIndex = 1;
+#include "SkTextMapStateProc.h"
SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
"Dump the contents of the font cache before every purge.");
+namespace {
+// position + texture coord
+extern const GrVertexAttrib gTextVertexAttribs[] = {
+ {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
+ {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
+};
+
+static const size_t kTextVASize = 2 * sizeof(SkPoint);
+
+// position + color + texture coord
+extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
+ {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
+ {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
+ {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
+};
+
+static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
+
+};
+
GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
const SkDeviceProperties& properties)
: GrTextContext(context, properties) {
fCurrTexture = NULL;
fCurrVertex = 0;
+ fEffectTextureUniqueID = SK_InvalidUniqueID;
fVertices = NULL;
fMaxVertices = 0;
if (fCurrVertex > 0) {
// setup our sampler state for our text texture/atlas
- SkASSERT(GrIsALIGN4(fCurrVertex));
+ SkASSERT(SkIsAlign4(fCurrVertex));
SkASSERT(fCurrTexture);
GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
+ uint32_t textureUniqueID = fCurrTexture->getUniqueID();
+
+ if (textureUniqueID != fEffectTextureUniqueID) {
+ fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
+ params));
+ fEffectTextureUniqueID = textureUniqueID;
+ }
+
// This effect could be stored with one of the cache objects (atlas?)
- drawState->addCoverageEffect(
- GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
- kGlyphCoordsAttributeIndex)->unref();
-
- if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
- drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
- drawState->setColor(0xffffffff);
- } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
- if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
- kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
- fPaint.numColorStages()) {
- GrPrintf("LCD Text will not draw correctly.\n");
+ drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
+ SkASSERT(fStrike);
+ switch (fStrike->getMaskFormat()) {
+ // Color bitmap text
+ case kARGB_GrMaskFormat:
+ SkASSERT(!drawState->hasColorVertexAttribute());
+ drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+ drawState->setColor(0xffffffff);
+ break;
+ // LCD text
+ case kA888_GrMaskFormat:
+ case kA565_GrMaskFormat: {
+ if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
+ kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
+ fPaint.numColorStages()) {
+ GrPrintf("LCD Text will not draw correctly.\n");
+ }
+ SkASSERT(!drawState->hasColorVertexAttribute());
+ // We don't use the GrPaint's color in this case because it's been premultiplied by
+ // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
+ // the mask texture color. The end result is that we get
+ // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
+ int a = SkColorGetA(fSkPaint.getColor());
+ // paintAlpha
+ drawState->setColor(SkColorSetARGB(a, a, a, a));
+ // paintColor
+ drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
+ drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
+ break;
}
- // We don't use the GrPaint's color in this case because it's been premultiplied by
- // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
- // the mask texture color. The end result is that we get
- // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
- int a = SkColorGetA(fSkPaint.getColor());
- // paintAlpha
- drawState->setColor(SkColorSetARGB(a, a, a, a));
- // paintColor
- drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
- drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
- } else {
- // set back to normal in case we took LCD path previously.
- drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
- drawState->setColor(fPaint.getColor());
+ // Grayscale/BW text
+ case kA8_GrMaskFormat:
+ // set back to normal in case we took LCD path previously.
+ drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+ // We're using per-vertex color.
+ SkASSERT(drawState->hasColorVertexAttribute());
+ break;
+ default:
+ SkFAIL("Unexepected mask format.");
}
-
int nGlyphs = fCurrVertex / 4;
fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
}
inline void GrBitmapTextContext::finish() {
- flushGlyphs();
+ this->flushGlyphs();
GrTextContext::finish();
}
this->finish();
}
-///////////////////////////////////////////////////////////////////////////////
-// 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];
-}
-
-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 GrPaint& paint, const SkPaint& skPaint,
const char text[], size_t byteLength,
const SkScalar pos[], SkScalar constY,
autoMatrix.setIdentity(fContext, &fPaint);
const char* stop = text + byteLength;
- AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
- BitmapTextMapState tms(ctm, constY);
- BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+ SkTextAlignProc alignProc(fSkPaint.getTextAlign());
+ SkTextMapStateProc tmsProc(ctm, constY, scalarsPerPosition);
SkFixed halfSampleX = 0, halfSampleY = 0;
if (cache->isSubpixel()) {
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;
+ SkPoint tmsLoc;
+ tmsProc(pos, &tmsLoc);
+ SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
+ SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
const SkGlyph& glyph = glyphCacheProc(cache, &text,
fx & fxMask, fy & fyMask);
if (metricGlyph.fWidth) {
SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
-
- tmsProc(tms, pos);
+ SkPoint tmsLoc;
+ tmsProc(pos, &tmsLoc);
SkIPoint fixedLoc;
- alignProc(tms.fLoc, metricGlyph, &fixedLoc);
+ alignProc(tmsLoc, metricGlyph, &fixedLoc);
SkFixed fx = fixedLoc.fX + halfSampleX;
SkFixed fy = fixedLoc.fY + halfSampleY;
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
if (glyph.fWidth) {
- tmsProc(tms, pos);
+ SkPoint tmsLoc;
+ tmsProc(pos, &tmsLoc);
- SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
- SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
+ SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
+ SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
glyph.getSubXFixed(),
glyph.getSubYFixed()),
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
if (glyph.fWidth) {
- tmsProc(tms, pos);
+ SkPoint tmsLoc;
+ tmsProc(pos, &tmsLoc);
SkIPoint fixedLoc;
- alignProc(tms.fLoc, glyph, &fixedLoc);
+ alignProc(tmsLoc, glyph, &fixedLoc);
SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
this->finish();
}
-namespace {
-
-// position + texture coord
-extern const GrVertexAttrib gTextVertexAttribs[] = {
- {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
- {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
-};
-
-};
-
void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
- GrFixed vx, GrFixed vy,
+ SkFixed vx, SkFixed vy,
GrFontScaler* scaler) {
if (NULL == fDrawTarget) {
return;
}
if (NULL == fStrike) {
-#if SK_DISTANCEFIELD_FONTS
fStrike = fContext->getFontCache()->getStrike(scaler, false);
-#else
- fStrike = fContext->getFontCache()->getStrike(scaler);
-#endif
}
GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
vy += SkIntToFixed(glyph->fBounds.fTop);
// keep them as ints until we've done the clip-test
- GrFixed width = glyph->fBounds.width();
- GrFixed height = glyph->fBounds.height();
+ SkFixed width = glyph->fBounds.width();
+ SkFixed height = glyph->fBounds.height();
// check if we clipped out
if (true || NULL == glyph->fPlot) {
}
if (NULL == glyph->fPlot) {
- if (fStrike->getGlyphAtlas(glyph, scaler)) {
- goto HAS_ATLAS;
- }
+ if (!fStrike->glyphTooLargeForAtlas(glyph)) {
+ if (fStrike->addGlyphToAtlas(glyph, scaler)) {
+ goto HAS_ATLAS;
+ }
- // try to clear out an unused plot before we flush
- fContext->getFontCache()->freePlotExceptFor(fStrike);
- if (fStrike->getGlyphAtlas(glyph, scaler)) {
- goto HAS_ATLAS;
- }
+ // try to clear out an unused plot before we flush
+ if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
+ fStrike->addGlyphToAtlas(glyph, scaler)) {
+ goto HAS_ATLAS;
+ }
- if (c_DumpFontCache) {
+ if (c_DumpFontCache) {
#ifdef SK_DEVELOPER
- fContext->getFontCache()->dump();
+ fContext->getFontCache()->dump();
#endif
- }
+ }
- // before we purge the cache, we must flush any accumulated draws
- this->flushGlyphs();
- fContext->flush();
+ // flush any accumulated draws to allow us to free up a plot
+ this->flushGlyphs();
+ fContext->flush();
- // try to purge
- fContext->getFontCache()->purgeExceptFor(fStrike);
- // need to use new flush count here
- if (fStrike->getGlyphAtlas(glyph, scaler)) {
- goto HAS_ATLAS;
+ // we should have an unused plot now
+ if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
+ fStrike->addGlyphToAtlas(glyph, scaler)) {
+ goto HAS_ATLAS;
+ }
}
if (NULL == glyph->fPath) {
SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
GrPaint tmpPaint(fPaint);
am.setPreConcat(fContext, translate, &tmpPaint);
- SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
- fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
+ GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
+ fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
return;
}
fCurrTexture->ref();
}
+ bool useColorVerts = kA8_GrMaskFormat == fStrike->getMaskFormat();
+
if (NULL == fVertices) {
// If we need to reserve vertices allow the draw target to suggest
// a number of verts to reserve and whether to perform a flush.
fMaxVertices = kMinRequestedVerts;
- fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
- SK_ARRAY_COUNT(gTextVertexAttribs));
+ if (useColorVerts) {
+ fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
+ SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
+ } else {
+ fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
+ SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
+ }
bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
if (flush) {
this->flushGlyphs();
fContext->flush();
- fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
- SK_ARRAY_COUNT(gTextVertexAttribs));
+ if (useColorVerts) {
+ fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
+ SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
+ } else {
+ fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
+ SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
+ }
}
fMaxVertices = kDefaultRequestedVerts;
// ignore return, no point in flushing again.
}
bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
0,
- GrTCast<void**>(&fVertices),
+ &fVertices,
NULL);
GrAlwaysAssert(success);
- SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
}
- GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
- GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
+ SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
+ SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
SkRect r;
r.fLeft = SkFixedToFloat(vx);
fVertexBounds.growToInclude(r);
- fVertices[2*fCurrVertex].setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom,
- 2 * sizeof(SkPoint));
- fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
- SkFixedToFloat(texture->normalizeFixedY(ty)),
- SkFixedToFloat(texture->normalizeFixedX(tx + width)),
- SkFixedToFloat(texture->normalizeFixedY(ty + height)),
- 2 * sizeof(SkPoint));
+ size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
+ (2 * sizeof(SkPoint));
+
+ SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
+
+ SkPoint* positions = reinterpret_cast<SkPoint*>(
+ reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
+ positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
+
+ // The texture coords are last in both the with and without color vertex layouts.
+ SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
+ reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
+ textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
+ SkFixedToFloat(texture->normalizeFixedY(ty)),
+ SkFixedToFloat(texture->normalizeFixedX(tx + width)),
+ SkFixedToFloat(texture->normalizeFixedY(ty + height)),
+ vertSize);
+ if (useColorVerts) {
+ if (0xFF == GrColorUnpackA(fPaint.getColor())) {
+ fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
+ }
+ // color comes after position.
+ GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
+ for (int i = 0; i < 4; ++i) {
+ *colors = fPaint.getColor();
+ colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
+ }
+ }
fCurrVertex += 4;
}