blob: ee1f1af4107ac17597732daeb5364d77581f8099 [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"
22#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070023#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070024
bsalomon1fcc01c2015-09-09 09:48:06 -070025#include "batches/GrDrawPathBatch.h"
26
cdaltoncdd79072015-10-05 15:37:35 -070027template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
28 SkASSERT(*val);
29 delete *val;
30}
31
32template<typename T> static void delete_hash_table_entry(T* val) {
33 SkASSERT(*val);
34 delete *val;
35}
36
joshualitt6e8cd962015-03-20 10:30:14 -070037GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070038 const SkSurfaceProps& surfaceProps)
cdaltoncdd79072015-10-05 15:37:35 -070039 : INHERITED(context, surfaceProps),
40 fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070041}
42
joshualitt6e8cd962015-03-20 10:30:14 -070043GrStencilAndCoverTextContext*
robertphillipsf6703fa2015-09-01 05:36:47 -070044GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
45 GrStencilAndCoverTextContext* textContext =
46 new GrStencilAndCoverTextContext(context, surfaceProps);
47 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
jvanverth8c27a182014-10-14 08:45:50 -070048
49 return textContext;
50}
51
kkinnunenc6cb56f2014-06-24 00:12:27 -070052GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
cdaltoncdd79072015-10-05 15:37:35 -070053 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
54 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070055}
56
cdaltoncdd79072015-10-05 15:37:35 -070057bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070058 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070059 return false;
60 }
cdaltone68f7362015-03-25 14:02:37 -070061 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070062 return false;
63 }
kkinnunen50b58e62015-05-18 23:02:07 -070064 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070065 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070066 return false;
67 }
jvanverth0fedb192014-10-08 09:07:27 -070068 }
cdalton7d5c9502015-10-03 13:28:35 -070069 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
70 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070071}
72
robertphillipsf6703fa2015-09-01 05:36:47 -070073void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080074 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080075 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080076 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080077 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080078 const char text[],
79 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070080 SkScalar x, SkScalar y,
cdaltoncdd79072015-10-05 15:37:35 -070081 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -070082 TextRun run(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -070083 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
cdalton8585dd22015-10-08 08:04:09 -070084 run.setText(text, byteLength, x, y);
85 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -070086 fFallbackTextContext, skPaint);
cdalton3bd909a2015-10-05 14:57:20 -070087}
jvanverthaab626c2014-10-16 08:04:39 -070088
cdalton3bd909a2015-10-05 14:57:20 -070089void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
90 const GrClip& clip,
91 const GrPaint& paint,
92 const SkPaint& skPaint,
93 const SkMatrix& viewMatrix,
94 const char text[],
95 size_t byteLength,
96 const SkScalar pos[],
97 int scalarsPerPosition,
98 const SkPoint& offset,
cdaltoncdd79072015-10-05 15:37:35 -070099 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -0700100 TextRun run(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700101 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
cdalton8585dd22015-10-08 08:04:09 -0700102 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
103 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -0700104 fFallbackTextContext, skPaint);
105}
106
107void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt,
108 const GrClip& clip, const SkPaint& skPaint,
109 const SkMatrix& viewMatrix,
110 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
111 SkDrawFilter* drawFilter,
112 const SkIRect& clipBounds) {
113 if (!this->internalCanDraw(skPaint)) {
114 fFallbackTextContext->drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y,
115 drawFilter, clipBounds);
116 return;
117 }
118
119 if (drawFilter || skPaint.getPathEffect()) {
120 // This draw can't be cached.
121 INHERITED::drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter,
122 clipBounds);
123 return;
124 }
125
126 if (fContext->abandoned()) {
127 return;
128 }
129
130 GrPaint paint;
131 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) {
132 return;
133 }
134
135 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
136 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
137
138 TextBlob::Iter iter(blob);
139 for (TextRun* run = iter.get(); run; run = iter.next()) {
cdalton8585dd22015-10-08 08:04:09 -0700140 run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
cdaltoncdd79072015-10-05 15:37:35 -0700141 fFallbackTextContext, skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700142 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700143 }
144}
145
146const GrStencilAndCoverTextContext::TextBlob&
147GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
148 const SkPaint& skPaint) {
149 // The font-related parameters are baked into the text blob and will override this skPaint, so
150 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
151 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
152 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
153 fLRUList.remove(*found);
154 fLRUList.addToTail(*found);
155 return **found;
156 }
cdalton8585dd22015-10-08 08:04:09 -0700157 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700158 this->purgeToFit(*blob);
159 fBlobIdCache.set(skBlob->uniqueID(), blob);
160 fLRUList.addToTail(blob);
161 fCacheSize += blob->cpuMemorySize();
162 return *blob;
163 } else {
164 GrStrokeInfo stroke(skPaint);
165 SkSTArray<4, uint32_t, true> key;
166 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
167 key[0] = skBlob->uniqueID();
168 stroke.asUniqueKeyFragment(&key[1]);
169 if (TextBlob** found = fBlobKeyCache.find(key)) {
170 fLRUList.remove(*found);
171 fLRUList.addToTail(*found);
172 return **found;
173 }
cdalton8585dd22015-10-08 08:04:09 -0700174 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700175 this->purgeToFit(*blob);
176 fBlobKeyCache.set(blob);
177 fLRUList.addToTail(blob);
178 fCacheSize += blob->cpuMemorySize();
179 return *blob;
180 }
181}
182
183void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700184 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700185
cdalton8585dd22015-10-08 08:04:09 -0700186 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700187 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
188 TextBlob* lru = fLRUList.head();
189 if (1 == lru->key().count()) {
190 // 1-length keys are unterstood to be the blob id.
191 fBlobIdCache.remove(lru->key()[0]);
192 } else {
193 fBlobKeyCache.remove(lru->key());
194 }
195 fLRUList.remove(lru);
196 fCacheSize -= lru->cpuMemorySize();
197 delete lru;
198 }
199}
200
201////////////////////////////////////////////////////////////////////////////////////////////////////
202
cdalton8585dd22015-10-08 08:04:09 -0700203void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
204 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700205 fCpuMemorySize = sizeof(TextBlob);
206 SkPaint runPaint(skPaint);
207 for (SkTextBlob::RunIterator iter(skBlob); !iter.done(); iter.next()) {
208 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
209 TextRun* run = SkNEW_INSERT_AT_LLIST_TAIL(this, TextRun, (runPaint));
210
211 const char* text = reinterpret_cast<const char*>(iter.glyphs());
212 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
213 const SkPoint& runOffset = iter.offset();
214
215 switch (iter.positioning()) {
216 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700217 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700218 break;
219 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700220 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700221 break;
222 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700223 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700224 break;
225 }
226
cdalton8585dd22015-10-08 08:04:09 -0700227 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700228 }
cdalton3bd909a2015-10-05 14:57:20 -0700229}
230
231////////////////////////////////////////////////////////////////////////////////////////////////////
232
cdalton02015e52015-10-05 15:28:20 -0700233class GrStencilAndCoverTextContext::FallbackBlobBuilder {
234public:
235 FallbackBlobBuilder() : fBuffIdx(0) {}
236
237 bool isInitialized() const { return SkToBool(fBuilder); }
238
239 void init(const SkPaint& font, SkScalar textRatio);
240
241 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
242
243 const SkTextBlob* buildIfInitialized();
244
245private:
246 enum { kWriteBufferSize = 1024 };
247
248 void flush();
249
250 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
251 SkPaint fFont;
252 int fBuffIdx;
253 uint16_t fGlyphIds[kWriteBufferSize];
254 SkPoint fPositions[kWriteBufferSize];
255};
256
257////////////////////////////////////////////////////////////////////////////////////////////////////
258
cdalton3bd909a2015-10-05 14:57:20 -0700259GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
260 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700261 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700262 fTotalGlyphCount(0),
263 fDetachedGlyphCache(nullptr),
264 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
cdalton3bd909a2015-10-05 14:57:20 -0700265 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
266
267 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
268 // rendering API for stroking).
269 fFont.setStyle(SkPaint::kFill_Style);
270
271 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
272 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
273 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
274 kStdFakeBoldInterpKeys,
275 kStdFakeBoldInterpValues,
276 kStdFakeBoldInterpLength);
277 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
278 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
279 true /*strokeAndFill*/);
280
281 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700282 }
283
cdalton3bd909a2015-10-05 14:57:20 -0700284 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
285 // We can draw the glyphs from canonically sized paths.
286 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
287 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700288
cdalton3bd909a2015-10-05 14:57:20 -0700289 // Compensate for the glyphs being scaled by fTextRatio.
290 if (!fStroke.isFillStyle()) {
291 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
292 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
293 }
294
295 fFont.setLinearText(true);
296 fFont.setLCDRenderText(false);
297 fFont.setAutohinted(false);
298 fFont.setHinting(SkPaint::kNo_Hinting);
299 fFont.setSubpixelText(true);
300 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
301
302 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
303 0 == fFont.getTextSkewX() &&
304 !fFont.isFakeBoldText() &&
305 !fFont.isVerticalText();
306 } else {
307 fTextRatio = fTextInverseRatio = 1.0f;
308 fUsingRawGlyphPaths = false;
309 }
310
cdalton8585dd22015-10-08 08:04:09 -0700311 // Generate the key that will be used to cache the GPU glyph path objects.
312 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
313 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
314
315 const SkTypeface* typeface = fFont.getTypeface();
316 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
317 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
318 } else {
319 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
320
321 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
322 if (fUsingRawGlyphPaths) {
323 const SkTypeface* typeface = fFont.getTypeface();
324 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
325 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
326 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
327 fStroke.asUniqueKeyFragment(&builder[2]);
328 } else {
329 SkGlyphCache* glyphCache = this->getGlyphCache();
330 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
331 const SkDescriptor* desc = &glyphCache->getDescriptor();
332 int descDataCount = (desc->getLength() + 3) / 4;
333 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
334 2 + strokeDataCount + descDataCount);
335 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
336 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
337 fStroke.asUniqueKeyFragment(&builder[2]);
338 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
339 }
340 }
341
cdalton3bd909a2015-10-05 14:57:20 -0700342 // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
cdaltoncdd79072015-10-05 15:37:35 -0700343 fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio);
cdalton3bd909a2015-10-05 14:57:20 -0700344}
345
346GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700347 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700348}
349
350void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700351 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700352 SkASSERT(byteLength == 0 || text != nullptr);
353
cdalton8585dd22015-10-08 08:04:09 -0700354 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700355 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
jvanverthaab626c2014-10-16 08:04:39 -0700356
cdalton8585dd22015-10-08 08:04:09 -0700357 fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
358 fTotalGlyphCount = fFont.countText(text, byteLength)));
359
jvanverthaab626c2014-10-16 08:04:39 -0700360 const char* stop = text + byteLength;
361
362 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700363 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700364 SkFixed stopX = 0;
365 SkFixed stopY = 0;
366
367 const char* textPtr = text;
368 while (textPtr < stop) {
369 // We don't need x, y here, since all subpixel variants will have the
370 // same advance.
cdalton3bd909a2015-10-05 14:57:20 -0700371 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700372
373 stopX += glyph.fAdvanceX;
374 stopY += glyph.fAdvanceY;
375 }
376 SkASSERT(textPtr == stop);
377
378 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
379 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
380
cdalton3bd909a2015-10-05 14:57:20 -0700381 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700382 alignX = SkScalarHalf(alignX);
383 alignY = SkScalarHalf(alignY);
384 }
385
386 x -= alignX;
387 y -= alignY;
388 }
389
390 SkAutoKern autokern;
391
392 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
393
394 SkFixed fx = SkScalarToFixed(x);
395 SkFixed fy = SkScalarToFixed(y);
cdalton02015e52015-10-05 15:28:20 -0700396 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700397 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700398 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700399 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700400 if (glyph.fWidth) {
cdalton02015e52015-10-05 15:28:20 -0700401 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)),
402 &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700403 }
404
bungemand709ea82015-03-17 07:23:39 -0700405 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
406 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700407 }
cdalton02015e52015-10-05 15:28:20 -0700408
409 fFallbackTextBlob.reset(fallback.buildIfInitialized());
jvanverthaab626c2014-10-16 08:04:39 -0700410}
411
cdalton3bd909a2015-10-05 14:57:20 -0700412void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
413 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700414 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700415 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700416 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
417
cdalton8585dd22015-10-08 08:04:09 -0700418 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700419 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700420
cdalton8585dd22015-10-08 08:04:09 -0700421 fDraw.reset(GrPathRangeDraw::Create(GrPathRendering::kTranslate_PathTransformType,
422 fTotalGlyphCount = fFont.countText(text, byteLength)));
423
kkinnunenc6cb56f2014-06-24 00:12:27 -0700424 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700425
cdalton38e13ad2014-11-07 06:02:15 -0800426 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700427 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700428 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800429 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700430 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800431 if (glyph.fWidth) {
432 SkPoint tmsLoc;
433 tmsProc(pos, &tmsLoc);
434 SkPoint loc;
435 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700436
cdalton02015e52015-10-05 15:28:20 -0700437 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700438 }
cdalton38e13ad2014-11-07 06:02:15 -0800439 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700440 }
cdalton02015e52015-10-05 15:28:20 -0700441
442 fFallbackTextBlob.reset(fallback.buildIfInitialized());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700443}
444
cdalton8585dd22015-10-08 08:04:09 -0700445GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
446 GrPathRange* glyphs = static_cast<GrPathRange*>(
447 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700448 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700449 if (fUsingRawGlyphPaths) {
450 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
451 } else {
452 SkGlyphCache* cache = this->getGlyphCache();
453 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
454 &cache->getDescriptor(),
455 fStroke);
456 }
457 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700458 }
cdalton8585dd22015-10-08 08:04:09 -0700459 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700460}
461
cdalton3bd909a2015-10-05 14:57:20 -0700462inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700463 const SkPoint& pos,
464 FallbackBlobBuilder* fallback) {
465 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700466 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700467 if (!fallback->isInitialized()) {
468 fallback->init(fFont, fTextRatio);
469 }
470 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700471 } else {
cdalton7d5c9502015-10-03 13:28:35 -0700472 float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
bsalomon1fcc01c2015-09-09 09:48:06 -0700473 fDraw->append(glyph.getGlyphID(), translate);
cdaltonb2808cd2014-07-25 14:13:57 -0700474 }
cdalton20b373c2014-12-01 08:38:55 -0800475}
476
cdalton8585dd22015-10-08 08:04:09 -0700477void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
478 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700479 GrPipelineBuilder* pipelineBuilder,
480 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700481 const SkMatrix& viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700482 SkScalar x, SkScalar y,
483 const SkIRect& clipBounds,
cdalton3bd909a2015-10-05 14:57:20 -0700484 GrTextContext* fallbackTextContext,
485 const SkPaint& originalSkPaint) const {
486 SkASSERT(fDraw);
cdaltoncdd79072015-10-05 15:37:35 -0700487 SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled() ||
488 !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
cdalton8585dd22015-10-08 08:04:09 -0700518 dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, glyphs, fDraw,
cdalton3bd909a2015-10-05 14:57:20 -0700519 GrPathRendering::kWinding_FillType);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700520 }
521
cdalton02015e52015-10-05 15:28:20 -0700522 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700523 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700524 fStroke.applyToPaint(&fallbackSkPaint);
525 if (!fStroke.isFillStyle()) {
526 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800527 }
cdalton20b373c2014-12-01 08:38:55 -0800528
cdaltoncdd79072015-10-05 15:37:35 -0700529 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->getRenderTarget(),
530 pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
531 fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800532 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700533}
cdalton02015e52015-10-05 15:28:20 -0700534
cdalton8585dd22015-10-08 08:04:09 -0700535SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
536 if (!fDetachedGlyphCache) {
537 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
538 }
539 return fDetachedGlyphCache;
540}
541
542
543void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
544 if (fDetachedGlyphCache) {
545 SkGlyphCache::AttachCache(fDetachedGlyphCache);
546 fDetachedGlyphCache = nullptr;
547 }
548}
549
550size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
551 size_t size = sizeof(TextRun) +
552 fGlyphPathsKey.size() +
553 fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float));
cdaltoncdd79072015-10-05 15:37:35 -0700554 if (fDraw) {
555 size += sizeof(GrPathRangeDraw);
556 }
557 if (fFallbackTextBlob) {
558 size += sizeof(SkTextBlob);
559 }
560 return size;
561}
562
cdalton02015e52015-10-05 15:28:20 -0700563////////////////////////////////////////////////////////////////////////////////////////////////////
564
565void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
566 SkScalar textRatio) {
567 SkASSERT(!this->isInitialized());
568 fBuilder.reset(new SkTextBlobBuilder);
569 fFont = font;
570 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
571 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
572 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
573 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
574 fFont.setSubpixelText(false);
575 fFont.setTextSize(fFont.getTextSize() * textRatio);
576 fBuffIdx = 0;
577}
578
579void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
580 const SkPoint& pos) {
581 SkASSERT(this->isInitialized());
582 if (fBuffIdx >= kWriteBufferSize) {
583 this->flush();
584 }
585 fGlyphIds[fBuffIdx] = glyphId;
586 fPositions[fBuffIdx] = pos;
587 fBuffIdx++;
588}
589
590void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
591 SkASSERT(this->isInitialized());
592 SkASSERT(fBuffIdx <= kWriteBufferSize);
593 if (!fBuffIdx) {
594 return;
595 }
596 // This will automatically merge with previous runs since we use the same font.
597 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
598 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
599 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
600 fBuffIdx = 0;
601}
602
603const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfInitialized() {
604 if (!this->isInitialized()) {
605 return nullptr;
606 }
607 this->flush();
608 return fBuilder->build();
609}