blob: 8f348f68cf8dd5a9a1ca1b90d9225ebe0050d8bf [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 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.
reed@google.comac10a2d2010-12-22 21:39:39 +00006 */
7
tomhudson@google.com375ff852012-06-29 18:37:57 +00008#include "GrTextContext.h"
robertphillipsccb1b572015-05-27 11:02:55 -07009#include "GrBlurUtils.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000010#include "GrContext.h"
robertphillipsccb1b572015-05-27 11:02:55 -070011#include "GrDrawContext.h"
joshualitt570d2f82015-02-25 13:19:48 -080012#include "GrFontScaler.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000013
14#include "SkAutoKern.h"
joshualitt9c328182015-03-23 08:13:04 -070015#include "SkDrawFilter.h"
joshualitt6e8cd962015-03-20 10:30:14 -070016#include "SkDrawProcs.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000017#include "SkGlyphCache.h"
joshualitt6e8cd962015-03-20 10:30:14 -070018#include "SkGpuDevice.h"
joshualitt9c328182015-03-23 08:13:04 -070019#include "SkTextBlob.h"
joshualitt6e8cd962015-03-20 10:30:14 -070020#include "SkTextMapStateProc.h"
21#include "SkTextToPathIter.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000022
robertphillips2334fb62015-06-17 05:43:33 -070023GrTextContext::GrTextContext(GrContext* context, GrDrawContext* drawContext,
robertphillipsfcf78292015-06-19 11:49:52 -070024 const SkSurfaceProps& surfaceProps)
joshualitt6e8cd962015-03-20 10:30:14 -070025 : fFallbackTextContext(NULL)
26 , fContext(context)
robertphillipsfcf78292015-06-19 11:49:52 -070027 , fSurfaceProps(surfaceProps)
robertphillips2334fb62015-06-17 05:43:33 -070028 , fDrawContext(drawContext) {
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000029}
tomhudson@google.com375ff852012-06-29 18:37:57 +000030
jvanverth8c27a182014-10-14 08:45:50 -070031GrTextContext::~GrTextContext() {
32 SkDELETE(fFallbackTextContext);
33}
34
joshualitt6e8cd962015-03-20 10:30:14 -070035void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
joshualitt570d2f82015-02-25 13:19:48 -080036 const SkPaint& skPaint, const SkMatrix& viewMatrix,
jvanverth8c27a182014-10-14 08:45:50 -070037 const char text[], size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070038 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -070039 if (fContext->abandoned() || !fDrawContext) {
robertphillipsccb1b572015-05-27 11:02:55 -070040 return;
41 }
42
jvanverthaab626c2014-10-16 08:04:39 -070043 GrTextContext* textContext = this;
44 do {
cdaltone68f7362015-03-25 14:02:37 -070045 if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
robertphillips2334fb62015-06-17 05:43:33 -070046 textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070047 text, byteLength, x, y, clipBounds);
joshualitt6e8cd962015-03-20 10:30:14 -070048 return;
jvanverthaab626c2014-10-16 08:04:39 -070049 }
50 textContext = textContext->fFallbackTextContext;
51 } while (textContext);
jvanverth8c27a182014-10-14 08:45:50 -070052
joshualitt6e8cd962015-03-20 10:30:14 -070053 // fall back to drawing as a path
robertphillips2334fb62015-06-17 05:43:33 -070054 this->drawTextAsPath(rt, clip, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070055 text, byteLength, x, y, clipBounds);
jvanverth8c27a182014-10-14 08:45:50 -070056}
57
joshualitt6e8cd962015-03-20 10:30:14 -070058void GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
joshualitt570d2f82015-02-25 13:19:48 -080059 const SkPaint& skPaint, const SkMatrix& viewMatrix,
jvanverth8c27a182014-10-14 08:45:50 -070060 const char text[], size_t byteLength,
61 const SkScalar pos[], int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -070062 const SkPoint& offset, const SkIRect& clipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -070063 if (fContext->abandoned() || !fDrawContext) {
robertphillipsccb1b572015-05-27 11:02:55 -070064 return;
65 }
66
jvanverth8c27a182014-10-14 08:45:50 -070067 GrTextContext* textContext = this;
68 do {
cdaltone68f7362015-03-25 14:02:37 -070069 if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
robertphillips2334fb62015-06-17 05:43:33 -070070 textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070071 text, byteLength, pos,
joshualitt6e8cd962015-03-20 10:30:14 -070072 scalarsPerPosition, offset, clipBounds);
73 return;
jvanverth8c27a182014-10-14 08:45:50 -070074 }
75 textContext = textContext->fFallbackTextContext;
76 } while (textContext);
77
joshualitt6e8cd962015-03-20 10:30:14 -070078 // fall back to drawing as a path
robertphillips2334fb62015-06-17 05:43:33 -070079 this->drawPosTextAsPath(rt, clip, skPaint, viewMatrix, text, byteLength, pos,
robertphillipsccb1b572015-05-27 11:02:55 -070080 scalarsPerPosition, offset, clipBounds);
jvanverth8c27a182014-10-14 08:45:50 -070081}
82
robertphillips9c240a12015-05-28 07:45:59 -070083bool GrTextContext::ShouldDisableLCD(const SkPaint& paint) {
84 if (paint.getShader() ||
85 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode) ||
86 paint.getMaskFilter() ||
87 paint.getRasterizer() ||
88 paint.getColorFilter() ||
89 paint.getPathEffect() ||
90 paint.isFakeBoldText() ||
91 paint.getStyle() != SkPaint::kFill_Style)
92 {
93 return true;
94 }
95 return false;
96}
97
robertphillipsfcf78292015-06-19 11:49:52 -070098uint32_t GrTextContext::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
robertphillips9c240a12015-05-28 07:45:59 -070099 uint32_t flags = paint.getFlags();
100
101 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
102 return flags;
103 }
104
robertphillipsfcf78292015-06-19 11:49:52 -0700105 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
robertphillips9c240a12015-05-28 07:45:59 -0700106 flags &= ~SkPaint::kLCDRenderText_Flag;
107 flags |= SkPaint::kGenA8FromLCD_Flag;
108 }
109
110 return flags;
111}
112
113void GrTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700114 const GrClip& clip, const SkPaint& skPaint,
joshualitt9c328182015-03-23 08:13:04 -0700115 const SkMatrix& viewMatrix, const SkTextBlob* blob,
116 SkScalar x, SkScalar y,
117 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
cdaltone68f7362015-03-25 14:02:37 -0700118 SkPaint runPaint = skPaint;
joshualitt9c328182015-03-23 08:13:04 -0700119
cdaltone68f7362015-03-25 14:02:37 -0700120 SkTextBlob::RunIterator it(blob);
121 for (;!it.done(); it.next()) {
122 size_t textLen = it.glyphCount() * sizeof(uint16_t);
123 const SkPoint& offset = it.offset();
124 // applyFontToPaint() always overwrites the exact same attributes,
125 // so it is safe to not re-seed the paint for this reason.
126 it.applyFontToPaint(&runPaint);
127
128 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
129 // A false return from filter() means we should abort the current draw.
130 runPaint = skPaint;
131 continue;
joshualitt9c328182015-03-23 08:13:04 -0700132 }
cdaltone68f7362015-03-25 14:02:37 -0700133
robertphillipsfcf78292015-06-19 11:49:52 -0700134 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
cdaltone68f7362015-03-25 14:02:37 -0700135
136 GrPaint grPaint;
joshualitt9df46592015-07-09 10:55:28 -0700137 if (!SkPaint2GrPaint(fContext, rt, runPaint, viewMatrix, true, &grPaint)) {
bsalomonbed83a62015-04-15 14:18:34 -0700138 return;
139 }
cdaltone68f7362015-03-25 14:02:37 -0700140
141 switch (it.positioning()) {
142 case SkTextBlob::kDefault_Positioning:
143 this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
144 textLen, x + offset.x(), y + offset.y(), clipBounds);
145 break;
146 case SkTextBlob::kHorizontal_Positioning:
147 this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
148 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
149 break;
150 case SkTextBlob::kFull_Positioning:
151 this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
152 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
153 break;
154 default:
155 SkFAIL("unhandled positioning mode");
156 }
157
158 if (drawFilter) {
159 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
160 runPaint = skPaint;
161 }
162 }
joshualitt9c328182015-03-23 08:13:04 -0700163}
164
robertphillips2334fb62015-06-17 05:43:33 -0700165void GrTextContext::drawTextAsPath(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700166 const GrClip& clip,
167 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt6e8cd962015-03-20 10:30:14 -0700168 const char text[], size_t byteLength, SkScalar x, SkScalar y,
169 const SkIRect& clipBounds) {
170 SkTextToPathIter iter(text, byteLength, skPaint, true);
171
172 SkMatrix matrix;
173 matrix.setScale(iter.getPathScale(), iter.getPathScale());
174 matrix.postTranslate(x, y);
175
176 const SkPath* iterPath;
177 SkScalar xpos, prevXPos = 0;
178
179 while (iter.next(&iterPath, &xpos)) {
180 matrix.postTranslate(xpos - prevXPos, 0);
181 if (iterPath) {
182 const SkPaint& pnt = iter.getPaint();
robertphillips2334fb62015-06-17 05:43:33 -0700183 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, *iterPath,
robertphillipsccb1b572015-05-27 11:02:55 -0700184 pnt, viewMatrix, &matrix, clipBounds, false);
joshualitt6e8cd962015-03-20 10:30:14 -0700185 }
186 prevXPos = xpos;
187 }
188}
189
robertphillips2334fb62015-06-17 05:43:33 -0700190void GrTextContext::drawPosTextAsPath(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700191 const GrClip& clip,
192 const SkPaint& origPaint, const SkMatrix& viewMatrix,
joshualitt6e8cd962015-03-20 10:30:14 -0700193 const char text[], size_t byteLength,
194 const SkScalar pos[], int scalarsPerPosition,
195 const SkPoint& offset, const SkIRect& clipBounds) {
196 // setup our std paint, in hopes of getting hits in the cache
197 SkPaint paint(origPaint);
198 SkScalar matrixScale = paint.setupForAsPaths();
199
200 SkMatrix matrix;
201 matrix.setScale(matrixScale, matrixScale);
202
203 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
204 paint.setStyle(SkPaint::kFill_Style);
205 paint.setPathEffect(NULL);
206
207 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
robertphillips8e0c1502015-07-07 10:28:43 -0700208 SkAutoGlyphCache autoCache(paint, &fSurfaceProps, NULL);
joshualitt6e8cd962015-03-20 10:30:14 -0700209 SkGlyphCache* cache = autoCache.getCache();
210
211 const char* stop = text + byteLength;
212 SkTextAlignProc alignProc(paint.getTextAlign());
213 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
214
215 // Now restore the original settings, so we "draw" with whatever style/stroking.
216 paint.setStyle(origPaint.getStyle());
217 paint.setPathEffect(origPaint.getPathEffect());
218
219 while (text < stop) {
220 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
221 if (glyph.fWidth) {
222 const SkPath* path = cache->findPath(glyph);
223 if (path) {
224 SkPoint tmsLoc;
225 tmsProc(pos, &tmsLoc);
226 SkPoint loc;
227 alignProc(tmsLoc, glyph, &loc);
228
229 matrix[SkMatrix::kMTransX] = loc.fX;
230 matrix[SkMatrix::kMTransY] = loc.fY;
robertphillips2334fb62015-06-17 05:43:33 -0700231 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, *path, paint,
robertphillipsccb1b572015-05-27 11:02:55 -0700232 viewMatrix, &matrix, clipBounds, false);
joshualitt6e8cd962015-03-20 10:30:14 -0700233 }
234 }
235 pos += scalarsPerPosition;
236 }
237}
jvanverth8c27a182014-10-14 08:45:50 -0700238
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000239//*** change to output positions?
jvanverth73f10532014-10-23 11:57:12 -0700240int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000241 const char text[], size_t byteLength, SkVector* stopVector) {
242 SkFixed x = 0, y = 0;
243 const char* stop = text + byteLength;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000244
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000245 SkAutoKern autokern;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000246
jvanverth73f10532014-10-23 11:57:12 -0700247 int numGlyphs = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000248 while (text < stop) {
249 // don't need x, y here, since all subpixel variants will have the
250 // same advance
251 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000252
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000253 x += autokern.adjust(glyph) + glyph.fAdvanceX;
254 y += glyph.fAdvanceY;
jvanverth73f10532014-10-23 11:57:12 -0700255 ++numGlyphs;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000256 }
257 stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000258
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000259 SkASSERT(text == stop);
jvanverth73f10532014-10-23 11:57:12 -0700260
261 return numGlyphs;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000262}
263
264static void GlyphCacheAuxProc(void* data) {
265 GrFontScaler* scaler = (GrFontScaler*)data;
266 SkSafeUnref(scaler);
267}
268
269GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
270 void* auxData;
271 GrFontScaler* scaler = NULL;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000272
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000273 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
274 scaler = (GrFontScaler*)auxData;
275 }
276 if (NULL == scaler) {
jvanverth733f5f52014-07-11 19:45:16 -0700277 scaler = SkNEW_ARGS(GrFontScaler, (cache));
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000278 cache->setAuxProc(GlyphCacheAuxProc, scaler);
279 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000280
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000281 return scaler;
282}