1 /*
2 Copyright 2010 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17
18 #include "GrTextContext.h"
19 #include "GrAtlas.h"
20 #include "GrContext.h"
21 #include "GrTextStrike.h"
22 #include "GrTextStrike_impl.h"
23 #include "GrFontScaler.h"
24 #include "GrIndexBuffer.h"
25 #include "GrGpuVertex.h"
26 #include "GrDrawTarget.h"
27
28 enum {
29 kGlyphMaskStage = GrPaint::kTotalStages,
30 };
31
flushGlyphs()32 void GrTextContext::flushGlyphs() {
33 if (fCurrVertex > 0) {
34 GrDrawTarget::AutoStateRestore asr(fDrawTarget);
35
36 // setup our sampler state for our text texture/atlas
37 GrSamplerState::Filter filter;
38 if (fExtMatrix.isIdentity()) {
39 filter = GrSamplerState::kNearest_Filter;
40 } else {
41 filter = GrSamplerState::kBilinear_Filter;
42 }
43 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
44 GrSamplerState::kRepeat_WrapMode,
45 filter);
46 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
47
48 GrAssert(GrIsALIGN4(fCurrVertex));
49 int nIndices = fCurrVertex + (fCurrVertex >> 1);
50 GrAssert(fCurrTexture);
51 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
52
53 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
54 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
55 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
56 fPaint.hasTexture()) {
57 GrPrintf("LCD Text will not draw correctly.\n");
58 }
59 // setup blend so that we get mask * paintColor + (1-mask)*dstColor
60 fDrawTarget->setBlendConstant(fPaint.fColor);
61 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
62 // don't modulate by the paint's color in the frag since we're
63 // already doing it via the blend const.
64 fDrawTarget->setColor(0xffffffff);
65 } else {
66 // set back to normal in case we took LCD path previously.
67 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
68 fDrawTarget->setColor(fPaint.fColor);
69 }
70
71 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
72
73 fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
74 0, 0, fCurrVertex, nIndices);
75 fDrawTarget->releaseReservedGeometry();
76 fVertices = NULL;
77 fMaxVertices = 0;
78 fCurrVertex = 0;
79 fCurrTexture->unref();
80 fCurrTexture = NULL;
81 }
82 }
83
GrTextContext(GrContext * context,const GrPaint & paint,const GrMatrix * extMatrix)84 GrTextContext::GrTextContext(GrContext* context,
85 const GrPaint& paint,
86 const GrMatrix* extMatrix) : fPaint(paint) {
87 fContext = context;
88 fStrike = NULL;
89
90 fCurrTexture = NULL;
91 fCurrVertex = 0;
92
93 if (NULL != extMatrix) {
94 fExtMatrix = *extMatrix;
95 } else {
96 fExtMatrix = GrMatrix::I();
97 }
98 if (context->getClip().hasConservativeBounds()) {
99 if (!fExtMatrix.isIdentity()) {
100 GrMatrix inverse;
101 GrRect r = context->getClip().getConservativeBounds();
102 if (fExtMatrix.invert(&inverse)) {
103 inverse.mapRect(&r);
104 r.roundOut(&fClipRect);
105 }
106 } else {
107 context->getClip().getConservativeBounds().roundOut(&fClipRect);
108 }
109 } else {
110 fClipRect.setLargest();
111 }
112
113 // save the context's original matrix off and restore in destructor
114 // this must be done before getTextTarget.
115 fOrigViewMatrix = fContext->getMatrix();
116 fContext->setMatrix(fExtMatrix);
117
118 fDrawTarget = fContext->getTextTarget(fPaint);
119
120 fVertices = NULL;
121 fMaxVertices = 0;
122
123 fVertexLayout =
124 GrDrawTarget::kTextFormat_VertexLayoutBit |
125 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
126
127 int stageMask = paint.getActiveStageMask();
128 if (stageMask) {
129 GrMatrix inverseViewMatrix;
130 if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
131 fDrawTarget->preConcatSamplerMatrices(stageMask,
132 inverseViewMatrix);
133 }
134 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
135 if ((1 << i) & stageMask) {
136 fVertexLayout |=
137 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
138 GrAssert(i != kGlyphMaskStage);
139 }
140 }
141 }
142
143 }
144
~GrTextContext()145 GrTextContext::~GrTextContext() {
146 this->flushGlyphs();
147 fContext->setMatrix(fOrigViewMatrix);
148 }
149
flush()150 void GrTextContext::flush() {
151 this->flushGlyphs();
152 }
153
setRectFan(GrGpuTextVertex v[4],int l,int t,int r,int b,int stride)154 static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
155 int stride) {
156 v[0 * stride].setI(l, t);
157 v[1 * stride].setI(l, b);
158 v[2 * stride].setI(r, b);
159 v[3 * stride].setI(r, t);
160 }
161
drawPackedGlyph(GrGlyph::PackedID packed,GrFixed vx,GrFixed vy,GrFontScaler * scaler)162 void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
163 GrFixed vx, GrFixed vy,
164 GrFontScaler* scaler) {
165 if (NULL == fStrike) {
166 fStrike = fContext->getFontCache()->getStrike(scaler);
167 }
168
169 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
170 if (NULL == glyph || glyph->fBounds.isEmpty()) {
171 return;
172 }
173
174 vx += GrIntToFixed(glyph->fBounds.fLeft);
175 vy += GrIntToFixed(glyph->fBounds.fTop);
176
177 // keep them as ints until we've done the clip-test
178 GrFixed width = glyph->fBounds.width();
179 GrFixed height = glyph->fBounds.height();
180
181 // check if we clipped out
182 if (true || NULL == glyph->fAtlas) {
183 int x = vx >> 16;
184 int y = vy >> 16;
185 if (fClipRect.quickReject(x, y, x + width, y + height)) {
186 // Gr_clz(3); // so we can set a break-point in the debugger
187 return;
188 }
189 }
190
191 if (NULL == glyph->fAtlas) {
192 if (fStrike->getGlyphAtlas(glyph, scaler)) {
193 goto HAS_ATLAS;
194 }
195
196 // before we purge the cache, we must flush any accumulated draws
197 this->flushGlyphs();
198 fContext->flushText();
199
200 // try to purge
201 fContext->getFontCache()->purgeExceptFor(fStrike);
202 if (fStrike->getGlyphAtlas(glyph, scaler)) {
203 goto HAS_ATLAS;
204 }
205
206 if (NULL == glyph->fPath) {
207 GrPath* path = new GrPath;
208 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
209 // flag the glyph as being dead?
210 delete path;
211 return;
212 }
213 glyph->fPath = path;
214 }
215
216 GrPoint translate;
217 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
218 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
219 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
220 &translate);
221 return;
222 }
223
224 HAS_ATLAS:
225 GrAssert(glyph->fAtlas);
226
227 // now promote them to fixed
228 width = GrIntToFixed(width);
229 height = GrIntToFixed(height);
230
231 GrTexture* texture = glyph->fAtlas->texture();
232 GrAssert(texture);
233
234 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
235 this->flushGlyphs();
236 fCurrTexture = texture;
237 fCurrTexture->ref();
238 }
239
240 if (NULL == fVertices) {
241 // If we need to reserve vertices allow the draw target to suggest
242 // a number of verts to reserve and whether to perform a flush.
243 fMaxVertices = kMinRequestedVerts;
244 bool flush = fDrawTarget->geometryHints(fVertexLayout,
245 &fMaxVertices,
246 NULL);
247 if (flush) {
248 this->flushGlyphs();
249 fContext->flushText();
250 fDrawTarget = fContext->getTextTarget(fPaint);
251 fMaxVertices = kDefaultRequestedVerts;
252 // ignore return, no point in flushing again.
253 fDrawTarget->geometryHints(fVertexLayout,
254 &fMaxVertices,
255 NULL);
256 }
257
258 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->size() / (6 * sizeof(uint16_t));
259 if (fMaxVertices < kMinRequestedVerts) {
260 fMaxVertices = kDefaultRequestedVerts;
261 } else if (fMaxVertices > maxQuadVertices) {
262 // don't exceed the limit of the index buffer
263 fMaxVertices = maxQuadVertices;
264 }
265 bool success = fDrawTarget->reserveAndLockGeometry(fVertexLayout,
266 fMaxVertices, 0,
267 GrTCast<void**>(&fVertices),
268 NULL);
269 GrAlwaysAssert(success);
270 }
271
272 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
273 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
274
275 #if GR_GL_TEXT_TEXTURE_NORMALIZED
276 int x = vx >> 16;
277 int y = vy >> 16;
278 int w = width >> 16;
279 int h = height >> 16;
280
281 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
282 setRectFan(&fVertices[2*fCurrVertex+1],
283 texture->normalizeFixedX(tx),
284 texture->normalizeFixedY(ty),
285 texture->normalizeFixedX(tx + width),
286 texture->normalizeFixedY(ty + height),
287 2);
288 #else
289 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
290 2 * sizeof(GrGpuTextVertex));
291 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
292 texture->normalizeFixedY(ty),
293 texture->normalizeFixedX(tx + width),
294 texture->normalizeFixedY(ty + height),
295 2 * sizeof(GrGpuTextVertex));
296 #endif
297 fCurrVertex += 4;
298 }
299
300
301