OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2014 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 #include "GrStencilAndCoverTextContext.h" | |
9 #include "GrDrawTarget.h" | |
10 #include "GrFontScaler.h" | |
11 #include "GrGpu.h" | |
12 #include "GrPath.h" | |
13 #include "GrTextStrike.h" | |
14 #include "GrTextStrike_impl.h" | |
15 #include "SkAutoKern.h" | |
16 #include "SkDraw.h" | |
17 #include "SkDrawProcs.h" | |
18 #include "SkGlyphCache.h" | |
19 #include "SkGpuDevice.h" | |
20 #include "SkPath.h" | |
21 #include "SkTextMapState.h" | |
22 | |
23 static const int kMaxReservedGlyphs = 64; | |
24 | |
25 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( | |
26 GrContext* context, const SkDeviceProperties& properties) | |
27 : GrTextContext(context, properties) | |
28 , fStroke(SkStrokeRec::kFill_InitStyle) { | |
29 } | |
30 | |
31 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | |
32 } | |
33 | |
34 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, | |
35 const SkPaint& skPaint, | |
36 const char text[], | |
37 size_t byteLength, | |
38 SkScalar x, SkScalar y) { | |
39 SkASSERT(byteLength == 0 || text != NULL); | |
40 | |
41 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { | |
42 return; | |
43 } | |
44 | |
45 this->init(paint, skPaint, byteLength); | |
46 | |
47 | |
48 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
49 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
50 SkGlyphCache* cache = autoCache.getCache(); | |
51 GrFontScaler* scaler = GetGrFontScaler(cache); | |
52 GrTextStrike* strike = | |
53 fContext->getFontCache()->getStrike(scaler, true); | |
54 | |
55 const char* stop = text + byteLength; | |
56 | |
57 // Measure first if needed. | |
58 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | |
59 SkFixed stopX = 0; | |
60 SkFixed stopY = 0; | |
61 | |
62 const char* textPtr = text; | |
63 while (textPtr < stop) { | |
64 // We don't need x, y here, since all subpixel variants will have th e | |
65 // same advance. | |
66 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); | |
67 | |
68 stopX += glyph.fAdvanceX; | |
69 stopY += glyph.fAdvanceY; | |
70 } | |
71 SkASSERT(textPtr == stop); | |
72 | |
73 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; | |
74 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; | |
75 | |
76 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
77 alignX = SkScalarHalf(alignX); | |
78 alignY = SkScalarHalf(alignY); | |
79 } | |
80 | |
81 x -= alignX; | |
82 y -= alignY; | |
83 } | |
84 | |
85 SkAutoKern autokern; | |
86 | |
87 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | |
88 SkFixed halfSampleX, halfSampleY; | |
89 if (cache->isSubpixel()) { | |
90 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits); | |
91 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getM atrix()); | |
92 if (kX_SkAxisAlignment == baseline) { | |
93 halfSampleY = SK_FixedHalf; | |
94 } else if (kY_SkAxisAlignment == baseline) { | |
95 halfSampleX = SK_FixedHalf; | |
96 } | |
97 } else { | |
98 halfSampleX = halfSampleY = SK_FixedHalf; | |
99 } | |
100 | |
101 SkFixed fx = SkScalarToFixed(x) + halfSampleX; | |
jvanverth1
2014/06/09 13:48:07
Doesn't this lead to glyphs being off by half a pi
bungeman-skia
2014/06/09 15:00:07
Yeah, this was done in SkDraw because of SkDraw1Gl
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
| |
102 SkFixed fy = SkScalarToFixed(y) + halfSampleY; | |
103 while (text < stop) { | |
104 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
105 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); | |
106 if (glyph.fWidth) { | |
107 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
108 glyph.getSubXFixed(), | |
109 glyph.getSubYFixed()), | |
110 SkPoint::Make( | |
111 SkFixedToScalar(fx), | |
112 SkFixedToScalar(fy)), | |
113 strike, | |
114 scaler); | |
115 } | |
116 | |
117 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); | |
118 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); | |
119 } | |
120 | |
121 this->finish(); | |
122 } | |
123 | |
124 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, const SkPai nt& skPaint, | |
125 const char text[], | |
126 size_t byteLength, | |
127 const SkScalar pos[], | |
128 SkScalar constY, | |
129 int scalarsPerPosition) { | |
130 SkASSERT(byteLength == 0 || text != NULL); | |
131 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
132 | |
133 // nothing to draw | |
134 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) { | |
135 return; | |
136 } | |
137 | |
138 this->init(paint, skPaint, byteLength); | |
139 | |
140 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
141 | |
142 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
143 SkGlyphCache* cache = autoCache.getCache(); | |
144 GrFontScaler* scaler = GetGrFontScaler(cache); | |
145 GrTextStrike* strike = | |
146 fContext->getFontCache()->getStrike(scaler, true); | |
147 | |
148 SkMatrix ctm = fContext->getMatrix(); | |
149 | |
150 const char* stop = text + byteLength; | |
151 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); | |
152 SkTextMapState tms(SkMatrix::I(), constY); | |
153 SkTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); | |
154 | |
155 SkTDArray<const GrPath*> paths; | |
156 SkTDArray<SkMatrix> transforms; | |
157 SkScalar halfSampleX = 0, halfSampleY = 0; | |
158 | |
159 if (cache->isSubpixel()) { | |
160 // maybe we should skip the rounding if linearText is set | |
161 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm); | |
162 | |
163 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX | |
164 if (kX_SkAxisAlignment == baseline) { | |
165 halfSampleY = SK_ScalarHalf; | |
166 } else if (kY_SkAxisAlignment == baseline) { | |
167 halfSampleX = SK_ScalarHalf; | |
168 } | |
169 #endif | |
170 | |
171 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
172 while (text < stop) { | |
173 tmsProc(tms, pos); | |
174 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
175 if (glyph.fWidth) { | |
176 SkScalar x = tms.fLoc.fX + halfSampleX; | |
jvanverth1
2014/06/09 13:48:07
Same issue with half-pixel alignment as above? (a
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
| |
177 SkScalar y = tms.fLoc.fY + halfSampleY; | |
178 | |
179 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
180 glyph.getSubXFixed(), | |
181 glyph.getSubYFixed()), | |
182 SkPoint::Make(x, y), | |
183 strike, | |
184 scaler); | |
185 } | |
186 pos += scalarsPerPosition; | |
187 } | |
188 } else { | |
189 while (text < stop) { | |
190 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
191 | |
192 if (glyph.fWidth) { | |
193 tmsProc(tms, pos); | |
194 SkPoint loc; | |
195 alignProc(tms.fLoc, glyph, &loc); | |
196 | |
197 loc.fX += halfSampleX; | |
198 loc.fY += halfSampleY; | |
199 | |
200 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
201 glyph.getSubXFixed(), | |
202 glyph.getSubYFixed()), | |
203 loc, | |
204 strike, | |
205 scaler); | |
206 | |
207 } | |
208 pos += scalarsPerPosition; | |
209 } | |
210 } | |
211 } else { // Codepath for "not subpixel" case. | |
212 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
jvanverth1
2014/06/09 13:48:07
Do we need to support non-subpixel layout when dra
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
Kimmo Kinnunen
2014/06/11 12:39:21
Actually, I mean to say that I couldn't get the gl
| |
213 while (text < stop) { | |
214 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
215 | |
216 if (glyph.fWidth) { | |
217 tmsProc(tms, pos); | |
218 | |
219 SkScalar x = tms.fLoc.fX + SK_ScalarHalf; //halfSampleX; | |
220 SkScalar y = tms.fLoc.fY + SK_ScalarHalf; //halfSampleY; | |
221 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
222 glyph.getSubXFixed(), | |
223 glyph.getSubYFixed()), | |
224 SkPoint::Make(x, y), | |
225 strike, | |
226 scaler); | |
227 } | |
228 pos += scalarsPerPosition; | |
229 } | |
230 } else { | |
231 while (text < stop) { | |
232 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
233 | |
234 if (glyph.fWidth) { | |
235 tmsProc(tms, pos); | |
236 | |
237 SkPoint loc; | |
238 alignProc(tms.fLoc, glyph, &loc); | |
239 | |
240 loc.fX += SK_ScalarHalf; //halfSampleX; | |
241 loc.fY += SK_ScalarHalf; //halfSampleY; | |
242 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
243 glyph.getSubXFixed(), | |
244 glyph.getSubYFixed()), | |
245 loc, | |
246 strike, | |
247 scaler); | |
248 } | |
249 pos += scalarsPerPosition; | |
250 } | |
251 } | |
252 } | |
253 | |
254 this->finish(); | |
255 } | |
256 | |
257 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { | |
258 if (paint.getRasterizer()) { | |
259 return false; | |
260 } | |
261 if (paint.getMaskFilter()) { | |
262 return false; | |
263 } | |
264 if (paint.getPathEffect()) { | |
265 return false; | |
266 } | |
267 | |
268 // No hairlines unless we can map the 1 px width to the object space. | |
269 if (paint.getStyle() != SkPaint::kFill_Style | |
270 && paint.getStrokeWidth() == 0 | |
271 && (fContext->getMatrix().hasPerspective() | |
272 || !fContext->getMatrix().invert(NULL))) { | |
273 return false; | |
274 } | |
275 | |
276 // No color bitmap fonts. | |
277 SkScalerContext::Rec rec; | |
278 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); | |
279 return rec.getFormat() != SkMask::kARGB32_Format; | |
280 } | |
281 | |
282 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | |
283 const SkPaint& skPaint, | |
284 size_t textByteLength) { | |
285 GrTextContext::init(paint, skPaint); | |
286 | |
287 if (SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix())) { | |
288 // This is to reproduce SkDraw::drawText_asPaths glyph positions. | |
289 fSkPaint.setLinearText(true); | |
290 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath s; | |
291 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)) ; | |
292 } else { | |
293 fTextRatio = 1.0f; | |
294 } | |
295 // Note: if Chrome ever draws text in very different text sizes in real | |
296 // time, then glyphs need to be looked up untransformed and scaled, but | |
297 // still based on the requested text size. This will let the path cache | |
298 // work. This will probably not be needed until 1) text size is animating, | |
299 // 2) layout change animations are fast enough 3) pictures are rasterized | |
300 // directly to framebuffer. | |
301 | |
302 if (fSkPaint.getStyle() != SkPaint::kFill_Style) { | |
303 SkScalar strokeWidth = fSkPaint.getStrokeWidth(); | |
304 if (0 == strokeWidth) { | |
305 SkMatrix inv; | |
306 if (fContext->getMatrix().invert(&inv)) { | |
jvanverth1
2014/06/09 13:48:07
This won't work for non-uniform scale. And if you'
Kimmo Kinnunen
2014/06/11 12:33:24
Right..
| |
307 strokeWidth = SK_Scalar1 * inv.getScaleX(); | |
308 } else { | |
309 // Avoid unused return value warning. | |
310 SkASSERT(false); | |
311 strokeWidth = SK_Scalar1; | |
312 } | |
313 } | |
314 // Compensate the glyphs being scaled up by fTextRatio by scaling the | |
315 // stroke down. | |
316 fSkPaint.setStrokeWidth(strokeWidth / fTextRatio); | |
317 } | |
318 fStroke = SkStrokeRec(fSkPaint); | |
319 | |
320 // Make glyph cache produce paths geometry for fill. We will stroke them | |
321 // by passing fStroke to drawPath. | |
322 fSkPaint.setStyle(SkPaint::kFill_Style); | |
323 | |
324 fStateRestore.set(fDrawTarget->drawState()); | |
325 | |
326 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | |
327 fContext->getRenderTarget()); | |
328 | |
329 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | |
330 kZero_StencilOp, | |
331 kZero_StencilOp, | |
332 kNotEqual_StencilFunc, | |
333 0xffff, | |
334 0x0000, | |
335 0xffff); | |
336 | |
337 *fDrawTarget->drawState()->stencil() = kStencilPass; | |
338 | |
339 size_t reserveAmount; | |
340 switch (skPaint.getTextEncoding()) { | |
341 default: | |
342 SkASSERT(false); | |
343 case SkPaint::kUTF8_TextEncoding: | |
344 reserveAmount = textByteLength; | |
345 break; | |
346 case SkPaint::kUTF16_TextEncoding: | |
347 reserveAmount = textByteLength / 2; | |
348 break; | |
349 case SkPaint::kUTF32_TextEncoding: | |
350 case SkPaint::kGlyphID_TextEncoding: | |
351 reserveAmount = textByteLength / 4; | |
352 break; | |
353 } | |
354 fPaths.setReserve(reserveAmount); | |
355 fTransforms.setReserve(reserveAmount); | |
356 } | |
357 | |
358 inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, | |
359 const SkPoint& pos, | |
360 GrTextStrike* strike, | |
361 GrFontScaler* scaler) { | |
362 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | |
363 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
364 return; | |
365 } | |
366 | |
367 if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { | |
368 if (!fTmpPath.isEmpty()) { | |
369 *fPaths.append() = fContext->createPath(fTmpPath, fStroke); | |
370 SkMatrix* t = fTransforms.append(); | |
371 t->setTranslate(pos.fX, pos.fY); | |
372 t->preScale(fTextRatio, fTextRatio); | |
373 } | |
374 } | |
375 } | |
376 | |
377 void GrStencilAndCoverTextContext::finish() { | |
378 if (fPaths.count() > 0) { | |
379 fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()), | |
380 fPaths.begin(), fTransforms.begin(), | |
381 SkPath::kWinding_FillType, fStroke.getStyle()); | |
382 for (int i = 0; i < fPaths.count(); ++i) { | |
383 fPaths[i]->unref(); | |
384 } | |
385 if (fPaths.count() > kMaxReservedGlyphs) { | |
386 fPaths.reset(); | |
387 fTransforms.reset(); | |
388 } else { | |
389 fPaths.rewind(); | |
390 fTransforms.rewind(); | |
391 } | |
392 } | |
393 fTmpPath.reset(); | |
394 | |
395 fDrawTarget->drawState()->stencil()->setDisabled(); | |
396 fStateRestore.set(NULL); | |
397 GrTextContext::finish(); | |
398 } | |
399 | |
OLD | NEW |