blob: 4ae5ec43dfffa7307e5baca9a41f14f47ad8992a [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"
cdaltoncdd79072015-10-05 15:37:35 -070020#include "SkGrPriv.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070021#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070022#include "SkTextBlobRunIterator.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070024#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070025
bsalomon1fcc01c2015-09-09 09:48:06 -070026#include "batches/GrDrawPathBatch.h"
27
cdaltoncdd79072015-10-05 15:37:35 -070028template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
29 SkASSERT(*val);
30 delete *val;
31}
32
33template<typename T> static void delete_hash_table_entry(T* val) {
34 SkASSERT(*val);
35 delete *val;
36}
37
joshualitt6e8cd962015-03-20 10:30:14 -070038GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070039 const SkSurfaceProps& surfaceProps)
cdaltoncdd79072015-10-05 15:37:35 -070040 : INHERITED(context, surfaceProps),
41 fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070042}
43
joshualitt6e8cd962015-03-20 10:30:14 -070044GrStencilAndCoverTextContext*
robertphillipsf6703fa2015-09-01 05:36:47 -070045GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
46 GrStencilAndCoverTextContext* textContext =
47 new GrStencilAndCoverTextContext(context, surfaceProps);
48 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
jvanverth8c27a182014-10-14 08:45:50 -070049
50 return textContext;
51}
52
kkinnunenc6cb56f2014-06-24 00:12:27 -070053GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
cdaltoncdd79072015-10-05 15:37:35 -070054 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
55 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070056}
57
cdaltoncdd79072015-10-05 15:37:35 -070058bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070059 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070060 return false;
61 }
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070063 return false;
64 }
kkinnunen50b58e62015-05-18 23:02:07 -070065 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070066 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070067 return false;
68 }
jvanverth0fedb192014-10-08 09:07:27 -070069 }
cdalton7d5c9502015-10-03 13:28:35 -070070 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
71 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070072}
73
robertphillips433625e2015-12-04 06:58:16 -080074void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc,
joshualitt570d2f82015-02-25 13:19:48 -080075 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080076 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080077 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080078 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080079 const char text[],
80 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070081 SkScalar x, SkScalar y,
cdaltoncdd79072015-10-05 15:37:35 -070082 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -070083 TextRun run(skPaint);
robertphillips433625e2015-12-04 06:58:16 -080084 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
cdalton8585dd22015-10-08 08:04:09 -070085 run.setText(text, byteLength, x, y);
86 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -070087 fFallbackTextContext, skPaint);
cdalton3bd909a2015-10-05 14:57:20 -070088}
jvanverthaab626c2014-10-16 08:04:39 -070089
robertphillips433625e2015-12-04 06:58:16 -080090void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc,
cdalton3bd909a2015-10-05 14:57:20 -070091 const GrClip& clip,
92 const GrPaint& paint,
93 const SkPaint& skPaint,
94 const SkMatrix& viewMatrix,
95 const char text[],
96 size_t byteLength,
97 const SkScalar pos[],
98 int scalarsPerPosition,
99 const SkPoint& offset,
cdaltoncdd79072015-10-05 15:37:35 -0700100 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -0700101 TextRun run(skPaint);
robertphillips433625e2015-12-04 06:58:16 -0800102 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
cdalton8585dd22015-10-08 08:04:09 -0700103 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
104 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -0700105 fFallbackTextContext, skPaint);
106}
107
robertphillips433625e2015-12-04 06:58:16 -0800108void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700109 const GrClip& clip, const SkPaint& skPaint,
110 const SkMatrix& viewMatrix,
111 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
112 SkDrawFilter* drawFilter,
113 const SkIRect& clipBounds) {
114 if (!this->internalCanDraw(skPaint)) {
robertphillips433625e2015-12-04 06:58:16 -0800115 fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y,
cdaltoncdd79072015-10-05 15:37:35 -0700116 drawFilter, clipBounds);
117 return;
118 }
119
120 if (drawFilter || skPaint.getPathEffect()) {
121 // This draw can't be cached.
robertphillips433625e2015-12-04 06:58:16 -0800122 INHERITED::drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter,
cdaltoncdd79072015-10-05 15:37:35 -0700123 clipBounds);
124 return;
125 }
126
127 if (fContext->abandoned()) {
128 return;
129 }
130
131 GrPaint paint;
132 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) {
133 return;
134 }
135
136 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
robertphillips433625e2015-12-04 06:58:16 -0800137 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
cdaltoncdd79072015-10-05 15:37:35 -0700138
139 TextBlob::Iter iter(blob);
140 for (TextRun* run = iter.get(); run; run = iter.next()) {
cdalton8585dd22015-10-08 08:04:09 -0700141 run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -0700142 fFallbackTextContext, skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700143 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700144 }
145}
146
147const GrStencilAndCoverTextContext::TextBlob&
148GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
149 const SkPaint& skPaint) {
150 // The font-related parameters are baked into the text blob and will override this skPaint, so
151 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
152 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
153 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
154 fLRUList.remove(*found);
155 fLRUList.addToTail(*found);
156 return **found;
157 }
cdalton8585dd22015-10-08 08:04:09 -0700158 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700159 this->purgeToFit(*blob);
160 fBlobIdCache.set(skBlob->uniqueID(), blob);
161 fLRUList.addToTail(blob);
162 fCacheSize += blob->cpuMemorySize();
163 return *blob;
164 } else {
165 GrStrokeInfo stroke(skPaint);
166 SkSTArray<4, uint32_t, true> key;
167 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
168 key[0] = skBlob->uniqueID();
169 stroke.asUniqueKeyFragment(&key[1]);
170 if (TextBlob** found = fBlobKeyCache.find(key)) {
171 fLRUList.remove(*found);
172 fLRUList.addToTail(*found);
173 return **found;
174 }
cdalton8585dd22015-10-08 08:04:09 -0700175 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700176 this->purgeToFit(*blob);
177 fBlobKeyCache.set(blob);
178 fLRUList.addToTail(blob);
179 fCacheSize += blob->cpuMemorySize();
180 return *blob;
181 }
182}
183
184void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700185 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700186
cdalton8585dd22015-10-08 08:04:09 -0700187 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700188 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
189 TextBlob* lru = fLRUList.head();
190 if (1 == lru->key().count()) {
191 // 1-length keys are unterstood to be the blob id.
192 fBlobIdCache.remove(lru->key()[0]);
193 } else {
194 fBlobKeyCache.remove(lru->key());
195 }
196 fLRUList.remove(lru);
197 fCacheSize -= lru->cpuMemorySize();
198 delete lru;
199 }
200}
201
202////////////////////////////////////////////////////////////////////////////////////////////////////
203
cdalton8585dd22015-10-08 08:04:09 -0700204void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
205 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700206 fCpuMemorySize = sizeof(TextBlob);
207 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700208 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700209 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
bsalomon5aaef1f2015-11-18 14:11:08 -0800210 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700211
212 const char* text = reinterpret_cast<const char*>(iter.glyphs());
213 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
214 const SkPoint& runOffset = iter.offset();
215
216 switch (iter.positioning()) {
217 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700218 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700219 break;
220 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700221 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700222 break;
223 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700224 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700225 break;
226 }
227
cdalton8585dd22015-10-08 08:04:09 -0700228 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700229 }
cdalton3bd909a2015-10-05 14:57:20 -0700230}
231
232////////////////////////////////////////////////////////////////////////////////////////////////////
233
cdalton02015e52015-10-05 15:28:20 -0700234class GrStencilAndCoverTextContext::FallbackBlobBuilder {
235public:
236 FallbackBlobBuilder() : fBuffIdx(0) {}
237
238 bool isInitialized() const { return SkToBool(fBuilder); }
239
240 void init(const SkPaint& font, SkScalar textRatio);
241
242 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
243
244 const SkTextBlob* buildIfInitialized();
245
246private:
247 enum { kWriteBufferSize = 1024 };
248
249 void flush();
250
251 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
252 SkPaint fFont;
253 int fBuffIdx;
254 uint16_t fGlyphIds[kWriteBufferSize];
255 SkPoint fPositions[kWriteBufferSize];
256};
257
258////////////////////////////////////////////////////////////////////////////////////////////////////
259
cdalton3bd909a2015-10-05 14:57:20 -0700260GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
261 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700262 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700263 fTotalGlyphCount(0),
264 fDetachedGlyphCache(nullptr),
265 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
cdalton3bd909a2015-10-05 14:57:20 -0700266 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
267
268 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
269 // rendering API for stroking).
270 fFont.setStyle(SkPaint::kFill_Style);
271
272 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
273 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
274 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
275 kStdFakeBoldInterpKeys,
276 kStdFakeBoldInterpValues,
277 kStdFakeBoldInterpLength);
278 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
279 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
280 true /*strokeAndFill*/);
281
282 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700283 }
284
cdalton3bd909a2015-10-05 14:57:20 -0700285 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
286 // We can draw the glyphs from canonically sized paths.
287 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
288 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700289
cdalton3bd909a2015-10-05 14:57:20 -0700290 // Compensate for the glyphs being scaled by fTextRatio.
291 if (!fStroke.isFillStyle()) {
292 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
293 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
294 }
295
296 fFont.setLinearText(true);
297 fFont.setLCDRenderText(false);
298 fFont.setAutohinted(false);
299 fFont.setHinting(SkPaint::kNo_Hinting);
300 fFont.setSubpixelText(true);
301 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
302
303 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
304 0 == fFont.getTextSkewX() &&
305 !fFont.isFakeBoldText() &&
306 !fFont.isVerticalText();
307 } else {
308 fTextRatio = fTextInverseRatio = 1.0f;
309 fUsingRawGlyphPaths = false;
310 }
311
cdalton8585dd22015-10-08 08:04:09 -0700312 // Generate the key that will be used to cache the GPU glyph path objects.
313 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
314 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
315
316 const SkTypeface* typeface = fFont.getTypeface();
317 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
318 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
319 } else {
320 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
321
322 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
323 if (fUsingRawGlyphPaths) {
324 const SkTypeface* typeface = fFont.getTypeface();
325 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
326 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
327 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
328 fStroke.asUniqueKeyFragment(&builder[2]);
329 } else {
330 SkGlyphCache* glyphCache = this->getGlyphCache();
331 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
332 const SkDescriptor* desc = &glyphCache->getDescriptor();
333 int descDataCount = (desc->getLength() + 3) / 4;
334 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
335 2 + strokeDataCount + descDataCount);
336 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
337 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
338 fStroke.asUniqueKeyFragment(&builder[2]);
339 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
340 }
341 }
342
cdalton3bd909a2015-10-05 14:57:20 -0700343 // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
cdaltoncdd79072015-10-05 15:37:35 -0700344 fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio);
cdalton3bd909a2015-10-05 14:57:20 -0700345}
346
347GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700348 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700349}
350
351void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700352 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700353 SkASSERT(byteLength == 0 || text != nullptr);
354
cdalton8585dd22015-10-08 08:04:09 -0700355 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700356 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
jvanverthaab626c2014-10-16 08:04:39 -0700357
cdalton8585dd22015-10-08 08:04:09 -0700358 fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
359 fTotalGlyphCount = fFont.countText(text, byteLength)));
360
jvanverthaab626c2014-10-16 08:04:39 -0700361 const char* stop = text + byteLength;
362
363 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700364 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700365 SkFixed stopX = 0;
366 SkFixed stopY = 0;
367
368 const char* textPtr = text;
369 while (textPtr < stop) {
370 // We don't need x, y here, since all subpixel variants will have the
371 // same advance.
cdalton3bd909a2015-10-05 14:57:20 -0700372 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700373
374 stopX += glyph.fAdvanceX;
375 stopY += glyph.fAdvanceY;
376 }
377 SkASSERT(textPtr == stop);
378
379 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
380 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
381
cdalton3bd909a2015-10-05 14:57:20 -0700382 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700383 alignX = SkScalarHalf(alignX);
384 alignY = SkScalarHalf(alignY);
385 }
386
387 x -= alignX;
388 y -= alignY;
389 }
390
391 SkAutoKern autokern;
392
393 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
394
395 SkFixed fx = SkScalarToFixed(x);
396 SkFixed fy = SkScalarToFixed(y);
cdalton02015e52015-10-05 15:28:20 -0700397 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700398 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700399 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700400 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700401 if (glyph.fWidth) {
cdalton02015e52015-10-05 15:28:20 -0700402 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)),
403 &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700404 }
405
bungemand709ea82015-03-17 07:23:39 -0700406 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
407 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700408 }
cdalton02015e52015-10-05 15:28:20 -0700409
410 fFallbackTextBlob.reset(fallback.buildIfInitialized());
jvanverthaab626c2014-10-16 08:04:39 -0700411}
412
cdalton3bd909a2015-10-05 14:57:20 -0700413void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
414 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700415 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700416 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700417 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
418
cdalton8585dd22015-10-08 08:04:09 -0700419 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700420 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700421
cdalton8585dd22015-10-08 08:04:09 -0700422 fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
423 fTotalGlyphCount = fFont.countText(text, byteLength)));
424
kkinnunenc6cb56f2014-06-24 00:12:27 -0700425 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700426
cdalton38e13ad2014-11-07 06:02:15 -0800427 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700428 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700429 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800430 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700431 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800432 if (glyph.fWidth) {
433 SkPoint tmsLoc;
434 tmsProc(pos, &tmsLoc);
435 SkPoint loc;
436 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700437
cdalton02015e52015-10-05 15:28:20 -0700438 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700439 }
cdalton38e13ad2014-11-07 06:02:15 -0800440 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700441 }
cdalton02015e52015-10-05 15:28:20 -0700442
443 fFallbackTextBlob.reset(fallback.buildIfInitialized());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700444}
445
cdalton8585dd22015-10-08 08:04:09 -0700446GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
447 GrPathRange* glyphs = static_cast<GrPathRange*>(
448 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700449 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700450 if (fUsingRawGlyphPaths) {
451 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
452 } else {
453 SkGlyphCache* cache = this->getGlyphCache();
454 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
455 &cache->getDescriptor(),
456 fStroke);
457 }
458 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700459 }
cdalton8585dd22015-10-08 08:04:09 -0700460 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700461}
462
cdalton3bd909a2015-10-05 14:57:20 -0700463inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700464 const SkPoint& pos,
465 FallbackBlobBuilder* fallback) {
466 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700467 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700468 if (!fallback->isInitialized()) {
469 fallback->init(fFont, fTextRatio);
470 }
471 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700472 } else {
cdalton7d5c9502015-10-03 13:28:35 -0700473 float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
bsalomon1fcc01c2015-09-09 09:48:06 -0700474 fDraw->append(glyph.getGlyphID(), translate);
cdaltonb2808cd2014-07-25 14:13:57 -0700475 }
cdalton20b373c2014-12-01 08:38:55 -0800476}
477
cdalton8585dd22015-10-08 08:04:09 -0700478void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
479 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700480 GrPipelineBuilder* pipelineBuilder,
481 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700482 const SkMatrix& viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700483 SkScalar x, SkScalar y,
484 const SkIRect& clipBounds,
cdalton3bd909a2015-10-05 14:57:20 -0700485 GrTextContext* fallbackTextContext,
486 const SkPaint& originalSkPaint) const {
487 SkASSERT(fDraw);
robertphillips433625e2015-12-04 06:58:16 -0800488 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700489
cdalton3bd909a2015-10-05 14:57:20 -0700490 if (fDraw->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700491 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700492
493 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
494 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700495 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700496 kNotEqual_StencilFunc,
497 0xffff,
498 0x0000,
499 0xffff);
500
cdaltoncdd79072015-10-05 15:37:35 -0700501 *pipelineBuilder->stencil() = kStencilPass;
joshualitt7b670db2015-07-09 13:25:02 -0700502
cdalton8585dd22015-10-08 08:04:09 -0700503 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
504 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
505 // Either this is the first draw or the glyphs object was purged since last draw.
506 glyphs->loadPathsIfNeeded(fDraw->indices(), fDraw->count());
507 fLastDrawnGlyphsID = glyphs->getUniqueID();
508 }
509
cdalton3bd909a2015-10-05 14:57:20 -0700510 SkMatrix drawMatrix(viewMatrix);
cdaltoncdd79072015-10-05 15:37:35 -0700511 drawMatrix.preTranslate(x, y);
cdalton3bd909a2015-10-05 14:57:20 -0700512 drawMatrix.preScale(fTextRatio, fTextRatio);
513
cdaltoncdd79072015-10-05 15:37:35 -0700514 SkMatrix& localMatrix = fLocalMatrixTemplate;
515 localMatrix.setTranslateX(x);
516 localMatrix.setTranslateY(y);
517
bsalomonbf074552015-11-23 14:25:19 -0800518 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
519 // the entire dst. Realistically this is a moot point, because any context that supports
520 // NV_path_rendering will also support NV_blend_equation_advanced.
521 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
522 // hurt batching.
523 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
524 pipelineBuilder->getRenderTarget()->height());
525
cdalton8585dd22015-10-08 08:04:09 -0700526 dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, glyphs, fDraw,
bsalomonbf074552015-11-23 14:25:19 -0800527 GrPathRendering::kWinding_FillType, bounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700528 }
529
cdalton02015e52015-10-05 15:28:20 -0700530 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700531 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700532 fStroke.applyToPaint(&fallbackSkPaint);
533 if (!fStroke.isFillStyle()) {
534 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800535 }
cdalton20b373c2014-12-01 08:38:55 -0800536
robertphillips433625e2015-12-04 06:58:16 -0800537 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700538 fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800539 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700540}
cdalton02015e52015-10-05 15:28:20 -0700541
cdalton8585dd22015-10-08 08:04:09 -0700542SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
543 if (!fDetachedGlyphCache) {
544 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
545 }
546 return fDetachedGlyphCache;
547}
548
549
550void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
551 if (fDetachedGlyphCache) {
552 SkGlyphCache::AttachCache(fDetachedGlyphCache);
553 fDetachedGlyphCache = nullptr;
554 }
555}
556
557size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
558 size_t size = sizeof(TextRun) +
559 fGlyphPathsKey.size() +
560 fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float));
cdaltoncdd79072015-10-05 15:37:35 -0700561 if (fDraw) {
562 size += sizeof(GrPathRangeDraw);
563 }
564 if (fFallbackTextBlob) {
565 size += sizeof(SkTextBlob);
566 }
567 return size;
568}
569
cdalton02015e52015-10-05 15:28:20 -0700570////////////////////////////////////////////////////////////////////////////////////////////////////
571
572void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
573 SkScalar textRatio) {
574 SkASSERT(!this->isInitialized());
575 fBuilder.reset(new SkTextBlobBuilder);
576 fFont = font;
577 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
578 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
579 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
580 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
581 fFont.setSubpixelText(false);
582 fFont.setTextSize(fFont.getTextSize() * textRatio);
583 fBuffIdx = 0;
584}
585
586void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
587 const SkPoint& pos) {
588 SkASSERT(this->isInitialized());
589 if (fBuffIdx >= kWriteBufferSize) {
590 this->flush();
591 }
592 fGlyphIds[fBuffIdx] = glyphId;
593 fPositions[fBuffIdx] = pos;
594 fBuffIdx++;
595}
596
597void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
598 SkASSERT(this->isInitialized());
599 SkASSERT(fBuffIdx <= kWriteBufferSize);
600 if (!fBuffIdx) {
601 return;
602 }
603 // This will automatically merge with previous runs since we use the same font.
604 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
605 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
606 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
607 fBuffIdx = 0;
608}
609
610const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfInitialized() {
611 if (!this->isInitialized()) {
612 return nullptr;
613 }
614 this->flush();
615 return fBuilder->build();
616}