blob: 5559b2da6d3d6b1b54315550b1dc2a40d3d80f8f [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,
24 const SkDeviceProperties& properties)
joshualitt6e8cd962015-03-20 10:30:14 -070025 : fFallbackTextContext(NULL)
26 , fContext(context)
robertphillips2334fb62015-06-17 05:43:33 -070027 , fDeviceProperties(properties)
28 , 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
joshualitt570d2f82015-02-25 13:19:48 -080035void GrTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
joshualitt6e8cd962015-03-20 10:30:14 -070036 const SkPaint& skPaint, const SkIRect& regionClipBounds) {
joshualitt570d2f82015-02-25 13:19:48 -080037 fClip = clip;
robertphillips@google.combeb1af72012-07-26 18:52:16 +000038
joshualitt25d9c152015-02-18 12:29:52 -080039 fRenderTarget.reset(SkRef(rt));
40
joshualitt6e8cd962015-03-20 10:30:14 -070041 fRegionClipBounds = regionClipBounds;
joshualitt570d2f82015-02-25 13:19:48 -080042 fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
robertphillips@google.combeb1af72012-07-26 18:52:16 +000043
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000044 fPaint = grPaint;
45 fSkPaint = skPaint;
reed@google.comac10a2d2010-12-22 21:39:39 +000046}
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000047
joshualitt6e8cd962015-03-20 10:30:14 -070048void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
joshualitt570d2f82015-02-25 13:19:48 -080049 const SkPaint& skPaint, const SkMatrix& viewMatrix,
jvanverth8c27a182014-10-14 08:45:50 -070050 const char text[], size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070051 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -070052 if (fContext->abandoned() || !fDrawContext) {
robertphillipsccb1b572015-05-27 11:02:55 -070053 return;
54 }
55
jvanverthaab626c2014-10-16 08:04:39 -070056 GrTextContext* textContext = this;
57 do {
cdaltone68f7362015-03-25 14:02:37 -070058 if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
robertphillips2334fb62015-06-17 05:43:33 -070059 textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070060 text, byteLength, x, y, clipBounds);
joshualitt6e8cd962015-03-20 10:30:14 -070061 return;
jvanverthaab626c2014-10-16 08:04:39 -070062 }
63 textContext = textContext->fFallbackTextContext;
64 } while (textContext);
jvanverth8c27a182014-10-14 08:45:50 -070065
joshualitt6e8cd962015-03-20 10:30:14 -070066 // fall back to drawing as a path
robertphillips2334fb62015-06-17 05:43:33 -070067 this->drawTextAsPath(rt, clip, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070068 text, byteLength, x, y, clipBounds);
jvanverth8c27a182014-10-14 08:45:50 -070069}
70
joshualitt6e8cd962015-03-20 10:30:14 -070071void GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
joshualitt570d2f82015-02-25 13:19:48 -080072 const SkPaint& skPaint, const SkMatrix& viewMatrix,
jvanverth8c27a182014-10-14 08:45:50 -070073 const char text[], size_t byteLength,
74 const SkScalar pos[], int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -070075 const SkPoint& offset, const SkIRect& clipBounds) {
robertphillips2334fb62015-06-17 05:43:33 -070076 if (fContext->abandoned() || !fDrawContext) {
robertphillipsccb1b572015-05-27 11:02:55 -070077 return;
78 }
79
jvanverth8c27a182014-10-14 08:45:50 -070080 GrTextContext* textContext = this;
81 do {
cdaltone68f7362015-03-25 14:02:37 -070082 if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
robertphillips2334fb62015-06-17 05:43:33 -070083 textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix,
robertphillipsccb1b572015-05-27 11:02:55 -070084 text, byteLength, pos,
joshualitt6e8cd962015-03-20 10:30:14 -070085 scalarsPerPosition, offset, clipBounds);
86 return;
jvanverth8c27a182014-10-14 08:45:50 -070087 }
88 textContext = textContext->fFallbackTextContext;
89 } while (textContext);
90
joshualitt6e8cd962015-03-20 10:30:14 -070091 // fall back to drawing as a path
robertphillips2334fb62015-06-17 05:43:33 -070092 this->drawPosTextAsPath(rt, clip, skPaint, viewMatrix, text, byteLength, pos,
robertphillipsccb1b572015-05-27 11:02:55 -070093 scalarsPerPosition, offset, clipBounds);
jvanverth8c27a182014-10-14 08:45:50 -070094}
95
robertphillips9c240a12015-05-28 07:45:59 -070096bool GrTextContext::ShouldDisableLCD(const SkPaint& paint) {
97 if (paint.getShader() ||
98 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode) ||
99 paint.getMaskFilter() ||
100 paint.getRasterizer() ||
101 paint.getColorFilter() ||
102 paint.getPathEffect() ||
103 paint.isFakeBoldText() ||
104 paint.getStyle() != SkPaint::kFill_Style)
105 {
106 return true;
107 }
108 return false;
109}
110
111uint32_t GrTextContext::FilterTextFlags(const SkDeviceProperties& devProps, const SkPaint& paint) {
112 uint32_t flags = paint.getFlags();
113
114 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
115 return flags;
116 }
117
118 if (kUnknown_SkPixelGeometry == devProps.pixelGeometry() || ShouldDisableLCD(paint)) {
119 flags &= ~SkPaint::kLCDRenderText_Flag;
120 flags |= SkPaint::kGenA8FromLCD_Flag;
121 }
122
123 return flags;
124}
125
126void GrTextContext::drawTextBlob(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700127 const GrClip& clip, const SkPaint& skPaint,
joshualitt9c328182015-03-23 08:13:04 -0700128 const SkMatrix& viewMatrix, const SkTextBlob* blob,
129 SkScalar x, SkScalar y,
130 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
cdaltone68f7362015-03-25 14:02:37 -0700131 SkPaint runPaint = skPaint;
joshualitt9c328182015-03-23 08:13:04 -0700132
cdaltone68f7362015-03-25 14:02:37 -0700133 SkTextBlob::RunIterator it(blob);
134 for (;!it.done(); it.next()) {
135 size_t textLen = it.glyphCount() * sizeof(uint16_t);
136 const SkPoint& offset = it.offset();
137 // applyFontToPaint() always overwrites the exact same attributes,
138 // so it is safe to not re-seed the paint for this reason.
139 it.applyFontToPaint(&runPaint);
140
141 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
142 // A false return from filter() means we should abort the current draw.
143 runPaint = skPaint;
144 continue;
joshualitt9c328182015-03-23 08:13:04 -0700145 }
cdaltone68f7362015-03-25 14:02:37 -0700146
robertphillips9c240a12015-05-28 07:45:59 -0700147 runPaint.setFlags(FilterTextFlags(fDeviceProperties, runPaint));
cdaltone68f7362015-03-25 14:02:37 -0700148
149 GrPaint grPaint;
bsalomonbed83a62015-04-15 14:18:34 -0700150 if (!SkPaint2GrPaint(fContext, fRenderTarget, runPaint, viewMatrix, true, &grPaint)) {
151 return;
152 }
cdaltone68f7362015-03-25 14:02:37 -0700153
154 switch (it.positioning()) {
155 case SkTextBlob::kDefault_Positioning:
156 this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
157 textLen, x + offset.x(), y + offset.y(), clipBounds);
158 break;
159 case SkTextBlob::kHorizontal_Positioning:
160 this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
161 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
162 break;
163 case SkTextBlob::kFull_Positioning:
164 this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
165 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
166 break;
167 default:
168 SkFAIL("unhandled positioning mode");
169 }
170
171 if (drawFilter) {
172 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
173 runPaint = skPaint;
174 }
175 }
joshualitt9c328182015-03-23 08:13:04 -0700176}
177
robertphillips2334fb62015-06-17 05:43:33 -0700178void GrTextContext::drawTextAsPath(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700179 const GrClip& clip,
180 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt6e8cd962015-03-20 10:30:14 -0700181 const char text[], size_t byteLength, SkScalar x, SkScalar y,
182 const SkIRect& clipBounds) {
183 SkTextToPathIter iter(text, byteLength, skPaint, true);
184
185 SkMatrix matrix;
186 matrix.setScale(iter.getPathScale(), iter.getPathScale());
187 matrix.postTranslate(x, y);
188
189 const SkPath* iterPath;
190 SkScalar xpos, prevXPos = 0;
191
192 while (iter.next(&iterPath, &xpos)) {
193 matrix.postTranslate(xpos - prevXPos, 0);
194 if (iterPath) {
195 const SkPaint& pnt = iter.getPaint();
robertphillips2334fb62015-06-17 05:43:33 -0700196 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, *iterPath,
robertphillipsccb1b572015-05-27 11:02:55 -0700197 pnt, viewMatrix, &matrix, clipBounds, false);
joshualitt6e8cd962015-03-20 10:30:14 -0700198 }
199 prevXPos = xpos;
200 }
201}
202
robertphillips2334fb62015-06-17 05:43:33 -0700203void GrTextContext::drawPosTextAsPath(GrRenderTarget* rt,
robertphillipsccb1b572015-05-27 11:02:55 -0700204 const GrClip& clip,
205 const SkPaint& origPaint, const SkMatrix& viewMatrix,
joshualitt6e8cd962015-03-20 10:30:14 -0700206 const char text[], size_t byteLength,
207 const SkScalar pos[], int scalarsPerPosition,
208 const SkPoint& offset, const SkIRect& clipBounds) {
209 // setup our std paint, in hopes of getting hits in the cache
210 SkPaint paint(origPaint);
211 SkScalar matrixScale = paint.setupForAsPaths();
212
213 SkMatrix matrix;
214 matrix.setScale(matrixScale, matrixScale);
215
216 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
217 paint.setStyle(SkPaint::kFill_Style);
218 paint.setPathEffect(NULL);
219
220 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
221 SkAutoGlyphCache autoCache(paint, NULL, NULL);
222 SkGlyphCache* cache = autoCache.getCache();
223
224 const char* stop = text + byteLength;
225 SkTextAlignProc alignProc(paint.getTextAlign());
226 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
227
228 // Now restore the original settings, so we "draw" with whatever style/stroking.
229 paint.setStyle(origPaint.getStyle());
230 paint.setPathEffect(origPaint.getPathEffect());
231
232 while (text < stop) {
233 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
234 if (glyph.fWidth) {
235 const SkPath* path = cache->findPath(glyph);
236 if (path) {
237 SkPoint tmsLoc;
238 tmsProc(pos, &tmsLoc);
239 SkPoint loc;
240 alignProc(tmsLoc, glyph, &loc);
241
242 matrix[SkMatrix::kMTransX] = loc.fX;
243 matrix[SkMatrix::kMTransY] = loc.fY;
robertphillips2334fb62015-06-17 05:43:33 -0700244 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext, rt, clip, *path, paint,
robertphillipsccb1b572015-05-27 11:02:55 -0700245 viewMatrix, &matrix, clipBounds, false);
joshualitt6e8cd962015-03-20 10:30:14 -0700246 }
247 }
248 pos += scalarsPerPosition;
249 }
250}
jvanverth8c27a182014-10-14 08:45:50 -0700251
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000252//*** change to output positions?
jvanverth73f10532014-10-23 11:57:12 -0700253int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000254 const char text[], size_t byteLength, SkVector* stopVector) {
255 SkFixed x = 0, y = 0;
256 const char* stop = text + byteLength;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000257
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000258 SkAutoKern autokern;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000259
jvanverth73f10532014-10-23 11:57:12 -0700260 int numGlyphs = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000261 while (text < stop) {
262 // don't need x, y here, since all subpixel variants will have the
263 // same advance
264 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000265
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000266 x += autokern.adjust(glyph) + glyph.fAdvanceX;
267 y += glyph.fAdvanceY;
jvanverth73f10532014-10-23 11:57:12 -0700268 ++numGlyphs;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000269 }
270 stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000271
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000272 SkASSERT(text == stop);
jvanverth73f10532014-10-23 11:57:12 -0700273
274 return numGlyphs;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000275}
276
277static void GlyphCacheAuxProc(void* data) {
278 GrFontScaler* scaler = (GrFontScaler*)data;
279 SkSafeUnref(scaler);
280}
281
282GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
283 void* auxData;
284 GrFontScaler* scaler = NULL;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000285
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000286 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
287 scaler = (GrFontScaler*)auxData;
288 }
289 if (NULL == scaler) {
jvanverth733f5f52014-07-11 19:45:16 -0700290 scaler = SkNEW_ARGS(GrFontScaler, (cache));
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000291 cache->setAuxProc(GlyphCacheAuxProc, scaler);
292 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000293
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000294 return scaler;
295}