blob: d28f1a803a0abe7df26843e7848fd21821d8b4f5 [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:
cdaltoncdd46822015-12-08 10:48:31 -0800236 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700237
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
cdaltoncdd46822015-12-08 10:48:31 -0800244 const SkTextBlob* buildIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700245
246private:
247 enum { kWriteBufferSize = 1024 };
248
249 void flush();
250
251 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
252 SkPaint fFont;
253 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800254 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700255 uint16_t fGlyphIds[kWriteBufferSize];
256 SkPoint fPositions[kWriteBufferSize];
257};
258
259////////////////////////////////////////////////////////////////////////////////////////////////////
260
cdalton3bd909a2015-10-05 14:57:20 -0700261GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
262 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700263 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700264 fTotalGlyphCount(0),
cdaltoncdd46822015-12-08 10:48:31 -0800265 fFallbackGlyphCount(0),
cdalton8585dd22015-10-08 08:04:09 -0700266 fDetachedGlyphCache(nullptr),
267 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
cdalton3bd909a2015-10-05 14:57:20 -0700268 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
269
270 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
271 // rendering API for stroking).
272 fFont.setStyle(SkPaint::kFill_Style);
273
274 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
275 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
276 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
277 kStdFakeBoldInterpKeys,
278 kStdFakeBoldInterpValues,
279 kStdFakeBoldInterpLength);
280 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
281 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
282 true /*strokeAndFill*/);
283
284 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700285 }
286
cdalton3bd909a2015-10-05 14:57:20 -0700287 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
288 // We can draw the glyphs from canonically sized paths.
289 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
290 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700291
cdalton3bd909a2015-10-05 14:57:20 -0700292 // Compensate for the glyphs being scaled by fTextRatio.
293 if (!fStroke.isFillStyle()) {
294 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
295 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
296 }
297
298 fFont.setLinearText(true);
299 fFont.setLCDRenderText(false);
300 fFont.setAutohinted(false);
301 fFont.setHinting(SkPaint::kNo_Hinting);
302 fFont.setSubpixelText(true);
303 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
304
305 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
306 0 == fFont.getTextSkewX() &&
307 !fFont.isFakeBoldText() &&
308 !fFont.isVerticalText();
309 } else {
310 fTextRatio = fTextInverseRatio = 1.0f;
311 fUsingRawGlyphPaths = false;
312 }
313
cdalton8585dd22015-10-08 08:04:09 -0700314 // Generate the key that will be used to cache the GPU glyph path objects.
315 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
316 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
317
318 const SkTypeface* typeface = fFont.getTypeface();
319 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
320 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
321 } else {
322 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
323
324 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
325 if (fUsingRawGlyphPaths) {
326 const SkTypeface* typeface = fFont.getTypeface();
327 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
328 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
329 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
330 fStroke.asUniqueKeyFragment(&builder[2]);
331 } else {
332 SkGlyphCache* glyphCache = this->getGlyphCache();
333 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
334 const SkDescriptor* desc = &glyphCache->getDescriptor();
335 int descDataCount = (desc->getLength() + 3) / 4;
336 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
337 2 + strokeDataCount + descDataCount);
338 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
339 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
340 fStroke.asUniqueKeyFragment(&builder[2]);
341 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
342 }
343 }
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
cdaltoncdd46822015-12-08 10:48:31 -0800357 fTotalGlyphCount = fFont.countText(text, byteLength);
358 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
359 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700360
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
cdaltoncdd46822015-12-08 10:48:31 -0800410 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
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
cdaltoncdd46822015-12-08 10:48:31 -0800422 fTotalGlyphCount = fFont.countText(text, byteLength);
423 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
424 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700425
kkinnunenc6cb56f2014-06-24 00:12:27 -0700426 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700427
cdalton38e13ad2014-11-07 06:02:15 -0800428 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700429 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700430 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800431 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700432 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800433 if (glyph.fWidth) {
434 SkPoint tmsLoc;
435 tmsProc(pos, &tmsLoc);
436 SkPoint loc;
437 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700438
cdalton02015e52015-10-05 15:28:20 -0700439 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700440 }
cdalton38e13ad2014-11-07 06:02:15 -0800441 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700442 }
cdalton02015e52015-10-05 15:28:20 -0700443
cdaltoncdd46822015-12-08 10:48:31 -0800444 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700445}
446
cdalton8585dd22015-10-08 08:04:09 -0700447GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
448 GrPathRange* glyphs = static_cast<GrPathRange*>(
449 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700450 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700451 if (fUsingRawGlyphPaths) {
452 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
453 } else {
454 SkGlyphCache* cache = this->getGlyphCache();
455 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
456 &cache->getDescriptor(),
457 fStroke);
458 }
459 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700460 }
cdalton8585dd22015-10-08 08:04:09 -0700461 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700462}
463
cdalton3bd909a2015-10-05 14:57:20 -0700464inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700465 const SkPoint& pos,
466 FallbackBlobBuilder* fallback) {
467 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700468 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700469 if (!fallback->isInitialized()) {
470 fallback->init(fFont, fTextRatio);
471 }
472 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700473 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800474 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
475 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700476 }
cdalton20b373c2014-12-01 08:38:55 -0800477}
478
cdalton8585dd22015-10-08 08:04:09 -0700479void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
480 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700481 GrPipelineBuilder* pipelineBuilder,
482 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700483 const SkMatrix& viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700484 SkScalar x, SkScalar y,
485 const SkIRect& clipBounds,
cdalton3bd909a2015-10-05 14:57:20 -0700486 GrTextContext* fallbackTextContext,
487 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800488 SkASSERT(fInstanceData);
robertphillips433625e2015-12-04 06:58:16 -0800489 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700490
cdaltoncdd46822015-12-08 10:48:31 -0800491 if (fInstanceData->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700492 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700493
494 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
495 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700496 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700497 kNotEqual_StencilFunc,
498 0xffff,
499 0x0000,
500 0xffff);
501
cdaltoncdd79072015-10-05 15:37:35 -0700502 *pipelineBuilder->stencil() = kStencilPass;
joshualitt7b670db2015-07-09 13:25:02 -0700503
cdalton8585dd22015-10-08 08:04:09 -0700504 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
505 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
506 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800507 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
cdalton8585dd22015-10-08 08:04:09 -0700508 fLastDrawnGlyphsID = glyphs->getUniqueID();
509 }
510
bsalomonbf074552015-11-23 14:25:19 -0800511 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
512 // the entire dst. Realistically this is a moot point, because any context that supports
513 // NV_path_rendering will also support NV_blend_equation_advanced.
514 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
515 // hurt batching.
516 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
517 pipelineBuilder->getRenderTarget()->height());
518
cdalton8ff8d242015-12-08 10:20:32 -0800519 SkAutoTUnref<GrDrawPathBatchBase> batch(
cdaltoncdd46822015-12-08 10:48:31 -0800520 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
521 fTextInverseRatio * y, color,
522 GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
cdalton8ff8d242015-12-08 10:20:32 -0800523 bounds));
524
525 dc->drawPathBatch(*pipelineBuilder, batch);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700526 }
527
cdalton02015e52015-10-05 15:28:20 -0700528 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700529 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700530 fStroke.applyToPaint(&fallbackSkPaint);
531 if (!fStroke.isFillStyle()) {
532 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800533 }
cdalton20b373c2014-12-01 08:38:55 -0800534
robertphillips433625e2015-12-04 06:58:16 -0800535 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700536 fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800537 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700538}
cdalton02015e52015-10-05 15:28:20 -0700539
cdalton8585dd22015-10-08 08:04:09 -0700540SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
541 if (!fDetachedGlyphCache) {
542 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
543 }
544 return fDetachedGlyphCache;
545}
546
547
548void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
549 if (fDetachedGlyphCache) {
550 SkGlyphCache::AttachCache(fDetachedGlyphCache);
551 fDetachedGlyphCache = nullptr;
552 }
553}
554
555size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800556 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
557 // The instance data always reserves enough space for every glyph.
558 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
559 if (fInstanceData) {
560 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700561 }
562 if (fFallbackTextBlob) {
563 size += sizeof(SkTextBlob);
564 }
565 return size;
566}
567
cdalton02015e52015-10-05 15:28:20 -0700568////////////////////////////////////////////////////////////////////////////////////////////////////
569
570void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
571 SkScalar textRatio) {
572 SkASSERT(!this->isInitialized());
573 fBuilder.reset(new SkTextBlobBuilder);
574 fFont = font;
575 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
576 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
577 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
578 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
579 fFont.setSubpixelText(false);
580 fFont.setTextSize(fFont.getTextSize() * textRatio);
581 fBuffIdx = 0;
582}
583
584void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
585 const SkPoint& pos) {
586 SkASSERT(this->isInitialized());
587 if (fBuffIdx >= kWriteBufferSize) {
588 this->flush();
589 }
590 fGlyphIds[fBuffIdx] = glyphId;
591 fPositions[fBuffIdx] = pos;
592 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800593 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700594}
595
596void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
597 SkASSERT(this->isInitialized());
598 SkASSERT(fBuffIdx <= kWriteBufferSize);
599 if (!fBuffIdx) {
600 return;
601 }
602 // This will automatically merge with previous runs since we use the same font.
603 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
604 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
605 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
606 fBuffIdx = 0;
607}
608
cdaltoncdd46822015-12-08 10:48:31 -0800609const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
610 *count = fCount;
611 if (fCount) {
612 this->flush();
613 return fBuilder->build();
cdalton02015e52015-10-05 15:28:20 -0700614 }
cdaltoncdd46822015-12-08 10:48:31 -0800615 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700616}