1 /*
2 * Copyright 2010 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9
10 #include "GrTextContext.h"
11 #include "GrAtlas.h"
12 #include "GrContext.h"
13 #include "GrDrawTarget.h"
14 #include "GrFontScaler.h"
15 #include "GrGpuVertex.h"
16 #include "GrIndexBuffer.h"
17 #include "GrTextStrike.h"
18 #include "GrTextStrike_impl.h"
19 #include "SkPath.h"
20 #include "SkStrokeRec.h"
21
22 enum {
23 kGlyphMaskStage = GrPaint::kTotalStages,
24 };
25
flushGlyphs()26 void GrTextContext::flushGlyphs() {
27 if (NULL == fDrawTarget) {
28 return;
29 }
30 GrDrawState* drawState = fDrawTarget->drawState();
31 if (fCurrVertex > 0) {
32 // setup our sampler state for our text texture/atlas
33 GrAssert(GrIsALIGN4(fCurrVertex));
34 GrAssert(fCurrTexture);
35 GrTextureParams params(SkShader::kRepeat_TileMode, false);
36 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, SkMatrix::I(), params);
37
38 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
39 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
40 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
41 fPaint.hasColorStage()) {
42 GrPrintf("LCD Text will not draw correctly.\n");
43 }
44 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
45 drawState->setBlendConstant(fPaint.getColor());
46 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
47 // don't modulate by the paint's color in the frag since we're
48 // already doing it via the blend const.
49 drawState->setColor(0xffffffff);
50 } else {
51 // set back to normal in case we took LCD path previously.
52 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
53 drawState->setColor(fPaint.getColor());
54 }
55
56 int nGlyphs = fCurrVertex / 4;
57 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
58 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
59 nGlyphs,
60 4, 6);
61 fDrawTarget->resetVertexSource();
62 fVertices = NULL;
63 fMaxVertices = 0;
64 fCurrVertex = 0;
65 GrSafeSetNull(fCurrTexture);
66 }
67 drawState->disableStages();
68 fDrawTarget = NULL;
69 }
70
GrTextContext(GrContext * context,const GrPaint & paint)71 GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
72 fContext = context;
73 fStrike = NULL;
74
75 fCurrTexture = NULL;
76 fCurrVertex = 0;
77
78 const GrClipData* clipData = context->getClip();
79
80 GrRect devConservativeBound;
81 clipData->fClipStack->getConservativeBounds(
82 -clipData->fOrigin.fX,
83 -clipData->fOrigin.fY,
84 context->getRenderTarget()->width(),
85 context->getRenderTarget()->height(),
86 &devConservativeBound);
87
88 devConservativeBound.roundOut(&fClipRect);
89
90 fAutoMatrix.setIdentity(fContext, &fPaint);
91
92 fDrawTarget = NULL;
93
94 fVertices = NULL;
95 fMaxVertices = 0;
96
97 fVertexLayout =
98 GrDrawState::kTextFormat_VertexLayoutBit |
99 GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
100 }
101
~GrTextContext()102 GrTextContext::~GrTextContext() {
103 this->flushGlyphs();
104 if (fDrawTarget) {
105 fDrawTarget->drawState()->disableStages();
106 }
107 }
108
flush()109 void GrTextContext::flush() {
110 this->flushGlyphs();
111 }
112
setRectFan(GrGpuTextVertex v[4],int l,int t,int r,int b,int stride)113 static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
114 int stride) {
115 v[0 * stride].setI(l, t);
116 v[1 * stride].setI(l, b);
117 v[2 * stride].setI(r, b);
118 v[3 * stride].setI(r, t);
119 }
120
drawPackedGlyph(GrGlyph::PackedID packed,GrFixed vx,GrFixed vy,GrFontScaler * scaler)121 void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
122 GrFixed vx, GrFixed vy,
123 GrFontScaler* scaler) {
124 if (NULL == fStrike) {
125 fStrike = fContext->getFontCache()->getStrike(scaler);
126 }
127
128 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
129 if (NULL == glyph || glyph->fBounds.isEmpty()) {
130 return;
131 }
132
133 vx += SkIntToFixed(glyph->fBounds.fLeft);
134 vy += SkIntToFixed(glyph->fBounds.fTop);
135
136 // keep them as ints until we've done the clip-test
137 GrFixed width = glyph->fBounds.width();
138 GrFixed height = glyph->fBounds.height();
139
140 // check if we clipped out
141 if (true || NULL == glyph->fAtlas) {
142 int x = vx >> 16;
143 int y = vy >> 16;
144 if (fClipRect.quickReject(x, y, x + width, y + height)) {
145 // SkCLZ(3); // so we can set a break-point in the debugger
146 return;
147 }
148 }
149
150 if (NULL == glyph->fAtlas) {
151 if (fStrike->getGlyphAtlas(glyph, scaler)) {
152 goto HAS_ATLAS;
153 }
154
155 // before we purge the cache, we must flush any accumulated draws
156 this->flushGlyphs();
157 fContext->flush();
158
159 // try to purge
160 fContext->getFontCache()->purgeExceptFor(fStrike);
161 if (fStrike->getGlyphAtlas(glyph, scaler)) {
162 goto HAS_ATLAS;
163 }
164
165 if (NULL == glyph->fPath) {
166 SkPath* path = SkNEW(SkPath);
167 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
168 // flag the glyph as being dead?
169 delete path;
170 return;
171 }
172 glyph->fPath = path;
173 }
174
175 GrContext::AutoMatrix am;
176 SkMatrix translate;
177 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
178 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
179 GrPaint tmpPaint(fPaint);
180 am.setPreConcat(fContext, translate, &tmpPaint);
181 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
182 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
183 return;
184 }
185
186 HAS_ATLAS:
187 GrAssert(glyph->fAtlas);
188
189 // now promote them to fixed
190 width = SkIntToFixed(width);
191 height = SkIntToFixed(height);
192
193 GrTexture* texture = glyph->fAtlas->texture();
194 GrAssert(texture);
195
196 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
197 this->flushGlyphs();
198 fCurrTexture = texture;
199 fCurrTexture->ref();
200 }
201
202 if (NULL == fVertices) {
203 // If we need to reserve vertices allow the draw target to suggest
204 // a number of verts to reserve and whether to perform a flush.
205 fMaxVertices = kMinRequestedVerts;
206 bool flush = (NULL != fDrawTarget) &&
207 fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
208 &fMaxVertices,
209 NULL);
210 if (flush) {
211 this->flushGlyphs();
212 fContext->flush();
213 }
214 fDrawTarget = fContext->getTextTarget(fPaint);
215 fMaxVertices = kDefaultRequestedVerts;
216 // ignore return, no point in flushing again.
217 fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
218 &fMaxVertices,
219 NULL);
220
221 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
222 if (fMaxVertices < kMinRequestedVerts) {
223 fMaxVertices = kDefaultRequestedVerts;
224 } else if (fMaxVertices > maxQuadVertices) {
225 // don't exceed the limit of the index buffer
226 fMaxVertices = maxQuadVertices;
227 }
228 bool success = fDrawTarget->reserveVertexAndIndexSpace(
229 fVertexLayout,
230 fMaxVertices,
231 0,
232 GrTCast<void**>(&fVertices),
233 NULL);
234 GrAlwaysAssert(success);
235 }
236
237 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
238 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
239
240 #if GR_TEXT_SCALAR_IS_USHORT
241 int x = vx >> 16;
242 int y = vy >> 16;
243 int w = width >> 16;
244 int h = height >> 16;
245
246 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
247 setRectFan(&fVertices[2*fCurrVertex+1],
248 texture->normalizeFixedX(tx),
249 texture->normalizeFixedY(ty),
250 texture->normalizeFixedX(tx + width),
251 texture->normalizeFixedY(ty + height),
252 2);
253 #else
254 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
255 2 * sizeof(GrGpuTextVertex));
256 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
257 texture->normalizeFixedY(ty),
258 texture->normalizeFixedX(tx + width),
259 texture->normalizeFixedY(ty + height),
260 2 * sizeof(GrGpuTextVertex));
261 #endif
262 fCurrVertex += 4;
263 }
264