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 "SkGlyphCache.h" | |
18 #include "SkGpuDevice.h" | |
19 #include "SkPath.h" | |
20 | |
21 static const int kBaseSCFontSize = 64; | |
22 static const int kMaxReservedGlyphs = 64; | |
23 | |
24 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( | |
25 GrContext* context, const SkDeviceProperties& properties) | |
26 : GrTextContext(context, properties) | |
27 , fStroke(SkStrokeRec::kFill_InitStyle) { | |
28 } | |
29 | |
30 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | |
31 } | |
32 | |
33 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, | |
34 const SkPaint& skPaint, | |
35 const char text[], | |
36 size_t byteLength, | |
37 SkScalar x, SkScalar y) { | |
38 SkASSERT(byteLength == 0 || text != NULL); | |
39 | |
40 // nothing to draw | |
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 // don't need x, y here, since all subpixel variants will have the | |
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 fx = SkScalarToFixed(x) + SK_FixedHalf; | |
88 SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; | |
89 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | |
90 | |
91 while (text < stop) { | |
92 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); | |
jvanverth1
2014/03/24 14:51:05
I believe fx, fy should always be 0 in this case.
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
| |
93 | |
94 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); | |
95 | |
96 if (glyph.fWidth) { | |
97 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
98 glyph.getSubXFixed(), | |
99 glyph.getSubYFixed()), | |
100 SkPoint::Make(SkFixedToScalar(fx), | |
101 SkFixedToScalar(fy)), | |
102 strike, | |
103 scaler); | |
104 } | |
105 | |
106 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); | |
107 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); | |
108 } | |
109 | |
110 this->finish(); | |
111 } | |
112 | |
113 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, const SkPai nt& skPaint, | |
114 const char text[], | |
115 size_t byteLength, | |
116 const SkScalar pos[], | |
117 SkScalar constY, | |
118 int scalarsPerPosition) { | |
119 SkASSERT(byteLength == 0 || text != NULL); | |
120 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
121 | |
122 // nothing to draw | |
123 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) { | |
124 return; | |
125 } | |
126 | |
127 this->init(paint, skPaint, byteLength); | |
128 | |
129 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
130 | |
131 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
132 SkGlyphCache* cache = autoCache.getCache(); | |
133 GrFontScaler* scaler = GetGrFontScaler(cache); | |
134 GrTextStrike* strike = | |
135 fContext->getFontCache()->getStrike(scaler, true); | |
136 | |
137 const char* stop = text + byteLength; | |
138 | |
139 SkTDArray<const GrPath*> paths; | |
140 SkTDArray<SkMatrix> transforms; | |
141 | |
142 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
143 while (text < stop) { | |
144 // the last 2 parameters are ignored | |
145 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
146 | |
147 if (glyph.fWidth) { | |
148 SkScalar x = pos[0]; | |
149 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; | |
150 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
151 glyph.getSubXFixed(), | |
152 glyph.getSubYFixed()), | |
153 SkPoint::Make(x, y), | |
154 strike, | |
155 scaler); | |
156 } | |
157 | |
158 pos += scalarsPerPosition; | |
159 } | |
160 } else { | |
161 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; | |
162 while (text < stop) { | |
163 // the last 2 parameters are ignored | |
164 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
165 | |
166 if (glyph.fWidth) { | |
167 SkScalar x = pos[0]; | |
168 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; | |
169 x -= SkFixedToScalar((glyph.fAdvanceX >> alignShift)); | |
170 y -= SkFixedToScalar((glyph.fAdvanceY >> alignShift)); | |
171 | |
172 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
173 glyph.getSubXFixed(), | |
174 glyph.getSubYFixed()), | |
175 SkPoint::Make(x, y), | |
176 strike, | |
177 scaler); | |
178 } | |
179 pos += scalarsPerPosition; | |
180 } | |
181 } | |
182 | |
183 this->finish(); | |
184 } | |
185 | |
186 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { | |
187 return !paint.getRasterizer() | |
188 && !paint.getMaskFilter() | |
189 && (paint.getStyle() == SkPaint::kFill_Style | |
190 || paint.getStrokeWidth() > 0); | |
191 | |
192 } | |
193 | |
194 static bool has_thick_frame(const SkPaint& paint) { | |
195 return paint.getStrokeWidth() > 0 && | |
196 paint.getStyle() != SkPaint::kFill_Style; | |
197 } | |
198 | |
199 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | |
200 const SkPaint& skPaint, | |
201 size_t textByteLength) { | |
202 GrTextContext::init(paint, skPaint); | |
203 fTextRatio = fSkPaint.getTextSize() / kBaseSCFontSize; | |
204 fSkPaint.setTextSize(SkIntToScalar(kBaseSCFontSize)); | |
205 fSkPaint.setLCDRenderText(false); | |
206 fSkPaint.setAutohinted(false); | |
207 fSkPaint.setSubpixelText(false); | |
jvanverth1
2014/03/24 14:51:05
You may want to always enable subpixel text so you
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
| |
208 if (has_thick_frame(fSkPaint)) { | |
209 // Compensate the glyphs being scaled up by fTextRatio, by scaling the | |
210 // stroke down. | |
211 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio); | |
212 } | |
213 fStroke = SkStrokeRec(fSkPaint); | |
214 | |
215 // Make glyph cache produce paths geometry for fill. We will stroke them | |
216 // by passing fStroke to drawPath. | |
217 fSkPaint.setStyle(SkPaint::kFill_Style); | |
218 | |
219 fStateRestore.set(fDrawTarget->drawState()); | |
220 | |
221 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | |
222 fContext->getRenderTarget()); | |
223 | |
224 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | |
225 kZero_StencilOp, | |
226 kZero_StencilOp, | |
227 kNotEqual_StencilFunc, | |
228 0xffff, | |
229 0x0000, | |
230 0xffff); | |
231 | |
232 *fDrawTarget->drawState()->stencil() = kStencilPass; | |
233 | |
234 size_t reserveAmount; | |
235 switch (skPaint.getTextEncoding()) { | |
236 default: | |
bsalomon
2014/03/24 13:53:37
tiny style nit: we indent the case/defaults (much
Kimmo Kinnunen
2014/03/25 12:32:48
Done.
| |
237 SkASSERT(false); | |
238 case SkPaint::kUTF8_TextEncoding: | |
239 reserveAmount = textByteLength; | |
240 break; | |
241 case SkPaint::kUTF16_TextEncoding: | |
242 reserveAmount = textByteLength / 2; | |
243 break; | |
244 case SkPaint::kUTF32_TextEncoding: | |
245 case SkPaint::kGlyphID_TextEncoding: | |
246 reserveAmount = textByteLength / 4; | |
247 break; | |
248 } | |
249 fPaths.setReserve(reserveAmount); | |
250 fTransforms.setReserve(reserveAmount); | |
251 } | |
252 | |
253 inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, | |
254 const SkPoint& pos, | |
255 GrTextStrike* strike, | |
256 GrFontScaler* scaler) { | |
257 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | |
258 | |
259 if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { | |
260 *fPaths.append() = fContext->createPath(fTmpPath, fStroke); | |
261 SkMatrix* t = fTransforms.append(); | |
262 t->setTranslate(pos.fX, pos.fY); | |
263 t->preScale(fTextRatio, fTextRatio); | |
264 } | |
265 } | |
266 | |
267 void GrStencilAndCoverTextContext::finish() { | |
268 if (fPaths.count() > 0) { | |
269 fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()), | |
270 fPaths.begin(), fTransforms.begin(), | |
271 SkPath::kWinding_FillType, fStroke.getStyle()); | |
272 for (int i = 0; i < fPaths.count(); ++i) { | |
273 fPaths[i]->unref(); | |
274 } | |
275 if (fPaths.count() > kMaxReservedGlyphs) { | |
276 fPaths.reset(); | |
277 fTransforms.reset(); | |
278 } else { | |
279 fPaths.rewind(); | |
280 fTransforms.rewind(); | |
281 } | |
282 } | |
283 fTmpPath.reset(); | |
284 | |
285 fDrawTarget->drawState()->stencil()->setDisabled(); | |
286 fStateRestore.set(NULL); | |
287 GrTextContext::finish(); | |
288 } | |
289 | |
OLD | NEW |