blob: b9f5cedc1531c7115b64f31eaef5d9b46f6c67b5 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkPaint.h"
reed@google.comb0a34d82012-07-11 19:57:55 +00009#include "SkAnnotation.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000010#include "SkAutoKern.h"
mtkleinfb1fe4f2014-10-07 09:26:10 -070011#include "SkChecksum.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000013#include "SkData.h"
rmistryc4b84ae2014-06-23 06:59:15 -070014#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000015#include "SkFontDescriptor.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000016#include "SkGlyphCache.h"
reed@google.com15356a62011-11-03 19:29:08 +000017#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000019#include "SkMaskGamma.h"
mtklein1b249332015-07-07 12:21:21 -070020#include "SkMutex.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000021#include "SkReadBuffer.h"
22#include "SkWriteBuffer.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000023#include "SkPaintDefaults.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPathEffect.h"
25#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000026#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000028#include "SkShader.h"
29#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000032#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000033#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkTypeface.h"
halcanary435657f2015-09-15 12:53:07 -070035#include "SkStrokeRec.h"
robertphillipsfcf78292015-06-19 11:49:52 -070036#include "SkSurfacePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000037#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000038
reed9a878a02015-12-27 12:47:25 -080039static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) {
40 return cond ? bits | mask : bits & ~mask;
41}
42
reed@google.coma3237872011-07-05 19:20:48 +000043// define this to get a printf for out-of-range parameter in setters
44// e.g. setTextSize(-1)
45//#define SK_REPORT_API_RANGE_CHECK
46
reed@android.coma3122b92009-08-13 20:38:25 +000047SkPaint::SkPaint() {
halcanary96fcdcc2015-08-27 07:41:13 -070048 fTypeface = nullptr;
49 fPathEffect = nullptr;
50 fShader = nullptr;
51 fXfermode = nullptr;
52 fMaskFilter = nullptr;
53 fColorFilter = nullptr;
54 fRasterizer = nullptr;
55 fLooper = nullptr;
56 fImageFilter = nullptr;
57 fAnnotation = nullptr;
reed@android.coma3122b92009-08-13 20:38:25 +000058
reedf59eab22014-07-14 14:39:15 -070059 fTextSize = SkPaintDefaults_TextSize;
60 fTextScaleX = SK_Scalar1;
61 fTextSkewX = 0;
62 fColor = SK_ColorBLACK;
63 fWidth = 0;
64 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000065
66 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070067 fBitfieldsUInt = 0;
68 fBitfields.fFlags = SkPaintDefaults_Flags;
69 fBitfields.fCapType = kDefault_Cap;
70 fBitfields.fJoinType = kDefault_Join;
71 fBitfields.fTextAlign = kLeft_Align;
72 fBitfields.fStyle = kFill_Style;
73 fBitfields.fTextEncoding = kUTF8_TextEncoding;
74 fBitfields.fHinting = SkPaintDefaults_Hinting;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075}
76
reed@google.com6fb7e2e2011-02-08 22:22:52 +000077SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000078#define COPY(field) field = src.field
79#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000081 REF_COPY(fTypeface);
82 REF_COPY(fPathEffect);
83 REF_COPY(fShader);
84 REF_COPY(fXfermode);
85 REF_COPY(fMaskFilter);
86 REF_COPY(fColorFilter);
87 REF_COPY(fRasterizer);
88 REF_COPY(fLooper);
89 REF_COPY(fImageFilter);
90 REF_COPY(fAnnotation);
91
92 COPY(fTextSize);
93 COPY(fTextScaleX);
94 COPY(fTextSkewX);
95 COPY(fColor);
96 COPY(fWidth);
97 COPY(fMiterLimit);
98 COPY(fBitfields);
djsollen@google.com40078cb2013-05-24 20:31:57 +000099
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000100#undef COPY
101#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102}
103
bungemanccce0e02016-02-07 14:37:23 -0800104SkPaint::SkPaint(SkPaint&& src) {
105#define MOVE(field) field = std::move(src.field)
106#define REF_MOVE(field) field = src.field; src.field = nullptr
107
108 REF_MOVE(fTypeface);
109 REF_MOVE(fPathEffect);
110 REF_MOVE(fShader);
111 REF_MOVE(fXfermode);
112 REF_MOVE(fMaskFilter);
113 REF_MOVE(fColorFilter);
114 REF_MOVE(fRasterizer);
115 REF_MOVE(fLooper);
116 REF_MOVE(fImageFilter);
117 REF_MOVE(fAnnotation);
118
119 MOVE(fTextSize);
120 MOVE(fTextScaleX);
121 MOVE(fTextSkewX);
122 MOVE(fColor);
123 MOVE(fWidth);
124 MOVE(fMiterLimit);
125 MOVE(fBitfields);
126
127#undef MOVE
128#undef REF_MOVE
129}
130
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000131SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000132 SkSafeUnref(fTypeface);
133 SkSafeUnref(fPathEffect);
134 SkSafeUnref(fShader);
135 SkSafeUnref(fXfermode);
136 SkSafeUnref(fMaskFilter);
137 SkSafeUnref(fColorFilter);
138 SkSafeUnref(fRasterizer);
139 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000140 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000141 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142}
143
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000144SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000145 if (this == &src) {
146 return *this;
147 }
148
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000149#define COPY(field) field = src.field
150#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
151
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000152 REF_COPY(fTypeface);
153 REF_COPY(fPathEffect);
154 REF_COPY(fShader);
155 REF_COPY(fXfermode);
156 REF_COPY(fMaskFilter);
157 REF_COPY(fColorFilter);
158 REF_COPY(fRasterizer);
159 REF_COPY(fLooper);
160 REF_COPY(fImageFilter);
161 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000163 COPY(fTextSize);
164 COPY(fTextScaleX);
165 COPY(fTextSkewX);
166 COPY(fColor);
167 COPY(fWidth);
168 COPY(fMiterLimit);
169 COPY(fBitfields);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
171 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000172
173#undef COPY
174#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175}
176
bungemanccce0e02016-02-07 14:37:23 -0800177SkPaint& SkPaint::operator=(SkPaint&& src) {
178 if (this == &src) {
179 return *this;
180 }
181
182#define MOVE(field) field = std::move(src.field)
183#define REF_MOVE(field) SkSafeUnref(field); field = src.field; src.field = nullptr
184
185 REF_MOVE(fTypeface);
186 REF_MOVE(fPathEffect);
187 REF_MOVE(fShader);
188 REF_MOVE(fXfermode);
189 REF_MOVE(fMaskFilter);
190 REF_MOVE(fColorFilter);
191 REF_MOVE(fRasterizer);
192 REF_MOVE(fLooper);
193 REF_MOVE(fImageFilter);
194 REF_MOVE(fAnnotation);
195
196 MOVE(fTextSize);
197 MOVE(fTextScaleX);
198 MOVE(fTextSkewX);
199 MOVE(fColor);
200 MOVE(fWidth);
201 MOVE(fMiterLimit);
202 MOVE(fBitfields);
203
204 return *this;
205
206#undef MOVE
207#undef REF_MOVE
208}
209
robertphillips@google.comb2657412013-08-07 22:36:29 +0000210bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000211#define EQUAL(field) (a.field == b.field)
mtklein610a0152014-09-25 11:57:53 -0700212 return EQUAL(fTypeface)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000213 && EQUAL(fPathEffect)
214 && EQUAL(fShader)
215 && EQUAL(fXfermode)
216 && EQUAL(fMaskFilter)
217 && EQUAL(fColorFilter)
218 && EQUAL(fRasterizer)
219 && EQUAL(fLooper)
220 && EQUAL(fImageFilter)
221 && EQUAL(fAnnotation)
222 && EQUAL(fTextSize)
223 && EQUAL(fTextScaleX)
224 && EQUAL(fTextSkewX)
225 && EQUAL(fColor)
226 && EQUAL(fWidth)
227 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700228 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000229 ;
230#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000231}
232
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000233void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 SkPaint init;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000236}
237
reedf803da12015-01-23 05:58:07 -0800238void SkPaint::setFilterQuality(SkFilterQuality quality) {
239 fBitfields.fFilterQuality = quality;
reed@google.comc9683152013-07-18 13:47:01 +0000240}
241
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000242void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700243 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244}
245
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700247 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248}
249
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000250void SkPaint::setAntiAlias(bool doAA) {
reed9a878a02015-12-27 12:47:25 -0800251 this->setFlags(set_clear_mask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252}
253
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000254void SkPaint::setDither(bool doDither) {
reed9a878a02015-12-27 12:47:25 -0800255 this->setFlags(set_clear_mask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256}
257
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258void SkPaint::setSubpixelText(bool doSubpixel) {
reed9a878a02015-12-27 12:47:25 -0800259 this->setFlags(set_clear_mask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setLCDRenderText(bool doLCDRender) {
reed9a878a02015-12-27 12:47:25 -0800263 this->setFlags(set_clear_mask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reed9a878a02015-12-27 12:47:25 -0800267 this->setFlags(set_clear_mask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setAutohinted(bool useAutohinter) {
reed9a878a02015-12-27 12:47:25 -0800271 this->setFlags(set_clear_mask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000272}
273
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000274void SkPaint::setLinearText(bool doLinearText) {
reed9a878a02015-12-27 12:47:25 -0800275 this->setFlags(set_clear_mask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276}
277
reed@google.com830a23e2011-11-10 15:20:49 +0000278void SkPaint::setVerticalText(bool doVertical) {
reed9a878a02015-12-27 12:47:25 -0800279 this->setFlags(set_clear_mask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000280}
281
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000282void SkPaint::setUnderlineText(bool doUnderline) {
reed9a878a02015-12-27 12:47:25 -0800283 this->setFlags(set_clear_mask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284}
285
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed9a878a02015-12-27 12:47:25 -0800287 this->setFlags(set_clear_mask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288}
289
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000290void SkPaint::setFakeBoldText(bool doFakeBold) {
reed9a878a02015-12-27 12:47:25 -0800291 this->setFlags(set_clear_mask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292}
293
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294void SkPaint::setDevKernText(bool doDevKern) {
reed9a878a02015-12-27 12:47:25 -0800295 this->setFlags(set_clear_mask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296}
297
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000298void SkPaint::setStyle(Style style) {
299 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700300 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000301 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000302#ifdef SK_REPORT_API_RANGE_CHECK
303 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
304#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306}
307
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000308void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 fColor = color;
310}
311
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000312void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000313 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
314 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000317void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000318 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319}
320
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000321void SkPaint::setStrokeWidth(SkScalar width) {
322 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000324 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000325#ifdef SK_REPORT_API_RANGE_CHECK
326 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
327#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329}
330
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331void SkPaint::setStrokeMiter(SkScalar limit) {
332 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000334 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000335#ifdef SK_REPORT_API_RANGE_CHECK
336 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
337#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000338 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339}
340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341void SkPaint::setStrokeCap(Cap ct) {
342 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700343 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000345#ifdef SK_REPORT_API_RANGE_CHECK
346 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
347#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349}
350
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000351void SkPaint::setStrokeJoin(Join jt) {
352 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700353 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000354 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000355#ifdef SK_REPORT_API_RANGE_CHECK
356 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
357#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000358 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359}
360
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000361///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000363void SkPaint::setTextAlign(Align align) {
364 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700365 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000366 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000367#ifdef SK_REPORT_API_RANGE_CHECK
368 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
369#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000370 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371}
372
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000373void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000374 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000376 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000377#ifdef SK_REPORT_API_RANGE_CHECK
378 SkDebugf("SkPaint::setTextSize() called with negative value\n");
379#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000380 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381}
382
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000383void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 fTextScaleX = scaleX;
385}
386
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000387void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 fTextSkewX = skewX;
389}
390
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000391void SkPaint::setTextEncoding(TextEncoding encoding) {
392 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700393 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000394 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000395#ifdef SK_REPORT_API_RANGE_CHECK
396 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
397#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000398 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399}
400
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000401///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000403SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 SkRefCnt_SafeAssign(fTypeface, font);
405 return font;
406}
407
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000408SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 SkRefCnt_SafeAssign(fRasterizer, r);
410 return r;
411}
412
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000413SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 SkRefCnt_SafeAssign(fLooper, looper);
415 return looper;
416}
417
reed@google.com15356a62011-11-03 19:29:08 +0000418SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
419 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
reed@google.com15356a62011-11-03 19:29:08 +0000420 return imageFilter;
421}
422
reed@google.comb0a34d82012-07-11 19:57:55 +0000423SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
424 SkRefCnt_SafeAssign(fAnnotation, annotation);
reed@google.comb0a34d82012-07-11 19:57:55 +0000425 return annotation;
426}
427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428///////////////////////////////////////////////////////////////////////////////
429
reed@google.comed43dff2013-06-04 16:56:27 +0000430static SkScalar mag2(SkScalar x, SkScalar y) {
431 return x * x + y * y;
432}
433
434static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
435 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
436 ||
437 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
438}
439
440bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
441 SkASSERT(!ctm.hasPerspective());
442 SkASSERT(!textM.hasPerspective());
443
444 SkMatrix matrix;
445 matrix.setConcat(ctm, textM);
446 return tooBig(matrix, MaxCacheSize2());
447}
448
reed@google.comed43dff2013-06-04 16:56:27 +0000449
450///////////////////////////////////////////////////////////////////////////////
451
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452#include "SkGlyphCache.h"
453#include "SkUtils.h"
454
reed@google.com90808e82013-03-19 14:44:54 +0000455static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
456 void* context) {
457 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000458}
459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
461 uint16_t glyphs[]) const {
462 if (byteLength == 0) {
463 return 0;
464 }
reed@google.com72cf4922011-01-04 19:58:20 +0000465
halcanary96fcdcc2015-08-27 07:41:13 -0700466 SkASSERT(textData != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467
halcanary96fcdcc2015-08-27 07:41:13 -0700468 if (nullptr == glyphs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 switch (this->getTextEncoding()) {
470 case kUTF8_TextEncoding:
471 return SkUTF8_CountUnichars((const char*)textData, byteLength);
472 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000473 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000474 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000475 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000477 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000479 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480 }
481 return 0;
482 }
reed@google.com72cf4922011-01-04 19:58:20 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 // handle this encoding before the setup for the glyphcache
487 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
488 // we want to ignore the low bit of byteLength
489 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000490 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 }
reed@google.com72cf4922011-01-04 19:58:20 +0000492
halcanary96fcdcc2015-08-27 07:41:13 -0700493 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 SkGlyphCache* cache = autoCache.getCache();
495
496 const char* text = (const char*)textData;
497 const char* stop = text + byteLength;
498 uint16_t* gptr = glyphs;
499
500 switch (this->getTextEncoding()) {
501 case SkPaint::kUTF8_TextEncoding:
502 while (text < stop) {
503 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
504 }
505 break;
506 case SkPaint::kUTF16_TextEncoding: {
507 const uint16_t* text16 = (const uint16_t*)text;
508 const uint16_t* stop16 = (const uint16_t*)stop;
509 while (text16 < stop16) {
510 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
511 }
512 break;
513 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000514 case kUTF32_TextEncoding: {
515 const int32_t* text32 = (const int32_t*)text;
516 const int32_t* stop32 = (const int32_t*)stop;
517 while (text32 < stop32) {
518 *gptr++ = cache->unicharToGlyph(*text32++);
519 }
520 break;
521 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000523 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000525 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
reed@android.coma5dcaf62010-02-05 17:12:32 +0000528bool SkPaint::containsText(const void* textData, size_t byteLength) const {
529 if (0 == byteLength) {
530 return true;
531 }
reed@google.com72cf4922011-01-04 19:58:20 +0000532
halcanary96fcdcc2015-08-27 07:41:13 -0700533 SkASSERT(textData != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000534
reed@android.coma5dcaf62010-02-05 17:12:32 +0000535 // handle this encoding before the setup for the glyphcache
536 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
537 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
538 size_t count = byteLength >> 1;
539 for (size_t i = 0; i < count; i++) {
540 if (0 == glyphID[i]) {
541 return false;
542 }
543 }
544 return true;
545 }
reed@google.com72cf4922011-01-04 19:58:20 +0000546
halcanary96fcdcc2015-08-27 07:41:13 -0700547 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000548 SkGlyphCache* cache = autoCache.getCache();
549
550 switch (this->getTextEncoding()) {
551 case SkPaint::kUTF8_TextEncoding: {
552 const char* text = static_cast<const char*>(textData);
553 const char* stop = text + byteLength;
554 while (text < stop) {
555 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
556 return false;
557 }
558 }
559 break;
560 }
561 case SkPaint::kUTF16_TextEncoding: {
562 const uint16_t* text = static_cast<const uint16_t*>(textData);
563 const uint16_t* stop = text + (byteLength >> 1);
564 while (text < stop) {
565 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
566 return false;
567 }
568 }
569 break;
570 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000571 case SkPaint::kUTF32_TextEncoding: {
572 const int32_t* text = static_cast<const int32_t*>(textData);
573 const int32_t* stop = text + (byteLength >> 2);
574 while (text < stop) {
575 if (0 == cache->unicharToGlyph(*text++)) {
576 return false;
577 }
578 }
579 break;
580 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000581 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000582 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000583 return false;
584 }
585 return true;
586}
587
robertphillipsd24955a2015-06-26 12:17:59 -0700588void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, SkUnichar textData[]) const {
reed@android.com9d3a9852010-01-08 14:07:42 +0000589 if (count <= 0) {
590 return;
591 }
592
halcanary96fcdcc2015-08-27 07:41:13 -0700593 SkASSERT(glyphs != nullptr);
594 SkASSERT(textData != nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000595
robertphillipsd24955a2015-06-26 12:17:59 -0700596 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
halcanary96fcdcc2015-08-27 07:41:13 -0700597 SkAutoGlyphCache autoCache(*this, &props, nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000598 SkGlyphCache* cache = autoCache.getCache();
599
600 for (int index = 0; index < count; index++) {
601 textData[index] = cache->glyphToUnichar(glyphs[index]);
602 }
603}
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605///////////////////////////////////////////////////////////////////////////////
606
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000607static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
608 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700609 SkASSERT(cache != nullptr);
610 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
613}
614
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000615static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
616 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700617 SkASSERT(cache != nullptr);
618 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
621}
622
reed@google.com68bc6f72012-03-14 19:41:55 +0000623static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
624 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700625 SkASSERT(cache != nullptr);
626 SkASSERT(text != nullptr);
reed@google.com68bc6f72012-03-14 19:41:55 +0000627
628 const int32_t* ptr = *(const int32_t**)text;
629 SkUnichar uni = *ptr++;
630 *text = (const char*)ptr;
631 return cache->getUnicharMetrics(uni);
632}
633
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000634static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
635 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700636 SkASSERT(cache != nullptr);
637 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 const uint16_t* ptr = *(const uint16_t**)text;
640 unsigned glyphID = *ptr;
641 ptr += 1;
642 *text = (const char*)ptr;
643 return cache->getGlyphIDMetrics(glyphID);
644}
645
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000646static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
647 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700648 SkASSERT(cache != nullptr);
649 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
652}
653
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000654static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
655 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700656 SkASSERT(cache != nullptr);
657 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
660}
661
reed@google.com68bc6f72012-03-14 19:41:55 +0000662static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
663 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700664 SkASSERT(cache != nullptr);
665 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000666
reed@google.com68bc6f72012-03-14 19:41:55 +0000667 const int32_t* ptr = *(const int32_t**)text;
668 SkUnichar uni = *ptr++;
669 *text = (const char*)ptr;
670 return cache->getUnicharAdvance(uni);
671}
672
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000673static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
674 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700675 SkASSERT(cache != nullptr);
676 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000677
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 const uint16_t* ptr = *(const uint16_t**)text;
679 unsigned glyphID = *ptr;
680 ptr += 1;
681 *text = (const char*)ptr;
682 return cache->getGlyphIDAdvance(glyphID);
683}
684
reed9e96aa02014-10-03 12:44:37 -0700685SkMeasureCacheProc SkPaint::getMeasureCacheProc(bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
687 sk_getMetrics_utf8_next,
688 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000689 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 sk_getAdvance_utf8_next,
693 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000694 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 sk_getAdvance_glyph_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 };
reed@google.com72cf4922011-01-04 19:58:20 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 unsigned index = this->getTextEncoding();
699
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000700 if (!needFullMetrics && !this->isDevKernText()) {
reed9e96aa02014-10-03 12:44:37 -0700701 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000702 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703
704 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
705 return gMeasureCacheProcs[index];
706}
707
708///////////////////////////////////////////////////////////////////////////////
709
710static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000711 const char** text, SkFixed, SkFixed) {
halcanary96fcdcc2015-08-27 07:41:13 -0700712 SkASSERT(cache != nullptr);
713 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
716}
717
718static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000719 const char** text, SkFixed x, SkFixed y) {
halcanary96fcdcc2015-08-27 07:41:13 -0700720 SkASSERT(cache != nullptr);
721 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
724}
725
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000726static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
727 const char** text, SkFixed, SkFixed) {
halcanary96fcdcc2015-08-27 07:41:13 -0700728 SkASSERT(cache != nullptr);
729 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000730
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
732}
733
734static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735 const char** text, SkFixed x, SkFixed y) {
halcanary96fcdcc2015-08-27 07:41:13 -0700736 SkASSERT(cache != nullptr);
737 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000738
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
740 x, y);
741}
742
reed@google.com68bc6f72012-03-14 19:41:55 +0000743static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
744 const char** text, SkFixed, SkFixed) {
halcanary96fcdcc2015-08-27 07:41:13 -0700745 SkASSERT(cache != nullptr);
746 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000747
reed@google.com68bc6f72012-03-14 19:41:55 +0000748 const int32_t* ptr = *(const int32_t**)text;
749 SkUnichar uni = *ptr++;
750 *text = (const char*)ptr;
751 return cache->getUnicharMetrics(uni);
752}
753
754static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
755 const char** text, SkFixed x, SkFixed y) {
halcanary96fcdcc2015-08-27 07:41:13 -0700756 SkASSERT(cache != nullptr);
757 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000758
reed@google.com68bc6f72012-03-14 19:41:55 +0000759 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000760 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000761 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000762 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000763}
764
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000765static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
766 const char** text, SkFixed, SkFixed) {
halcanary96fcdcc2015-08-27 07:41:13 -0700767 SkASSERT(cache != nullptr);
768 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 const uint16_t* ptr = *(const uint16_t**)text;
771 unsigned glyphID = *ptr;
772 ptr += 1;
773 *text = (const char*)ptr;
774 return cache->getGlyphIDMetrics(glyphID);
775}
776
777static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000778 const char** text, SkFixed x, SkFixed y) {
halcanary96fcdcc2015-08-27 07:41:13 -0700779 SkASSERT(cache != nullptr);
780 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 const uint16_t* ptr = *(const uint16_t**)text;
783 unsigned glyphID = *ptr;
784 ptr += 1;
785 *text = (const char*)ptr;
786 return cache->getGlyphIDMetrics(glyphID, x, y);
787}
788
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000789SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 static const SkDrawCacheProc gDrawCacheProcs[] = {
791 sk_getMetrics_utf8_00,
792 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000793 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 sk_getMetrics_utf8_xy,
797 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000798 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 sk_getMetrics_glyph_xy
800 };
reed@google.com72cf4922011-01-04 19:58:20 +0000801
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 unsigned index = this->getTextEncoding();
reedf59eab22014-07-14 14:39:15 -0700803 if (fBitfields.fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000804 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000805 }
reed@google.com72cf4922011-01-04 19:58:20 +0000806
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
808 return gDrawCacheProcs[index];
809}
810
811///////////////////////////////////////////////////////////////////////////////
812
reed@google.comed43dff2013-06-04 16:56:27 +0000813#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
814SkPaint::kDevKernText_Flag | \
815SkPaint::kLinearText_Flag | \
816SkPaint::kLCDRenderText_Flag | \
817SkPaint::kEmbeddedBitmapText_Flag | \
818SkPaint::kAutoHinting_Flag | \
819SkPaint::kGenA8FromLCD_Flag )
820
821SkScalar SkPaint::setupForAsPaths() {
822 uint32_t flags = this->getFlags();
823 // clear the flags we don't care about
824 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
825 // set the flags we do care about
826 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000827
reed@google.comed43dff2013-06-04 16:56:27 +0000828 this->setFlags(flags);
829 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000830
reed@google.comed43dff2013-06-04 16:56:27 +0000831 SkScalar textSize = fTextSize;
832 this->setTextSize(kCanonicalTextSizeForPaths);
833 return textSize / kCanonicalTextSizeForPaths;
834}
835
836class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837public:
reed@google.comed43dff2013-06-04 16:56:27 +0000838 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700839 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000840 SkPaint* p = fLazy.set(paint);
841 fScale = p->setupForAsPaths();
842 fPaint = p;
843 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000845
reed@google.comed43dff2013-06-04 16:56:27 +0000846 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000847
reed@google.comed43dff2013-06-04 16:56:27 +0000848 /**
849 * Returns 0 if the paint was unmodified, or the scale factor need to
850 * the original textSize
851 */
852 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000853
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854private:
reed@google.comed43dff2013-06-04 16:56:27 +0000855 const SkPaint* fPaint;
856 SkScalar fScale;
857 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858};
859
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000860static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 bounds->set(SkIntToScalar(g.fLeft),
862 SkIntToScalar(g.fTop),
863 SkIntToScalar(g.fLeft + g.fWidth),
864 SkIntToScalar(g.fTop + g.fHeight));
865}
866
reed@google.com44da42e2011-11-10 20:04:47 +0000867static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000868 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 bounds->join(SkIntToScalar(g.fLeft) + sx,
870 SkIntToScalar(g.fTop),
871 SkIntToScalar(g.fLeft + g.fWidth) + sx,
872 SkIntToScalar(g.fTop + g.fHeight));
873}
874
reed@google.com44da42e2011-11-10 20:04:47 +0000875static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
876 SkScalar sy = Sk48Dot16ToScalar(dy);
877 bounds->join(SkIntToScalar(g.fLeft),
878 SkIntToScalar(g.fTop) + sy,
879 SkIntToScalar(g.fLeft + g.fWidth),
880 SkIntToScalar(g.fTop + g.fHeight) + sy);
881}
882
883typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
884
885// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
886static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
887 SkASSERT(0 == xyIndex || 1 == xyIndex);
888 return (&glyph.fAdvanceX)[xyIndex];
889}
890
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891SkScalar SkPaint::measure_text(SkGlyphCache* cache,
892 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000893 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000895 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000897 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000899 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 return 0;
901 }
902
halcanary96fcdcc2015-08-27 07:41:13 -0700903 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904
reed@google.com44da42e2011-11-10 20:04:47 +0000905 int xyIndex;
906 JoinBoundsProc joinBoundsProc;
907 if (this->isVerticalText()) {
908 xyIndex = 1;
909 joinBoundsProc = join_bounds_y;
910 } else {
911 xyIndex = 0;
912 joinBoundsProc = join_bounds_x;
913 }
914
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 int n = 1;
916 const char* stop = (const char*)text + byteLength;
917 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000918 // our accumulated fixed-point advances might overflow 16.16, so we use
919 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
920 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000921 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922
923 SkAutoKern autokern;
924
halcanary96fcdcc2015-08-27 07:41:13 -0700925 if (nullptr == bounds) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000926 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 int rsb;
928 for (; text < stop; n++) {
929 rsb = g->fRsbDelta;
930 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000931 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000933 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000935 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 }
937 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000938 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000940 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 int rsb;
942 for (; text < stop; n++) {
943 rsb = g->fRsbDelta;
944 g = &glyphCacheProc(cache, &text);
945 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000946 joinBoundsProc(*g, bounds, x);
947 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000949 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 for (; text < stop; n++) {
951 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000952 joinBoundsProc(*g, bounds, x);
953 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 }
955 }
956 }
957 SkASSERT(text == stop);
958
959 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000960 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961}
962
reed99ae8812014-08-26 11:30:01 -0700963SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -0700965 SkASSERT(text != nullptr || length == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966
reed@google.comed43dff2013-06-04 16:56:27 +0000967 SkCanonicalizePaint canon(*this);
968 const SkPaint& paint = canon.getPaint();
969 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000970
halcanary96fcdcc2015-08-27 07:41:13 -0700971 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 SkGlyphCache* cache = autoCache.getCache();
973
974 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000975
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000976 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 int tempCount;
978
reed@google.comed43dff2013-06-04 16:56:27 +0000979 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000980 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000982 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
984 bounds->fTop = SkScalarMul(bounds->fTop, scale);
985 bounds->fRight = SkScalarMul(bounds->fRight, scale);
986 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
987 }
988 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000989 } else if (bounds) {
990 // ensure that even if we don't measure_text we still update the bounds
991 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 }
993 return width;
994}
995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700997 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000998 if (0 == length || 0 >= maxWidth) {
999 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001001 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 return 0;
1003 }
1004
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001005 if (0 == fTextSize) {
1006 if (measuredWidth) {
1007 *measuredWidth = 0;
1008 }
1009 return length;
1010 }
1011
halcanary96fcdcc2015-08-27 07:41:13 -07001012 SkASSERT(textD != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -07001014 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015
reed@google.comed43dff2013-06-04 16:56:27 +00001016 SkCanonicalizePaint canon(*this);
1017 const SkPaint& paint = canon.getPaint();
1018 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019
reed@google.comed43dff2013-06-04 16:56:27 +00001020 // adjust max in case we changed the textSize in paint
1021 if (scale) {
1022 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 }
reed@google.com72cf4922011-01-04 19:58:20 +00001024
halcanary96fcdcc2015-08-27 07:41:13 -07001025 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 SkGlyphCache* cache = autoCache.getCache();
1027
reed9e96aa02014-10-03 12:44:37 -07001028 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(false);
reed@google.comed43dff2013-06-04 16:56:27 +00001029 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001030 // use 64bits for our accumulator, to avoid overflowing 16.16
1031 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1032 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033
1034 SkAutoKern autokern;
1035
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001036 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -07001038 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 const char* curr = text;
1040 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001041 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001042 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 width -= x;
1044 text = curr;
1045 break;
1046 }
1047 rsb = g.fRsbDelta;
1048 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001049 } else {
reed9e96aa02014-10-03 12:44:37 -07001050 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001052 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001053 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 width -= x;
1055 text = curr;
1056 break;
1057 }
1058 }
1059 }
reed@google.com72cf4922011-01-04 19:58:20 +00001060
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001061 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001062 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001063 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001065 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 *measuredWidth = scalarWidth;
1067 }
reed@google.com72cf4922011-01-04 19:58:20 +00001068
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -07001070 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
1073///////////////////////////////////////////////////////////////////////////////
1074
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001075static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001076 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 return false; // don't detach the cache
1078}
1079
reed@google.com90808e82013-03-19 14:44:54 +00001080static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1081 void* context) {
1082 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083}
1084
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001085SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001086 SkCanonicalizePaint canon(*this);
1087 const SkPaint& paint = canon.getPaint();
1088 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001089
halcanary96fcdcc2015-08-27 07:41:13 -07001090 SkMatrix zoomMatrix, *zoomPtr = nullptr;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001091 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 zoomMatrix.setScale(zoom, zoom);
1093 zoomPtr = &zoomMatrix;
1094 }
1095
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 FontMetrics storage;
halcanary96fcdcc2015-08-27 07:41:13 -07001097 if (nullptr == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001099 }
reed@google.com72cf4922011-01-04 19:58:20 +00001100
halcanary96fcdcc2015-08-27 07:41:13 -07001101 paint.descriptorProc(nullptr, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001103 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1105 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1106 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1107 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1108 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001109 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1110 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1111 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1112 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001113 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1114 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 }
1116 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1117}
1118
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001119///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001121static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 bounds->set(g.fLeft * scale,
1123 g.fTop * scale,
1124 (g.fLeft + g.fWidth) * scale,
1125 (g.fTop + g.fHeight) * scale);
1126}
1127
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001128int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1129 SkScalar widths[], SkRect bounds[]) const {
1130 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001132 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133
bsalomon49f085d2014-09-05 13:34:00 -07001134 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
halcanary96fcdcc2015-08-27 07:41:13 -07001136 if (nullptr == widths && nullptr == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001138 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139
reed@google.comed43dff2013-06-04 16:56:27 +00001140 SkCanonicalizePaint canon(*this);
1141 const SkPaint& paint = canon.getPaint();
1142 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143
halcanary96fcdcc2015-08-27 07:41:13 -07001144 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 SkGlyphCache* cache = autoCache.getCache();
1146 SkMeasureCacheProc glyphCacheProc;
halcanary96fcdcc2015-08-27 07:41:13 -07001147 glyphCacheProc = paint.getMeasureCacheProc(nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148
1149 const char* text = (const char*)textData;
1150 const char* stop = text + byteLength;
1151 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001152 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001154 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 // we adjust the widths returned here through auto-kerning
1156 SkAutoKern autokern;
1157 SkFixed prevWidth = 0;
1158
1159 if (scale) {
1160 while (text < stop) {
1161 const SkGlyph& g = glyphCacheProc(cache, &text);
1162 if (widths) {
1163 SkFixed adjust = autokern.adjust(g);
1164
1165 if (count > 0) {
1166 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1167 *widths++ = SkScalarMul(w, scale);
1168 }
reed@google.com44da42e2011-11-10 20:04:47 +00001169 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 }
1171 if (bounds) {
1172 set_bounds(g, bounds++, scale);
1173 }
1174 ++count;
1175 }
1176 if (count > 0 && widths) {
1177 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1178 }
1179 } else {
1180 while (text < stop) {
1181 const SkGlyph& g = glyphCacheProc(cache, &text);
1182 if (widths) {
1183 SkFixed adjust = autokern.adjust(g);
1184
1185 if (count > 0) {
1186 *widths++ = SkFixedToScalar(prevWidth + adjust);
1187 }
reed@google.com44da42e2011-11-10 20:04:47 +00001188 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 }
1190 if (bounds) {
1191 set_bounds(g, bounds++);
1192 }
1193 ++count;
1194 }
1195 if (count > 0 && widths) {
1196 *widths = SkFixedToScalar(prevWidth);
1197 }
1198 }
1199 } else { // no devkern
1200 if (scale) {
1201 while (text < stop) {
1202 const SkGlyph& g = glyphCacheProc(cache, &text);
1203 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001204 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 scale);
1206 }
1207 if (bounds) {
1208 set_bounds(g, bounds++, scale);
1209 }
1210 ++count;
1211 }
1212 } else {
1213 while (text < stop) {
1214 const SkGlyph& g = glyphCacheProc(cache, &text);
1215 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001216 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 }
1218 if (bounds) {
1219 set_bounds(g, bounds++);
1220 }
1221 ++count;
1222 }
1223 }
1224 }
1225
1226 SkASSERT(text == stop);
1227 return count;
1228}
1229
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001230///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231
1232#include "SkDraw.h"
1233
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001234void SkPaint::getTextPath(const void* textData, size_t length,
1235 SkScalar x, SkScalar y, SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001236 SkASSERT(length == 0 || textData != nullptr);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001237
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001239 if (text == nullptr || length == 0 || path == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242
djsollen@google.com166e6532012-03-20 14:24:38 +00001243 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 SkMatrix matrix;
1245 SkScalar prevXPos = 0;
1246
1247 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1248 matrix.postTranslate(x, y);
1249 path->reset();
1250
1251 SkScalar xpos;
1252 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001253 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001255 if (iterPath) {
1256 path->addPath(*iterPath, matrix);
1257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 prevXPos = xpos;
1259 }
1260}
1261
reed@google.comca0062e2012-07-20 11:20:32 +00001262void SkPaint::getPosTextPath(const void* textData, size_t length,
1263 const SkPoint pos[], SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001264 SkASSERT(length == 0 || textData != nullptr);
reed@google.comca0062e2012-07-20 11:20:32 +00001265
1266 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001267 if (text == nullptr || length == 0 || path == nullptr) {
reed@google.comca0062e2012-07-20 11:20:32 +00001268 return;
1269 }
1270
1271 SkTextToPathIter iter(text, length, *this, false);
1272 SkMatrix matrix;
1273 SkPoint prevPos;
1274 prevPos.set(0, 0);
1275
1276 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1277 path->reset();
1278
1279 unsigned int i = 0;
1280 const SkPath* iterPath;
halcanary96fcdcc2015-08-27 07:41:13 -07001281 while (iter.next(&iterPath, nullptr)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001282 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001283 if (iterPath) {
1284 path->addPath(*iterPath, matrix);
1285 }
reed@google.comca0062e2012-07-20 11:20:32 +00001286 prevPos = pos[i];
1287 i++;
1288 }
1289}
1290
reed8893e5f2014-12-15 13:27:26 -08001291SkRect SkPaint::getFontBounds() const {
1292 SkMatrix m;
1293 m.setScale(fTextSize * fTextScaleX, fTextSize);
1294 m.postSkew(fTextSkewX, 0);
1295
1296 SkTypeface* typeface = this->getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001297 if (nullptr == typeface) {
reed8893e5f2014-12-15 13:27:26 -08001298 typeface = SkTypeface::GetDefaultTypeface();
1299 }
1300
1301 SkRect bounds;
1302 m.mapRect(&bounds, typeface->getBounds());
1303 return bounds;
1304}
1305
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001307 SkWriteBuffer* buffer) {
halcanary96fcdcc2015-08-27 07:41:13 -07001308 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), nullptr));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309}
1310
joshualitt20dac882015-07-24 13:16:24 -07001311static SkMask::Format compute_mask_format(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001314 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001315 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001316 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001317 }
1318
reed@google.com65dd8f82011-03-14 13:31:16 +00001319 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001320 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001321 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001322
1323 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
reed@android.com1cdcb512009-08-24 19:11:00 +00001326// if linear-text is on, then we force hinting to be off (since that's sort of
1327// the point of linear-text.
1328static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1329 SkPaint::Hinting h = paint.getHinting();
1330 if (paint.isLinearText()) {
1331 h = SkPaint::kNo_Hinting;
1332 }
1333 return h;
1334}
1335
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001336// return true if the paint is just a single color (i.e. not a shader). If its
1337// a shader, then we can't compute a const luminance for it :(
1338static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001339 SkColor c = paint.getColor();
1340
1341 SkShader* shader = paint.getShader();
1342 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001343 return false;
1344 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001345 if (paint.getColorFilter()) {
1346 c = paint.getColorFilter()->filterColor(c);
1347 }
1348 if (color) {
1349 *color = c;
1350 }
1351 return true;
1352}
1353
joshualitt9e36c1a2015-04-14 12:17:27 -07001354SkColor SkPaint::computeLuminanceColor() const {
reed@google.comce6dbb62012-02-10 22:01:45 +00001355 SkColor c;
joshualitt9e36c1a2015-04-14 12:17:27 -07001356 if (!justAColor(*this, &c)) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001357 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1358 }
1359 return c;
1360}
reed@google.com813d38b2012-02-13 21:37:57 +00001361
reed@google.comdd43df92012-02-15 14:50:29 +00001362#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1363
reed@google.com4f79b9b2011-09-13 13:23:26 +00001364// Beyond this size, LCD doesn't appreciably improve quality, but it always
1365// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001366#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1367 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1368#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001369
reedb3da83a2014-10-01 12:06:12 -07001370const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1371
reed4942e752014-10-01 13:59:33 -07001372static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001373 if (checkPost2x2) {
1374 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1375 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001376 area *= rec.fTextSize * rec.fTextSize;
1377 return area > gMaxSize2ForLCDText;
1378 } else {
1379 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001380 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001381}
1382
reed@google.com72cf4922011-01-04 19:58:20 +00001383/*
1384 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1385 * that vary only slightly when we create our key into the font cache, since the font scaler
1386 * typically returns the same looking resuts for tiny changes in the matrix.
1387 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001388static SkScalar sk_relax(SkScalar x) {
1389 int n = sk_float_round2int(x * 1024);
1390 return n / 1024.0f;
1391}
1392
reed@android.com36a4c2a2009-07-22 19:52:11 +00001393void SkScalerContext::MakeRec(const SkPaint& paint,
robertphillipsfcf78292015-06-19 11:49:52 -07001394 const SkSurfaceProps* surfaceProps,
bungeman@google.com532470f2013-01-22 19:25:14 +00001395 const SkMatrix* deviceMatrix,
1396 Rec* rec) {
halcanary96fcdcc2015-08-27 07:41:13 -07001397 SkASSERT(deviceMatrix == nullptr || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001399 SkTypeface* typeface = paint.getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001400 if (nullptr == typeface) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001401 typeface = SkTypeface::GetDefaultTypeface();
1402 }
bungemanec730b92014-08-18 07:57:35 -07001403 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 rec->fTextSize = paint.getTextSize();
1405 rec->fPreScaleX = paint.getTextScaleX();
1406 rec->fPreSkewX = paint.getTextSkewX();
1407
reedb3da83a2014-10-01 12:06:12 -07001408 bool checkPost2x2 = false;
1409
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001410 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001411 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1412 if (mask & SkMatrix::kScale_Mask) {
1413 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1414 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1415 checkPost2x2 = true;
1416 } else {
1417 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1418 }
1419 if (mask & SkMatrix::kAffine_Mask) {
1420 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1421 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1422 checkPost2x2 = true;
1423 } else {
1424 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1425 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001426 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1428 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1429 }
reed@google.com72cf4922011-01-04 19:58:20 +00001430
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkPaint::Style style = paint.getStyle();
1432 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001433
reed@google.comffe49f52011-11-22 19:42:41 +00001434 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001435
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001436 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001437#ifdef SK_USE_FREETYPE_EMBOLDEN
1438 flags |= SkScalerContext::kEmbolden_Flag;
1439#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001440 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1441 kStdFakeBoldInterpKeys,
1442 kStdFakeBoldInterpValues,
1443 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001445
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001446 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 style = SkPaint::kStrokeAndFill_Style;
1448 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001449 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001451 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001452#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 }
1454
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001455 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001457 }
reed@google.com72cf4922011-01-04 19:58:20 +00001458
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001459 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 rec->fFrameWidth = strokeWidth;
1461 rec->fMiterLimit = paint.getStrokeMiter();
1462 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1463
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001464 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001466 }
1467 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 rec->fFrameWidth = 0;
1469 rec->fMiterLimit = 0;
1470 rec->fStrokeJoin = 0;
1471 }
1472
joshualitt20dac882015-07-24 13:16:24 -07001473 rec->fMaskFormat = SkToU8(compute_mask_format(paint));
reed@google.com02b53312011-05-18 19:00:53 +00001474
reedd54d3fc2014-11-13 14:39:58 -08001475 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001476 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001477 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001478 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001479 } else {
robertphillipsfcf78292015-06-19 11:49:52 -07001480 SkPixelGeometry geometry = surfaceProps
1481 ? surfaceProps->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001482 : SkSurfacePropsDefaultPixelGeometry();
1483 switch (geometry) {
1484 case kUnknown_SkPixelGeometry:
1485 // eeek, can't support LCD
1486 rec->fMaskFormat = SkMask::kA8_Format;
1487 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1488 break;
1489 case kRGB_H_SkPixelGeometry:
1490 // our default, do nothing.
1491 break;
1492 case kBGR_H_SkPixelGeometry:
1493 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1494 break;
1495 case kRGB_V_SkPixelGeometry:
1496 flags |= SkScalerContext::kLCD_Vertical_Flag;
1497 break;
1498 case kBGR_V_SkPixelGeometry:
1499 flags |= SkScalerContext::kLCD_Vertical_Flag;
1500 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1501 break;
reed@google.com02b53312011-05-18 19:00:53 +00001502 }
1503 }
1504 }
1505
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001506 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001507 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001508 }
1509 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001510 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001511 }
1512 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001513 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001514 }
reed@google.com830a23e2011-11-10 15:20:49 +00001515 if (paint.isVerticalText()) {
1516 flags |= SkScalerContext::kVertical_Flag;
1517 }
reed@google.com8351aab2012-01-18 17:06:35 +00001518 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1519 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1520 }
reed@google.com02b53312011-05-18 19:00:53 +00001521 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001522
reed@google.comffe49f52011-11-22 19:42:41 +00001523 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001524 rec->setHinting(computeHinting(paint));
1525
joshualitt9e36c1a2015-04-14 12:17:27 -07001526 rec->setLuminanceColor(paint.computeLuminanceColor());
bungeman@google.com532470f2013-01-22 19:25:14 +00001527
robertphillips9fc82752015-06-19 04:46:45 -07001528 //For now always set the paint gamma equal to the device gamma.
1529 //The math in SkMaskGamma can handle them being different,
1530 //but it requires superluminous masks when
1531 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1532 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1533 rec->setPaintGamma(SK_GAMMA_EXPONENT);
bungeman@google.com532470f2013-01-22 19:25:14 +00001534
1535#ifdef SK_GAMMA_CONTRAST
1536 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001537#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001538 /**
1539 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1540 * With lower values small text appears washed out (though correctly so).
1541 * With higher values lcd fringing is worse and the smoothing effect of
1542 * partial coverage is diminished.
1543 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001544 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001545#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001546
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001547 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001548
reed@android.com36a4c2a2009-07-22 19:52:11 +00001549 /* Allow the fonthost to modify our rec before we use it as a key into the
1550 cache. This way if we're asking for something that they will ignore,
1551 they can modify our rec up front, so we don't create duplicate cache
1552 entries.
1553 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001554 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001555
reed@google.com10d2d4d2012-03-01 22:32:51 +00001556 // be sure to call PostMakeRec(rec) before you actually use it!
1557}
1558
1559/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001560 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1561 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1562 * to hold it until the returned pointer is refed or forgotten.
1563 */
1564SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1565
halcanary96fcdcc2015-08-27 07:41:13 -07001566static SkMaskGamma* gLinearMaskGamma = nullptr;
1567static SkMaskGamma* gMaskGamma = nullptr;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001568static SkScalar gContrast = SK_ScalarMin;
1569static SkScalar gPaintGamma = SK_ScalarMin;
1570static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001571/**
1572 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1573 * the returned SkMaskGamma pointer is refed or forgotten.
1574 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001575static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001576 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001577 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
halcanary96fcdcc2015-08-27 07:41:13 -07001578 if (nullptr == gLinearMaskGamma) {
halcanary385fe4d2015-08-26 13:07:48 -07001579 gLinearMaskGamma = new SkMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001580 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001581 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001582 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001583 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1584 SkSafeUnref(gMaskGamma);
halcanary385fe4d2015-08-26 13:07:48 -07001585 gMaskGamma = new SkMaskGamma(contrast, paintGamma, deviceGamma);
bungeman@google.com97efada2012-07-30 20:40:50 +00001586 gContrast = contrast;
1587 gPaintGamma = paintGamma;
1588 gDeviceGamma = deviceGamma;
1589 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001590 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001591}
1592
1593/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001594 * We ensure that the rec is self-consistent and efficient (where possible)
1595 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001596void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001597 /**
1598 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001599 * limits the number of unique entries, and the scaler will only look at
1600 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001601 */
reed@google.comdd43df92012-02-15 14:50:29 +00001602 switch (rec->fMaskFormat) {
reedd54d3fc2014-11-13 14:39:58 -08001603 case SkMask::kLCD16_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001604 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001605 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001606 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001607 break;
1608 }
1609 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001610 // filter down the luminance to a single component, since A8 can't
1611 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001612 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001613 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1614 SkColorGetG(color),
1615 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001616 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001617 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001618 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001619 break;
1620 }
1621 case SkMask::kBW_Format:
1622 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001623 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001624 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001625 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626}
1627
1628#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1629
reed@google.com17fb3872011-05-04 14:31:07 +00001630#ifdef SK_DEBUG
1631 #define TEST_DESC
1632#endif
1633
joshualittfd450792015-03-13 08:38:43 -07001634static void write_out_descriptor(SkDescriptor* desc, const SkScalerContext::Rec& rec,
1635 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1636 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1637 const SkRasterizer* ra, SkWriteBuffer* raBuffer,
1638 size_t descSize) {
1639 desc->init();
1640 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1641
1642 if (pe) {
1643 add_flattenable(desc, kPathEffect_SkDescriptorTag, peBuffer);
1644 }
1645 if (mf) {
1646 add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfBuffer);
1647 }
1648 if (ra) {
1649 add_flattenable(desc, kRasterizer_SkDescriptorTag, raBuffer);
1650 }
1651
1652 desc->computeChecksum();
1653}
1654
1655static size_t fill_out_rec(const SkPaint& paint, SkScalerContext::Rec* rec,
robertphillipsfcf78292015-06-19 11:49:52 -07001656 const SkSurfaceProps* surfaceProps,
joshualittfd450792015-03-13 08:38:43 -07001657 const SkMatrix* deviceMatrix, bool ignoreGamma,
1658 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1659 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1660 const SkRasterizer* ra, SkWriteBuffer* raBuffer) {
robertphillipsfcf78292015-06-19 11:49:52 -07001661 SkScalerContext::MakeRec(paint, surfaceProps, deviceMatrix, rec);
joshualittfd450792015-03-13 08:38:43 -07001662 if (ignoreGamma) {
1663 rec->ignorePreBlend();
1664 }
1665
1666 int entryCount = 1;
1667 size_t descSize = sizeof(*rec);
1668
1669 if (pe) {
1670 peBuffer->writeFlattenable(pe);
1671 descSize += peBuffer->bytesWritten();
1672 entryCount += 1;
1673 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1674 // seems like we could support kLCD as well at this point...
1675 }
1676 if (mf) {
1677 mfBuffer->writeFlattenable(mf);
1678 descSize += mfBuffer->bytesWritten();
1679 entryCount += 1;
1680 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1681 /* Pre-blend is not currently applied to filtered text.
1682 The primary filter is blur, for which contrast makes no sense,
1683 and for which the destination guess error is more visible.
1684 Also, all existing users of blur have calibrated for linear. */
1685 rec->ignorePreBlend();
1686 }
1687 if (ra) {
1688 raBuffer->writeFlattenable(ra);
1689 descSize += raBuffer->bytesWritten();
1690 entryCount += 1;
1691 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1692 }
1693
1694 ///////////////////////////////////////////////////////////////////////////
1695 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
1696 SkScalerContext::PostMakeRec(paint, rec);
1697
1698 descSize += SkDescriptor::ComputeOverhead(entryCount);
1699 return descSize;
1700}
1701
1702#ifdef TEST_DESC
1703static void test_desc(const SkScalerContext::Rec& rec,
1704 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1705 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1706 const SkRasterizer* ra, SkWriteBuffer* raBuffer,
1707 const SkDescriptor* desc, size_t descSize) {
1708 // Check that we completely write the bytes in desc (our key), and that
1709 // there are no uninitialized bytes. If there were, then we would get
1710 // false-misses (or worse, false-hits) in our fontcache.
1711 //
1712 // We do this buy filling 2 others, one with 0s and the other with 1s
1713 // and create those, and then check that all 3 are identical.
1714 SkAutoDescriptor ad1(descSize);
1715 SkAutoDescriptor ad2(descSize);
1716 SkDescriptor* desc1 = ad1.getDesc();
1717 SkDescriptor* desc2 = ad2.getDesc();
1718
1719 memset(desc1, 0x00, descSize);
1720 memset(desc2, 0xFF, descSize);
1721
1722 desc1->init();
1723 desc2->init();
1724 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1725 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1726
1727 if (pe) {
1728 add_flattenable(desc1, kPathEffect_SkDescriptorTag, peBuffer);
1729 add_flattenable(desc2, kPathEffect_SkDescriptorTag, peBuffer);
1730 }
1731 if (mf) {
1732 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, mfBuffer);
1733 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, mfBuffer);
1734 }
1735 if (ra) {
1736 add_flattenable(desc1, kRasterizer_SkDescriptorTag, raBuffer);
1737 add_flattenable(desc2, kRasterizer_SkDescriptorTag, raBuffer);
1738 }
1739
1740 SkASSERT(descSize == desc1->getLength());
1741 SkASSERT(descSize == desc2->getLength());
1742 desc1->computeChecksum();
1743 desc2->computeChecksum();
1744 SkASSERT(!memcmp(desc, desc1, descSize));
1745 SkASSERT(!memcmp(desc, desc2, descSize));
1746}
1747#endif
1748
1749/* see the note on ignoreGamma on descriptorProc */
joshualitt2b6acb42015-04-01 11:30:27 -07001750void SkPaint::getScalerContextDescriptor(SkAutoDescriptor* ad,
robertphillipsfcf78292015-06-19 11:49:52 -07001751 const SkSurfaceProps& surfaceProps,
joshualitt2b6acb42015-04-01 11:30:27 -07001752 const SkMatrix* deviceMatrix, bool ignoreGamma) const {
joshualittfd450792015-03-13 08:38:43 -07001753 SkScalerContext::Rec rec;
1754
1755 SkPathEffect* pe = this->getPathEffect();
1756 SkMaskFilter* mf = this->getMaskFilter();
1757 SkRasterizer* ra = this->getRasterizer();
1758
1759 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
robertphillipsfcf78292015-06-19 11:49:52 -07001760 size_t descSize = fill_out_rec(*this, &rec, &surfaceProps, deviceMatrix, ignoreGamma,
joshualittfd450792015-03-13 08:38:43 -07001761 pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
1762
joshualitt2b6acb42015-04-01 11:30:27 -07001763 ad->reset(descSize);
1764 SkDescriptor* desc = ad->getDesc();
joshualittfd450792015-03-13 08:38:43 -07001765
1766 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
1767
1768 SkASSERT(descSize == desc->getLength());
1769
1770#ifdef TEST_DESC
1771 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
1772#endif
joshualittfd450792015-03-13 08:38:43 -07001773}
1774
reed@google.comffe49f52011-11-22 19:42:41 +00001775/*
1776 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001777 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1778 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001779 */
robertphillipsfcf78292015-06-19 11:49:52 -07001780void SkPaint::descriptorProc(const SkSurfaceProps* surfaceProps,
bungeman@google.com532470f2013-01-22 19:25:14 +00001781 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001782 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001783 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 SkScalerContext::Rec rec;
1785
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 SkPathEffect* pe = this->getPathEffect();
1787 SkMaskFilter* mf = this->getMaskFilter();
1788 SkRasterizer* ra = this->getRasterizer();
1789
joshualittfd450792015-03-13 08:38:43 -07001790 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
robertphillipsfcf78292015-06-19 11:49:52 -07001791 size_t descSize = fill_out_rec(*this, &rec, surfaceProps, deviceMatrix, ignoreGamma,
joshualittfd450792015-03-13 08:38:43 -07001792 pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793
1794 SkAutoDescriptor ad(descSize);
1795 SkDescriptor* desc = ad.getDesc();
1796
joshualittfd450792015-03-13 08:38:43 -07001797 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798
1799 SkASSERT(descSize == desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800
reed@google.com17fb3872011-05-04 14:31:07 +00001801#ifdef TEST_DESC
joshualittfd450792015-03-13 08:38:43 -07001802 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
reed@google.com17fb3872011-05-04 14:31:07 +00001803#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001804
reed@google.com90808e82013-03-19 14:44:54 +00001805 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806}
1807
robertphillipsfcf78292015-06-19 11:49:52 -07001808SkGlyphCache* SkPaint::detachCache(const SkSurfaceProps* surfaceProps,
jvanverth2d2a68c2014-06-10 06:42:56 -07001809 const SkMatrix* deviceMatrix,
1810 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 SkGlyphCache* cache;
robertphillipsfcf78292015-06-19 11:49:52 -07001812 this->descriptorProc(surfaceProps, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 return cache;
1814}
1815
bungeman@google.com97efada2012-07-30 20:40:50 +00001816/**
1817 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1818 */
1819//static
1820SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1821 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001822 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1823 rec.getPaintGamma(),
1824 rec.getDeviceGamma());
1825 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001826}
1827
jvanverth2d2a68c2014-06-10 06:42:56 -07001828size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1829 SkScalar deviceGamma, int* width, int* height) {
1830 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1831 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1832 paintGamma,
1833 deviceGamma);
1834
1835 maskGamma.getGammaTableDimensions(width, height);
1836 size_t size = (*width)*(*height)*sizeof(uint8_t);
1837
1838 return size;
1839}
1840
1841void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1842 void* data) {
1843 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1844 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1845 paintGamma,
1846 deviceGamma);
1847 int width, height;
1848 maskGamma.getGammaTableDimensions(&width, &height);
1849 size_t size = width*height*sizeof(uint8_t);
1850 const uint8_t* gammaTables = maskGamma.getGammaTables();
1851 memcpy(data, gammaTables, size);
1852}
1853
1854
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855///////////////////////////////////////////////////////////////////////////////
1856
1857#include "SkStream.h"
1858
reed@android.comaefd2bc2009-03-30 21:02:14 +00001859static uintptr_t asint(const void* p) {
1860 return reinterpret_cast<uintptr_t>(p);
1861}
1862
1863union Scalar32 {
1864 SkScalar fScalar;
1865 uint32_t f32;
1866};
1867
1868static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1869 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1870 Scalar32 tmp;
1871 tmp.fScalar = value;
1872 *ptr = tmp.f32;
1873 return ptr + 1;
1874}
1875
1876static SkScalar read_scalar(const uint32_t*& ptr) {
1877 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1878 Scalar32 tmp;
1879 tmp.f32 = *ptr++;
1880 return tmp.fScalar;
1881}
1882
1883static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1884 SkASSERT(a == (uint8_t)a);
1885 SkASSERT(b == (uint8_t)b);
1886 SkASSERT(c == (uint8_t)c);
1887 SkASSERT(d == (uint8_t)d);
1888 return (a << 24) | (b << 16) | (c << 8) | d;
1889}
1890
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001891#ifdef SK_DEBUG
1892 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1893 SkASSERT(bitCount > 0 && bitCount <= 32);
1894 uint32_t mask = ~0U;
1895 mask >>= (32 - bitCount);
1896 SkASSERT(0 == (value & ~mask));
1897 }
1898#else
1899 #define ASSERT_FITS_IN(value, bitcount)
1900#endif
1901
reed@android.comaefd2bc2009-03-30 21:02:14 +00001902enum FlatFlags {
mtklein88fd0fb2014-12-01 06:56:38 -08001903 kHasTypeface_FlatFlag = 0x1,
1904 kHasEffects_FlatFlag = 0x2,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001905
mtklein88fd0fb2014-12-01 06:56:38 -08001906 kFlatFlagMask = 0x3,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001907};
1908
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001909enum BitsPerField {
1910 kFlags_BPF = 16,
1911 kHint_BPF = 2,
1912 kAlign_BPF = 2,
1913 kFilter_BPF = 2,
1914 kFlatFlags_BPF = 3,
1915};
1916
1917static inline int BPF_Mask(int bits) {
1918 return (1 << bits) - 1;
1919}
1920
1921static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1922 unsigned filter, unsigned flatFlags) {
1923 ASSERT_FITS_IN(flags, kFlags_BPF);
1924 ASSERT_FITS_IN(hint, kHint_BPF);
1925 ASSERT_FITS_IN(align, kAlign_BPF);
1926 ASSERT_FITS_IN(filter, kFilter_BPF);
1927 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1928
1929 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1930 // add more bits in the future.
1931 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1932}
1933
1934static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1935 paint->setFlags(packed >> 16);
1936 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1937 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
reed93a12152015-03-16 10:08:34 -07001938 paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001939 return (FlatFlags)(packed & kFlatFlagMask);
1940}
1941
reed@android.comaefd2bc2009-03-30 21:02:14 +00001942// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00001943static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00001944 1 * sizeof(SkColor) +
1945 1 * sizeof(uint16_t) +
1946 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001947
1948/* To save space/time, we analyze the paint, and write a truncated version of
1949 it if there are not tricky elements like shaders, etc.
1950 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001951void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001952 uint8_t flatFlags = 0;
1953 if (this->getTypeface()) {
1954 flatFlags |= kHasTypeface_FlatFlag;
1955 }
1956 if (asint(this->getPathEffect()) |
1957 asint(this->getShader()) |
1958 asint(this->getXfermode()) |
1959 asint(this->getMaskFilter()) |
1960 asint(this->getColorFilter()) |
1961 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001962 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001963 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001964 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001965 flatFlags |= kHasEffects_FlatFlag;
1966 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001967
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001968 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1969 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001970
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001971 ptr = write_scalar(ptr, this->getTextSize());
1972 ptr = write_scalar(ptr, this->getTextScaleX());
1973 ptr = write_scalar(ptr, this->getTextSkewX());
1974 ptr = write_scalar(ptr, this->getStrokeWidth());
1975 ptr = write_scalar(ptr, this->getStrokeMiter());
1976 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001977
1978 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
reed93a12152015-03-16 10:08:34 -07001979 this->getFilterQuality(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001980 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1981 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001982
1983 // now we're done with ptr and the (pre)reserved space. If we need to write
1984 // additional fields, use the buffer directly
1985 if (flatFlags & kHasTypeface_FlatFlag) {
1986 buffer.writeTypeface(this->getTypeface());
1987 }
1988 if (flatFlags & kHasEffects_FlatFlag) {
1989 buffer.writeFlattenable(this->getPathEffect());
1990 buffer.writeFlattenable(this->getShader());
1991 buffer.writeFlattenable(this->getXfermode());
1992 buffer.writeFlattenable(this->getMaskFilter());
1993 buffer.writeFlattenable(this->getColorFilter());
1994 buffer.writeFlattenable(this->getRasterizer());
1995 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001996 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00001997
1998 if (fAnnotation) {
1999 buffer.writeBool(true);
2000 fAnnotation->writeToBuffer(buffer);
2001 } else {
2002 buffer.writeBool(false);
2003 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002004 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005}
2006
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002007void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002008 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
ajumaf8aec582016-01-13 13:46:31 -08002009 if (!buffer.validateAvailable(kPODPaintSize)) {
2010 return;
2011 }
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002012 const void* podData = buffer.skip(kPODPaintSize);
2013 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002014
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002015 // the order we read must match the order we wrote in flatten()
2016 this->setTextSize(read_scalar(pod));
2017 this->setTextScaleX(read_scalar(pod));
2018 this->setTextSkewX(read_scalar(pod));
2019 this->setStrokeWidth(read_scalar(pod));
2020 this->setStrokeMiter(read_scalar(pod));
2021 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002022
mtklein88fd0fb2014-12-01 06:56:38 -08002023 unsigned flatFlags = unpack_paint_flags(this, *pod++);
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002024
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002025 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002026 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2027 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2028 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2029 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002030
2031 if (flatFlags & kHasTypeface_FlatFlag) {
2032 this->setTypeface(buffer.readTypeface());
2033 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002034 this->setTypeface(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002035 }
2036
2037 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002038 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2039 SkSafeUnref(this->setShader(buffer.readShader()));
2040 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2041 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2042 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2043 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2044 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2045 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002046
reed@google.com0cd2ac62013-10-14 20:02:44 +00002047 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002048 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00002049 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002050 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002051 this->setPathEffect(nullptr);
2052 this->setShader(nullptr);
2053 this->setXfermode(nullptr);
2054 this->setMaskFilter(nullptr);
2055 this->setColorFilter(nullptr);
2056 this->setRasterizer(nullptr);
2057 this->setLooper(nullptr);
2058 this->setImageFilter(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002059 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002060}
2061
2062///////////////////////////////////////////////////////////////////////////////
2063
reed@google.com82065d62011-02-07 15:30:46 +00002064SkShader* SkPaint::setShader(SkShader* shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065 SkRefCnt_SafeAssign(fShader, shader);
2066 return shader;
2067}
2068
reed@google.com82065d62011-02-07 15:30:46 +00002069SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070 SkRefCnt_SafeAssign(fColorFilter, filter);
2071 return filter;
2072}
2073
reed@google.com82065d62011-02-07 15:30:46 +00002074SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 SkRefCnt_SafeAssign(fXfermode, mode);
2076 return mode;
2077}
2078
reed@android.com0baf1932009-06-24 12:41:42 +00002079SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002080 SkSafeUnref(fXfermode);
2081 fXfermode = SkXfermode::Create(mode);
2082 return fXfermode;
2083}
2084
reed@google.com82065d62011-02-07 15:30:46 +00002085SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 SkRefCnt_SafeAssign(fPathEffect, effect);
2087 return effect;
2088}
2089
reed@google.com82065d62011-02-07 15:30:46 +00002090SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 SkRefCnt_SafeAssign(fMaskFilter, filter);
2092 return filter;
2093}
2094
reed@google.com82065d62011-02-07 15:30:46 +00002095///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096
reed05d90442015-02-12 13:35:52 -08002097bool SkPaint::getFillPath(const SkPath& src, SkPath* dst, const SkRect* cullRect,
2098 SkScalar resScale) const {
2099 SkStrokeRec rec(*this, resScale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100
reed@google.comfd4be262012-05-25 01:04:12 +00002101 const SkPath* srcPtr = &src;
2102 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002103
reed@google.com4bbdeac2013-01-24 21:03:11 +00002104 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002105 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 }
2107
reed@google.comfd4be262012-05-25 01:04:12 +00002108 if (!rec.applyToPath(dst, *srcPtr)) {
2109 if (srcPtr == &tmpPath) {
2110 // If path's were copy-on-write, this trick would not be needed.
2111 // As it is, we want to save making a deep-copy from tmpPath -> dst
2112 // since we know we're just going to delete tmpPath when we return,
2113 // so the swap saves that copy.
2114 dst->swap(tmpPath);
2115 } else {
2116 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 }
2118 }
reed@google.comfd4be262012-05-25 01:04:12 +00002119 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120}
2121
senorblanco0abdf762015-08-20 11:10:41 -07002122bool SkPaint::canComputeFastBounds() const {
2123 if (this->getLooper()) {
2124 return this->getLooper()->canComputeFastBounds(*this);
2125 }
2126 if (this->getImageFilter() && !this->getImageFilter()->canComputeFastBounds()) {
2127 return false;
2128 }
2129 return !this->getRasterizer();
2130}
2131
reed@google.come4f10a72012-05-15 20:47:50 +00002132const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002133 SkRect* storage,
2134 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002136
reed@google.come4f10a72012-05-15 20:47:50 +00002137 const SkRect* src = &origSrc;
2138
reed@google.com9efd9a02012-01-30 15:41:43 +00002139 if (this->getLooper()) {
2140 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002141 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002142 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002144
reed@google.come4f10a72012-05-15 20:47:50 +00002145 SkRect tmpSrc;
2146 if (this->getPathEffect()) {
2147 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2148 src = &tmpSrc;
2149 }
2150
reed@google.coma584aed2012-05-16 14:06:02 +00002151 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002152 // since we're stroked, outset the rect by the radius (and join type)
2153 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2154 if (0 == radius) { // hairline
2155 radius = SK_Scalar1;
2156 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2157 SkScalar scale = this->getStrokeMiter();
2158 if (scale > SK_Scalar1) {
2159 radius = SkScalarMul(radius, scale);
2160 }
2161 }
reed@google.come4f10a72012-05-15 20:47:50 +00002162 storage->set(src->fLeft - radius, src->fTop - radius,
2163 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002164 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002165 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002166 }
2167
reed@google.com9efd9a02012-01-30 15:41:43 +00002168 if (this->getMaskFilter()) {
2169 this->getMaskFilter()->computeFastBounds(*storage, storage);
2170 }
2171
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002172 if (this->getImageFilter()) {
2173 this->getImageFilter()->computeFastBounds(*storage, storage);
2174 }
2175
reed@android.comd252db02009-04-01 18:31:44 +00002176 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177}
2178
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002179#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002180
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002181void SkPaint::toString(SkString* str) const {
2182 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2183
2184 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002185 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002186 SkDynamicMemoryWStream ostream;
2187 typeface->serialize(&ostream);
scroggoa1193e42015-01-21 12:09:53 -08002188 SkAutoTDelete<SkStreamAsset> istream(ostream.detachAsStream());
bungemand71b7572014-09-18 10:55:32 -07002189 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002190
2191 str->append("<dt>Font Family Name:</dt><dd>");
2192 str->append(descriptor.getFamilyName());
2193 str->append("</dd><dt>Font Full Name:</dt><dd>");
2194 str->append(descriptor.getFullName());
2195 str->append("</dd><dt>Font PS Name:</dt><dd>");
2196 str->append(descriptor.getPostscriptName());
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002197 str->append("</dd>");
2198 }
2199
2200 str->append("<dt>TextSize:</dt><dd>");
2201 str->appendScalar(this->getTextSize());
2202 str->append("</dd>");
2203
2204 str->append("<dt>TextScaleX:</dt><dd>");
2205 str->appendScalar(this->getTextScaleX());
2206 str->append("</dd>");
2207
2208 str->append("<dt>TextSkewX:</dt><dd>");
2209 str->appendScalar(this->getTextSkewX());
2210 str->append("</dd>");
2211
2212 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002213 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002214 str->append("<dt>PathEffect:</dt><dd>");
robertphillips42dbfa82015-01-26 06:08:52 -08002215 pathEffect->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002216 str->append("</dd>");
2217 }
2218
2219 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002220 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002221 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002222 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002223 str->append("</dd>");
2224 }
2225
2226 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002227 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002228 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002229 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002230 str->append("</dd>");
2231 }
2232
2233 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002234 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002235 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002236 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002237 str->append("</dd>");
2238 }
2239
2240 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002241 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002242 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002243 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002244 str->append("</dd>");
2245 }
2246
2247 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002248 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002249 str->append("<dt>Rasterizer:</dt><dd>");
2250 str->append("</dd>");
2251 }
2252
2253 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002254 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002255 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002256 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002257 str->append("</dd>");
2258 }
2259
2260 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002261 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002262 str->append("<dt>ImageFilter:</dt><dd>");
robertphillipsf3f5bad2014-12-19 13:49:15 -08002263 imageFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002264 str->append("</dd>");
2265 }
2266
2267 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002268 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002269 str->append("<dt>Annotation:</dt><dd>");
2270 str->append("</dd>");
2271 }
2272
2273 str->append("<dt>Color:</dt><dd>0x");
2274 SkColor color = this->getColor();
2275 str->appendHex(color);
2276 str->append("</dd>");
2277
2278 str->append("<dt>Stroke Width:</dt><dd>");
2279 str->appendScalar(this->getStrokeWidth());
2280 str->append("</dd>");
2281
2282 str->append("<dt>Stroke Miter:</dt><dd>");
2283 str->appendScalar(this->getStrokeMiter());
2284 str->append("</dd>");
2285
2286 str->append("<dt>Flags:</dt><dd>(");
2287 if (this->getFlags()) {
2288 bool needSeparator = false;
2289 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002290 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2291 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2292 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2293 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2294 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2295 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2296 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2297 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2298 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2299 "EmbeddedBitmapText", &needSeparator);
2300 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2301 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2302 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2303 "GenA8FromLCD", &needSeparator);
2304 } else {
2305 str->append("None");
2306 }
2307 str->append(")</dd>");
2308
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002309 str->append("<dt>FilterLevel:</dt><dd>");
reed93a12152015-03-16 10:08:34 -07002310 static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High" };
2311 str->append(gFilterQualityStrings[this->getFilterQuality()]);
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002312 str->append("</dd>");
2313
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002314 str->append("<dt>TextAlign:</dt><dd>");
2315 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2316 str->append(gTextAlignStrings[this->getTextAlign()]);
2317 str->append("</dd>");
2318
2319 str->append("<dt>CapType:</dt><dd>");
2320 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2321 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2322 str->append("</dd>");
2323
2324 str->append("<dt>JoinType:</dt><dd>");
2325 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2326 str->append(gJoinStrings[this->getStrokeJoin()]);
2327 str->append("</dd>");
2328
2329 str->append("<dt>Style:</dt><dd>");
2330 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2331 str->append(gStyleStrings[this->getStyle()]);
2332 str->append("</dd>");
2333
2334 str->append("<dt>TextEncoding:</dt><dd>");
2335 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2336 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2337 str->append("</dd>");
2338
2339 str->append("<dt>Hinting:</dt><dd>");
2340 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2341 str->append(gHintingStrings[this->getHinting()]);
2342 str->append("</dd>");
2343
2344 str->append("</dd></dl></dl>");
2345}
2346#endif
2347
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002348///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002350static bool has_thick_frame(const SkPaint& paint) {
2351 return paint.getStrokeWidth() > 0 &&
2352 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353}
2354
robertphillipsfcf78292015-06-19 11:49:52 -07002355SkTextToPathIter::SkTextToPathIter(const char text[], size_t length,
2356 const SkPaint& paint,
2357 bool applyStrokeAndPathEffects)
2358 : fPaint(paint) {
reed9e96aa02014-10-03 12:44:37 -07002359 fGlyphCacheProc = paint.getMeasureCacheProc(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360
djsollen@google.com166e6532012-03-20 14:24:38 +00002361 fPaint.setLinearText(true);
halcanary96fcdcc2015-08-27 07:41:13 -07002362 fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363
halcanary96fcdcc2015-08-27 07:41:13 -07002364 if (fPaint.getPathEffect() == nullptr && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002366 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367
djsollen@google.com166e6532012-03-20 14:24:38 +00002368 // can't use our canonical size if we need to apply patheffects
halcanary96fcdcc2015-08-27 07:41:13 -07002369 if (fPaint.getPathEffect() == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2371 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002372 if (has_thick_frame(fPaint)) {
reed80ea19c2015-05-12 10:37:34 -07002373 fPaint.setStrokeWidth(fPaint.getStrokeWidth() / fScale);
djsollen@google.com166e6532012-03-20 14:24:38 +00002374 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002375 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002377 }
reed@google.com72cf4922011-01-04 19:58:20 +00002378
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002379 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380 fPaint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07002381 fPaint.setPathEffect(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 }
2383
halcanary96fcdcc2015-08-27 07:41:13 -07002384 fCache = fPaint.detachCache(nullptr, nullptr, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385
2386 SkPaint::Style style = SkPaint::kFill_Style;
halcanary96fcdcc2015-08-27 07:41:13 -07002387 SkPathEffect* pe = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002389 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 style = paint.getStyle(); // restore
2391 pe = paint.getPathEffect(); // restore
2392 }
2393 fPaint.setStyle(style);
2394 fPaint.setPathEffect(pe);
2395 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2396
2397 // now compute fXOffset if needed
2398
2399 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002400 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002402 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
halcanary96fcdcc2015-08-27 07:41:13 -07002403 &count, nullptr), fScale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002404 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 xOffset = -width;
2408 }
2409 fXPos = xOffset;
2410 fPrevAdvance = 0;
2411
2412 fText = text;
2413 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002414
reed@google.com44da42e2011-11-10 20:04:47 +00002415 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416}
2417
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002418SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419 SkGlyphCache::AttachCache(fCache);
2420}
2421
reed@google.com7b4531f2012-08-07 15:53:00 +00002422bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2423 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002424 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2425
2426 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002427 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002429 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002430 if (path) {
2431 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002432 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002433 } else {
2434 if (path) {
halcanary96fcdcc2015-08-27 07:41:13 -07002435 *path = nullptr;
reed@google.com7b4531f2012-08-07 15:53:00 +00002436 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002438 if (xpos) {
2439 *xpos = fXPos;
2440 }
2441 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002443 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002445
2446///////////////////////////////////////////////////////////////////////////////
2447
reedf539b8c2014-11-11 12:51:33 -08002448// return true if the filter exists, and may affect alpha
2449static bool affects_alpha(const SkColorFilter* cf) {
2450 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2451}
2452
2453// return true if the filter exists, and may affect alpha
2454static bool affects_alpha(const SkImageFilter* imf) {
2455 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2456 // ala colorfilters
halcanary96fcdcc2015-08-27 07:41:13 -07002457 return imf != nullptr;
reedf539b8c2014-11-11 12:51:33 -08002458}
2459
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002460bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002461 if (fLooper) {
2462 return false;
2463 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002464 SkXfermode::Mode mode;
2465 if (SkXfermode::AsMode(fXfermode, &mode)) {
2466 switch (mode) {
2467 case SkXfermode::kSrcOver_Mode:
2468 case SkXfermode::kSrcATop_Mode:
2469 case SkXfermode::kDstOut_Mode:
2470 case SkXfermode::kDstOver_Mode:
2471 case SkXfermode::kPlus_Mode:
reedf539b8c2014-11-11 12:51:33 -08002472 if (0 == this->getAlpha()) {
2473 return !affects_alpha(fColorFilter) && !affects_alpha(fImageFilter);
2474 }
2475 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002476 case SkXfermode::kDst_Mode:
2477 return true;
2478 default:
2479 break;
2480 }
2481 }
2482 return false;
2483}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002484
mtkleinfb1fe4f2014-10-07 09:26:10 -07002485uint32_t SkPaint::getHash() const {
2486 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2487 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
bungeman99fe8222015-08-20 07:57:51 -07002488 static_assert(offsetof(SkPaint, fBitfields) == 10 * sizeof(void*) + 6 * sizeof(uint32_t),
2489 "SkPaint_notPackedTightly");
mtkleinfb1fe4f2014-10-07 09:26:10 -07002490 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(this),
2491 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
2492}