Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(802)

Side by Side Diff: src/gpu/GrStencilAndCoverTextContext.cpp

Issue 196133014: Implement text rendering with NVPR (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: rebase and fix postext Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698