blob: 9f394f901875364ff3fc59570716a215410f86f1 [file] [log] [blame]
kkinnunenc6cb56f2014-06-24 00:12:27 -07001/*
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"
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrAtlasTextContext.h"
robertphillipsea461502015-05-26 11:38:03 -070010#include "GrDrawContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070012#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070013#include "GrPathRange.h"
bsalomond309e7a2015-04-30 14:18:54 -070014#include "GrResourceProvider.h"
jvanverthaab626c2014-10-16 08:04:39 -070015#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070016#include "SkDraw.h"
17#include "SkDrawProcs.h"
18#include "SkGlyphCache.h"
19#include "SkGpuDevice.h"
20#include "SkPath.h"
21#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070022#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023
bsalomon1fcc01c2015-09-09 09:48:06 -070024#include "batches/GrDrawPathBatch.h"
25
joshualitt6e8cd962015-03-20 10:30:14 -070026GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070027 const SkSurfaceProps& surfaceProps)
cdalton3bd909a2015-10-05 14:57:20 -070028 : INHERITED(context, surfaceProps) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070029}
30
joshualitt6e8cd962015-03-20 10:30:14 -070031GrStencilAndCoverTextContext*
robertphillipsf6703fa2015-09-01 05:36:47 -070032GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
33 GrStencilAndCoverTextContext* textContext =
34 new GrStencilAndCoverTextContext(context, surfaceProps);
35 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
jvanverth8c27a182014-10-14 08:45:50 -070036
37 return textContext;
38}
39
kkinnunenc6cb56f2014-06-24 00:12:27 -070040GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
41}
42
cdaltone68f7362015-03-25 14:02:37 -070043bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
44 const GrClip& clip,
45 const GrPaint& paint,
46 const SkPaint& skPaint,
47 const SkMatrix& viewMatrix) {
48 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070049 return false;
50 }
cdaltone68f7362015-03-25 14:02:37 -070051 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070052 return false;
53 }
kkinnunen50b58e62015-05-18 23:02:07 -070054 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070055 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070056 return false;
57 }
jvanverth0fedb192014-10-08 09:07:27 -070058 }
cdalton7d5c9502015-10-03 13:28:35 -070059 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
60 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070061}
62
robertphillipsf6703fa2015-09-01 05:36:47 -070063void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080064 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080065 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080066 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080067 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080068 const char text[],
69 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070070 SkScalar x, SkScalar y,
71 const SkIRect& regionClipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -070072 TextRun run(skPaint);
73 run.setText(text, byteLength, x, y, fContext, &fSurfaceProps);
74 run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
75}
jvanverthaab626c2014-10-16 08:04:39 -070076
cdalton3bd909a2015-10-05 14:57:20 -070077void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
78 const GrClip& clip,
79 const GrPaint& paint,
80 const SkPaint& skPaint,
81 const SkMatrix& viewMatrix,
82 const char text[],
83 size_t byteLength,
84 const SkScalar pos[],
85 int scalarsPerPosition,
86 const SkPoint& offset,
87 const SkIRect& regionClipBounds) {
88 TextRun run(skPaint);
89 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps);
90 run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
91}
92
93////////////////////////////////////////////////////////////////////////////////////////////////////
94
95GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
96 : fStroke(fontAndStroke),
97 fFont(fontAndStroke) {
98 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
99
100 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
101 // rendering API for stroking).
102 fFont.setStyle(SkPaint::kFill_Style);
103
104 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
105 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
106 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
107 kStdFakeBoldInterpKeys,
108 kStdFakeBoldInterpValues,
109 kStdFakeBoldInterpLength);
110 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
111 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
112 true /*strokeAndFill*/);
113
114 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700115 }
116
cdalton3bd909a2015-10-05 14:57:20 -0700117 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
118 // We can draw the glyphs from canonically sized paths.
119 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
120 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700121
cdalton3bd909a2015-10-05 14:57:20 -0700122 // Compensate for the glyphs being scaled by fTextRatio.
123 if (!fStroke.isFillStyle()) {
124 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
125 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
126 }
127
128 fFont.setLinearText(true);
129 fFont.setLCDRenderText(false);
130 fFont.setAutohinted(false);
131 fFont.setHinting(SkPaint::kNo_Hinting);
132 fFont.setSubpixelText(true);
133 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
134
135 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
136 0 == fFont.getTextSkewX() &&
137 !fFont.isFakeBoldText() &&
138 !fFont.isVerticalText();
139 } else {
140 fTextRatio = fTextInverseRatio = 1.0f;
141 fUsingRawGlyphPaths = false;
142 }
143
144 // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
145 fLocalMatrix.setScale(fTextRatio, fTextRatio);
146}
147
148GrStencilAndCoverTextContext::TextRun::~TextRun() {
149}
150
151void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
152 SkScalar x, SkScalar y, GrContext* ctx,
153 const SkSurfaceProps* surfaceProps) {
154 SkASSERT(byteLength == 0 || text != nullptr);
155
156 SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
157 SkGlyphCache* glyphCache = autoGlyphCache.getCache();
158
159 fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
160 GrPathRendering::kTranslate_PathTransformType,
161 fFont.countText(text, byteLength)));
162
163 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
jvanverthaab626c2014-10-16 08:04:39 -0700164
jvanverthaab626c2014-10-16 08:04:39 -0700165 const char* stop = text + byteLength;
166
167 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700168 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700169 SkFixed stopX = 0;
170 SkFixed stopY = 0;
171
172 const char* textPtr = text;
173 while (textPtr < stop) {
174 // We don't need x, y here, since all subpixel variants will have the
175 // same advance.
cdalton3bd909a2015-10-05 14:57:20 -0700176 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700177
178 stopX += glyph.fAdvanceX;
179 stopY += glyph.fAdvanceY;
180 }
181 SkASSERT(textPtr == stop);
182
183 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
184 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
185
cdalton3bd909a2015-10-05 14:57:20 -0700186 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700187 alignX = SkScalarHalf(alignX);
188 alignY = SkScalarHalf(alignY);
189 }
190
191 x -= alignX;
192 y -= alignY;
193 }
194
195 SkAutoKern autokern;
196
197 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
198
199 SkFixed fx = SkScalarToFixed(x);
200 SkFixed fy = SkScalarToFixed(y);
201 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700202 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700203 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700204 if (glyph.fWidth) {
bsalomon1fcc01c2015-09-09 09:48:06 -0700205 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700206 }
207
bungemand709ea82015-03-17 07:23:39 -0700208 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
209 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700210 }
jvanverthaab626c2014-10-16 08:04:39 -0700211}
212
cdalton3bd909a2015-10-05 14:57:20 -0700213void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
214 const SkScalar pos[], int scalarsPerPosition,
215 const SkPoint& offset, GrContext* ctx,
216 const SkSurfaceProps* surfaceProps) {
halcanary96fcdcc2015-08-27 07:41:13 -0700217 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700218 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
219
cdalton3bd909a2015-10-05 14:57:20 -0700220 SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
221 SkGlyphCache* glyphCache = autoGlyphCache.getCache();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700222
cdalton3bd909a2015-10-05 14:57:20 -0700223 fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
224 GrPathRendering::kTranslate_PathTransformType,
225 fFont.countText(text, byteLength)));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700226
cdalton3bd909a2015-10-05 14:57:20 -0700227 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700228
kkinnunenc6cb56f2014-06-24 00:12:27 -0700229 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700230
cdalton38e13ad2014-11-07 06:02:15 -0800231 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700232 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800233 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700234 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800235 if (glyph.fWidth) {
236 SkPoint tmsLoc;
237 tmsProc(pos, &tmsLoc);
238 SkPoint loc;
239 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700240
bsalomon1fcc01c2015-09-09 09:48:06 -0700241 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700242 }
cdalton38e13ad2014-11-07 06:02:15 -0800243 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700244 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700245}
246
cdalton3bd909a2015-10-05 14:57:20 -0700247GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx,
248 SkGlyphCache* glyphCache) {
249 SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface()
250 : glyphCache->getScalerContext()->getTypeface();
251 const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor();
kkinnunen50b58e62015-05-18 23:02:07 -0700252
253 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
cdalton3bd909a2015-10-05 14:57:20 -0700254 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
kkinnunen50b58e62015-05-18 23:02:07 -0700255 GrUniqueKey glyphKey;
256 GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
257 reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
258 reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
259 if (strokeDataCount > 0) {
cdalton3bd909a2015-10-05 14:57:20 -0700260 fStroke.asUniqueKeyFragment(&builder[2]);
kkinnunen50b58e62015-05-18 23:02:07 -0700261 }
bsalomon24db3b12015-01-23 04:24:04 -0800262 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700263
264 SkAutoTUnref<GrPathRange> glyphs(
kkinnunen50b58e62015-05-18 23:02:07 -0700265 static_cast<GrPathRange*>(
266 ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
halcanary96fcdcc2015-08-27 07:41:13 -0700267 if (nullptr == glyphs) {
cdalton3bd909a2015-10-05 14:57:20 -0700268 glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke));
kkinnunen50b58e62015-05-18 23:02:07 -0700269 ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
270 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700271 SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
cdalton855d83f2014-09-18 13:51:53 -0700272 }
273
274 return glyphs.detach();
275}
276
cdalton3bd909a2015-10-05 14:57:20 -0700277inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
278 const SkPoint& pos) {
bsalomon1fcc01c2015-09-09 09:48:06 -0700279 // Stick the glyphs we can't draw into the fallback arrays.
280 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
281 fFallbackIndices.push_back(glyph.getGlyphID());
cdalton7d5c9502015-10-03 13:28:35 -0700282 fFallbackPositions.push_back(pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700283 } else {
cdalton7d5c9502015-10-03 13:28:35 -0700284 float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
bsalomon1fcc01c2015-09-09 09:48:06 -0700285 fDraw->append(glyph.getGlyphID(), translate);
cdaltonb2808cd2014-07-25 14:13:57 -0700286 }
cdalton20b373c2014-12-01 08:38:55 -0800287}
288
cdalton3bd909a2015-10-05 14:57:20 -0700289void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc,
290 GrRenderTarget* rt,
291 const GrClip& clip,
292 const GrPaint& paint,
293 const SkMatrix& viewMatrix,
294 const SkIRect& regionClipBounds,
295 GrTextContext* fallbackTextContext,
296 const SkPaint& originalSkPaint) const {
297 SkASSERT(fDraw);
robertphillipsea461502015-05-26 11:38:03 -0700298
cdalton3bd909a2015-10-05 14:57:20 -0700299 if (fDraw->count()) {
300 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
301 SkASSERT(rt->isStencilBufferMultisampled() || !paint.isAntiAlias());
302 pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, paint.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700303
304 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
305 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700306 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700307 kNotEqual_StencilFunc,
308 0xffff,
309 0x0000,
310 0xffff);
311
312 *pipelineBuilder.stencil() = kStencilPass;
313
cdalton3bd909a2015-10-05 14:57:20 -0700314 SkMatrix drawMatrix(viewMatrix);
315 drawMatrix.preScale(fTextRatio, fTextRatio);
316
317 dc->drawPathsFromRange(&pipelineBuilder, drawMatrix, fLocalMatrix, paint.getColor(), fDraw,
318 GrPathRendering::kWinding_FillType);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700319 }
320
bsalomon1fcc01c2015-09-09 09:48:06 -0700321 if (fFallbackIndices.count()) {
322 SkASSERT(fFallbackPositions.count() == fFallbackIndices.count());
cdalton20b373c2014-12-01 08:38:55 -0800323
cdalton3bd909a2015-10-05 14:57:20 -0700324 enum { kPreservedFlags = SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
325 SkPaint::kLCDRenderText_Flag | SkPaint::kAutoHinting_Flag };
326
327 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700328 fStroke.applyToPaint(&fallbackSkPaint);
329 if (!fStroke.isFillStyle()) {
330 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800331 }
cdalton7d5c9502015-10-03 13:28:35 -0700332 fallbackSkPaint.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
333 fallbackSkPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
cdalton3bd909a2015-10-05 14:57:20 -0700334 fallbackSkPaint.setHinting(fFont.getHinting());
335 fallbackSkPaint.setFlags((fFont.getFlags() & kPreservedFlags) |
336 (originalSkPaint.getFlags() & ~kPreservedFlags));
cdalton7d5c9502015-10-03 13:28:35 -0700337 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color
338 // glyphs show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
339 fallbackSkPaint.setSubpixelText(false);
cdalton3bd909a2015-10-05 14:57:20 -0700340 fallbackSkPaint.setTextSize(fFont.getTextSize() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800341
cdalton3bd909a2015-10-05 14:57:20 -0700342 fallbackTextContext->drawPosText(dc, rt, clip, paint, fallbackSkPaint, viewMatrix,
343 (char*)fFallbackIndices.begin(),
344 sizeof(uint16_t) * fFallbackIndices.count(),
345 fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0),
346 regionClipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800347 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700348}