Split GrTextContext into baseclass and subclass
This is a step towards enabling alternate text rendering code paths (GLyphy in particular)
Committed on behalf of baranowski@chromium.org
Review URL: http://codereview.appspot.com/5796071/
git-svn-id: http://skia.googlecode.com/svn/trunk@3412 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrDefaultTextContext.cpp b/src/gpu/GrDefaultTextContext.cpp
new file mode 100644
index 0000000..bde06c6
--- /dev/null
+++ b/src/gpu/GrDefaultTextContext.cpp
@@ -0,0 +1,280 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrAtlas.h"
+#include "GrDefaultTextContext.h"
+#include "GrContext.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrGpuVertex.h"
+#include "GrTemplates.h"
+#include "GrTextStrike.h"
+#include "GrTextStrike_impl.h"
+
+void GrDefaultTextContext::flushGlyphs() {
+ GrAssert(this->isValid());
+ if (fCurrVertex > 0) {
+ GrDrawTarget::AutoStateRestore asr(fDrawTarget);
+ GrDrawState* drawState = fDrawTarget->drawState();
+ // setup our sampler state for our text texture/atlas
+ GrSamplerState::Filter filter;
+ if (fExtMatrix.isIdentity()) {
+ filter = GrSamplerState::kNearest_Filter;
+ } else {
+ filter = GrSamplerState::kBilinear_Filter;
+ }
+ drawState->sampler(kGlyphMaskStage)->reset(
+ GrSamplerState::kRepeat_WrapMode,filter);
+
+ GrAssert(GrIsALIGN4(fCurrVertex));
+ int nIndices = fCurrVertex + (fCurrVertex >> 1);
+ GrAssert(fCurrTexture);
+ drawState->setTexture(kGlyphMaskStage, fCurrTexture);
+
+ if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
+ if (kOne_BlendCoeff != fGrPaint.fSrcBlendCoeff ||
+ kISA_BlendCoeff != fGrPaint.fDstBlendCoeff ||
+ fGrPaint.hasTexture()) {
+ GrPrintf("LCD Text will not draw correctly.\n");
+ }
+ // setup blend so that we get mask * paintColor + (1-mask)*dstColor
+ drawState->setBlendConstant(fGrPaint.fColor);
+ drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
+ // don't modulate by the paint's color in the frag since we're
+ // already doing it via the blend const.
+ drawState->setColor(0xffffffff);
+ } else {
+ // set back to normal in case we took LCD path previously.
+ drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff);
+ drawState->setColor(fGrPaint.fColor);
+ }
+
+ fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
+
+ fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
+ 0, 0, fCurrVertex, nIndices);
+ fVertices = NULL;
+ this->INHERITED::reset();
+ }
+}
+
+GrDefaultTextContext::GrDefaultTextContext() {
+}
+
+GrDefaultTextContext::~GrDefaultTextContext() {
+}
+
+void GrDefaultTextContext::init(GrContext* context,
+ const GrPaint& paint,
+ const GrMatrix* extMatrix) {
+ this->INHERITED::init(context, paint, extMatrix);
+
+ fStrike = NULL;
+
+ if (NULL != extMatrix) {
+ fExtMatrix = *extMatrix;
+ } else {
+ fExtMatrix = GrMatrix::I();
+ }
+ if (context->getClip().hasConservativeBounds()) {
+ if (!fExtMatrix.isIdentity()) {
+ GrMatrix inverse;
+ GrRect r = context->getClip().getConservativeBounds();
+ if (fExtMatrix.invert(&inverse)) {
+ inverse.mapRect(&r);
+ r.roundOut(&fClipRect);
+ }
+ } else {
+ context->getClip().getConservativeBounds().roundOut(&fClipRect);
+ }
+ } else {
+ fClipRect.setLargest();
+ }
+
+ // save the context's original matrix off and restore in destructor
+ // getTextTarget should be called after that
+ fOrigViewMatrix = fContext->getMatrix();
+ fContext->setMatrix(fExtMatrix);
+
+ /*
+ We need to call preConcatMatrix with our viewmatrix's inverse, for each
+ texture and mask in the paint. However, computing the inverse can be
+ expensive, and its possible we may not have any textures or masks, so these
+ two loops are written such that we only compute the inverse (once) if we
+ need it. We do this on our copy of the paint rather than directly on the
+ draw target because we re-provide the paint to the context when we have
+ to flush our glyphs or draw a glyph as a path midstream.
+ */
+ bool invVMComputed = false;
+ GrMatrix invVM;
+ for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
+ if (NULL != fGrPaint.getTexture(t)) {
+ if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
+ invVMComputed = true;
+ fGrPaint.textureSampler(t)->preConcatMatrix(invVM);
+ }
+ }
+ }
+ for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
+ if (NULL != fGrPaint.getMask(m)) {
+ if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
+ invVMComputed = true;
+ fGrPaint.maskSampler(m)->preConcatMatrix(invVM);
+ }
+ }
+ }
+
+ // this has been already done in the baseclass, but we need to repeat
+ // due to new matrix
+ fDrawTarget = fContext->getTextTarget(fGrPaint);
+
+ fVertices = NULL;
+
+ fVertexLayout =
+ GrDrawTarget::kTextFormat_VertexLayoutBit |
+ GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+
+ int stageMask = paint.getActiveStageMask();
+ if (stageMask) {
+ for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+ if ((1 << i) & stageMask) {
+ fVertexLayout |=
+ GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+ GrAssert(i != kGlyphMaskStage);
+ }
+ }
+ }
+}
+
+void GrDefaultTextContext::finish() {
+ this->flush();
+
+ fStrike = NULL;
+ fContext->setMatrix(fOrigViewMatrix);
+
+ this->INHERITED::finish();
+}
+
+void GrDefaultTextContext::flush() {
+ GrAssert(this->isValid());
+ this->flushGlyphs();
+}
+
+static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
+ int stride) {
+ v[0 * stride].setI(l, t);
+ v[1 * stride].setI(l, b);
+ v[2 * stride].setI(r, b);
+ v[3 * stride].setI(r, t);
+}
+
+void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
+ GrFixed vx, GrFixed vy,
+ GrFontScaler* scaler) {
+ GrAssert(this->isValid());
+ if (NULL == fStrike) {
+ fStrike = fContext->getFontCache()->getStrike(scaler);
+ }
+
+ GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
+ if (NULL == glyph || glyph->fBounds.isEmpty()) {
+ return;
+ }
+
+ vx += GrIntToFixed(glyph->fBounds.fLeft);
+ vy += GrIntToFixed(glyph->fBounds.fTop);
+
+ // keep them as ints until we've done the clip-test
+ GrFixed width = glyph->fBounds.width();
+ GrFixed height = glyph->fBounds.height();
+
+ // check if we clipped out
+ if (true || NULL == glyph->fAtlas) {
+ int x = vx >> 16;
+ int y = vy >> 16;
+ if (fClipRect.quickReject(x, y, x + width, y + height)) {
+// Gr_clz(3); // so we can set a break-point in the debugger
+ return;
+ }
+ }
+
+ if (NULL == glyph->fAtlas) {
+ if (fStrike->getGlyphAtlas(glyph, scaler)) {
+ goto HAS_ATLAS;
+ }
+
+ // before we purge the cache, we must flush any accumulated draws
+ this->flushGlyphs();
+ fContext->flushText();
+
+ // try to purge
+ fContext->getFontCache()->purgeExceptFor(fStrike);
+ if (fStrike->getGlyphAtlas(glyph, scaler)) {
+ goto HAS_ATLAS;
+ }
+
+ if (NULL == glyph->fPath) {
+ GrPath* path = new GrPath;
+ if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+ // flag the glyph as being dead?
+ delete path;
+ return;
+ }
+ glyph->fPath = path;
+ }
+
+ GrPoint translate;
+ translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
+ GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
+ fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_PathFill,
+ &translate);
+ return;
+ }
+
+HAS_ATLAS:
+ GrAssert(glyph->fAtlas);
+
+ // now promote them to fixed
+ width = GrIntToFixed(width);
+ height = GrIntToFixed(height);
+
+ GrTexture* texture = glyph->fAtlas->texture();
+ this->prepareForGlyph(texture);
+
+ this->setupVertexBuff(GrTCast<void**>(&fVertices),
+ fVertexLayout);
+
+ GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
+ GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
+
+#if GR_GL_TEXT_TEXTURE_NORMALIZED
+ int x = vx >> 16;
+ int y = vy >> 16;
+ int w = width >> 16;
+ int h = height >> 16;
+
+ setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
+ setRectFan(&fVertices[2*fCurrVertex+1],
+ texture->normalizeFixedX(tx),
+ texture->normalizeFixedY(ty),
+ texture->normalizeFixedX(tx + width),
+ texture->normalizeFixedY(ty + height),
+ 2);
+#else
+ fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
+ 2 * sizeof(GrGpuTextVertex));
+ fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
+ texture->normalizeFixedY(ty),
+ texture->normalizeFixedX(tx + width),
+ texture->normalizeFixedY(ty + height),
+ 2 * sizeof(GrGpuTextVertex));
+#endif
+ fCurrVertex += 4;
+}