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

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: fix VS "performance warning" converting int to bool 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
« no previous file with comments | « src/gpu/GrStencilAndCoverTextContext.h ('k') | src/gpu/SkGpuDevice.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "SkTextMapStateProc.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 is the slow path, mainly used by Skia unit tests. The other
46 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
47 // order to match the glyph positions that the other code paths produce, we
48 // must also use device-space dependent glyph cache. This has the
49 // side-effect that the glyph shape outline will be in device-space,
50 // too. This in turn has the side-effect that NVPR can not stroke the paths,
51 // as the stroke in NVPR is defined in object-space.
52 // NOTE: here we have following coincidence that works at the moment:
53 // - When using the device-space glyphs, the transforms we pass to NVPR
54 // instanced drawing are the global transforms, and the view transform is
55 // identity. NVPR can not use non-affine transforms in the instanced
56 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
57 // will turn off the use of device-space glyphs when perspective transforms
58 // are in use.
59
60 fGlyphTransform = fContext->getMatrix();
61
62 this->init(paint, skPaint, byteLength);
63
64 SkMatrix* glyphCacheTransform = NULL;
65 // Transform our starting point.
66 if (fNeedsDeviceSpaceGlyphs) {
67 SkPoint loc;
68 fGlyphTransform.mapXY(x, y, &loc);
69 x = loc.fX;
70 y = loc.fY;
71 glyphCacheTransform = &fGlyphTransform;
72 }
73
74 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
75 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform );
76 SkGlyphCache* cache = autoCache.getCache();
77 GrFontScaler* scaler = GetGrFontScaler(cache);
78 GrTextStrike* strike =
79 fContext->getFontCache()->getStrike(scaler, true);
80
81 const char* stop = text + byteLength;
82
83 // Measure first if needed.
84 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
85 SkFixed stopX = 0;
86 SkFixed stopY = 0;
87
88 const char* textPtr = text;
89 while (textPtr < stop) {
90 // We don't need x, y here, since all subpixel variants will have th e
91 // same advance.
92 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
93
94 stopX += glyph.fAdvanceX;
95 stopY += glyph.fAdvanceY;
96 }
97 SkASSERT(textPtr == stop);
98
99 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
100 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
101
102 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
103 alignX = SkScalarHalf(alignX);
104 alignY = SkScalarHalf(alignY);
105 }
106
107 x -= alignX;
108 y -= alignY;
109 }
110
111 SkAutoKern autokern;
112
113 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
114
115 SkFixed fx = SkScalarToFixed(x);
116 SkFixed fy = SkScalarToFixed(y);
117 while (text < stop) {
118 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
119 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
120 if (glyph.fWidth) {
121 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
122 glyph.getSubXFixed(),
123 glyph.getSubYFixed()),
124 SkPoint::Make(
125 SkFixedToScalar(fx),
126 SkFixedToScalar(fy)),
127 strike,
128 scaler);
129 }
130
131 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
132 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
133 }
134
135 this->finish();
136 }
137
138 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
139 const SkPaint& skPaint,
140 const char text[],
141 size_t byteLength,
142 const SkScalar pos[],
143 SkScalar constY,
144 int scalarsPerPosition) {
145 SkASSERT(byteLength == 0 || text != NULL);
146 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
147
148 // nothing to draw
149 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
150 return;
151 }
152
153 // This is the fast path. Here we do not bake in the device-transform to
154 // the glyph outline or the advances. This is because we do not need to
155 // position the glyphs at all, since the caller has done the positioning.
156 // The positioning is based on SkPaint::measureText of individual
157 // glyphs. That already uses glyph cache without device transforms. Device
158 // transform is not part of SkPaint::measureText API, and thus we use the
159 // same glyphs as what were measured.
160 fGlyphTransform.reset();
161
162 this->init(paint, skPaint, byteLength);
163
164 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
165
166 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
167 SkGlyphCache* cache = autoCache.getCache();
168 GrFontScaler* scaler = GetGrFontScaler(cache);
169 GrTextStrike* strike =
170 fContext->getFontCache()->getStrike(scaler, true);
171
172 const char* stop = text + byteLength;
173 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
174 SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition);
175
176 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
177 while (text < stop) {
178 SkPoint loc;
179 tmsProc(pos, &loc);
180 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
181 if (glyph.fWidth) {
182 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
183 glyph.getSubXFixed(),
184 glyph.getSubYFixed()),
185 loc,
186 strike,
187 scaler);
188 }
189 pos += scalarsPerPosition;
190 }
191 } else {
192 while (text < stop) {
193 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
194 if (glyph.fWidth) {
195 SkPoint tmsLoc;
196 tmsProc(pos, &tmsLoc);
197 SkPoint loc;
198 alignProc(tmsLoc, glyph, &loc);
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
212 this->finish();
213 }
214
215 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
216 if (paint.getRasterizer()) {
217 return false;
218 }
219 if (paint.getMaskFilter()) {
220 return false;
221 }
222 if (paint.getPathEffect()) {
223 return false;
224 }
225
226 // No hairlines unless we can map the 1 px width to the object space.
227 if (paint.getStyle() == SkPaint::kStroke_Style
228 && paint.getStrokeWidth() == 0
229 && fContext->getMatrix().hasPerspective()) {
230 return false;
231 }
232
233 // No color bitmap fonts.
234 SkScalerContext::Rec rec;
235 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
236 return rec.getFormat() != SkMask::kARGB32_Format;
237 }
238
239 void GrStencilAndCoverTextContext::init(const GrPaint& paint,
240 const SkPaint& skPaint,
241 size_t textByteLength) {
242 GrTextContext::init(paint, skPaint);
243
244 bool otherBackendsWillDrawAsPaths =
245 SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix());
246
247 if (otherBackendsWillDrawAsPaths) {
248 // This is to reproduce SkDraw::drawText_asPaths glyph positions.
249 fSkPaint.setLinearText(true);
250 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath s;
251 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)) ;
252 if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
253 // Compensate the glyphs being scaled up by fTextRatio by scaling th e
254 // stroke down.
255 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio);
256 }
257 fNeedsDeviceSpaceGlyphs = false;
258 } else {
259 fTextRatio = 1.0f;
260 fNeedsDeviceSpaceGlyphs = (fGlyphTransform.getType() &
261 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0;
262 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
263 SkASSERT(!fGlyphTransform.hasPerspective());
264 if (fNeedsDeviceSpaceGlyphs) {
265 fPaint.localCoordChangeInverse(fGlyphTransform);
266 fContext->setIdentityMatrix();
267 }
268 }
269
270 fStroke = SkStrokeRec(fSkPaint);
271
272 if (fNeedsDeviceSpaceGlyphs) {
273 // The whole shape is baked into the glyph. Make NVPR just fill the
274 // baked shape.
275 fStroke.setStrokeStyle(-1, false);
276 } else {
277 if (fSkPaint.getStrokeWidth() == 0.0f) {
278 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) {
279 fStroke.setStrokeStyle(-1, false);
280 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) {
281 // Approximate hairline stroke.
282 const SkMatrix& ctm = fContext->getMatrix();
283 SkScalar strokeWidth = SK_Scalar1 /
284 (fTextRatio * SkVector::Make(ctm.getScaleX(), ctm.getSkewY() ).length());
285 fStroke.setStrokeStyle(strokeWidth, false);
286 }
287 }
288
289 // Make glyph cache produce paths geometry for fill. We will stroke them
290 // by passing fStroke to drawPath. This is the fast path.
291 fSkPaint.setStyle(SkPaint::kFill_Style);
292 }
293 fStateRestore.set(fDrawTarget->drawState());
294
295 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
296 fContext->getRenderTarget());
297
298 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
299 kZero_StencilOp,
300 kZero_StencilOp,
301 kNotEqual_StencilFunc,
302 0xffff,
303 0x0000,
304 0xffff);
305
306 *fDrawTarget->drawState()->stencil() = kStencilPass;
307
308 size_t reserveAmount;
309 switch (skPaint.getTextEncoding()) {
310 default:
311 SkASSERT(false);
312 case SkPaint::kUTF8_TextEncoding:
313 reserveAmount = textByteLength;
314 break;
315 case SkPaint::kUTF16_TextEncoding:
316 reserveAmount = textByteLength / 2;
317 break;
318 case SkPaint::kUTF32_TextEncoding:
319 case SkPaint::kGlyphID_TextEncoding:
320 reserveAmount = textByteLength / 4;
321 break;
322 }
323 fPaths.setReserve(reserveAmount);
324 fTransforms.setReserve(reserveAmount);
325 }
326
327 inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID,
328 const SkPoint& pos,
329 GrTextStrike* strike,
330 GrFontScaler* scaler) {
331 GrGlyph* glyph = strike->getGlyph(glyphID, scaler);
332 if (NULL == glyph || glyph->fBounds.isEmpty()) {
333 return;
334 }
335
336 if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) {
337 if (!fTmpPath.isEmpty()) {
338 *fPaths.append() = fContext->createPath(fTmpPath, fStroke);
339 SkMatrix* t = fTransforms.append();
340 t->setTranslate(pos.fX, pos.fY);
341 t->preScale(fTextRatio, fTextRatio);
342 }
343 }
344 }
345
346 void GrStencilAndCoverTextContext::finish() {
347 if (fPaths.count() > 0) {
348 fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()),
349 fPaths.begin(), fTransforms.begin(),
350 SkPath::kWinding_FillType, fStroke.getStyle());
351
352 for (int i = 0; i < fPaths.count(); ++i) {
353 fPaths[i]->unref();
354 }
355 if (fPaths.count() > kMaxReservedGlyphs) {
356 fPaths.reset();
357 fTransforms.reset();
358 } else {
359 fPaths.rewind();
360 fTransforms.rewind();
361 }
362 }
363 fTmpPath.reset();
364
365 fDrawTarget->drawState()->stencil()->setDisabled();
366 fStateRestore.set(NULL);
367 if (fNeedsDeviceSpaceGlyphs) {
368 fContext->setMatrix(fGlyphTransform);
369 }
370 GrTextContext::finish();
371 }
372
OLDNEW
« no previous file with comments | « src/gpu/GrStencilAndCoverTextContext.h ('k') | src/gpu/SkGpuDevice.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698