blob: f3bf01bfafc5ca30e0507190a8ffee37f8a5efaa [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000012#include "SkData.h"
bungeman@google.com532470f2013-01-22 19:25:14 +000013#include "SkDeviceProperties.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000014#include "SkFontDescriptor.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkFontHost.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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000020#include "SkReadBuffer.h"
21#include "SkWriteBuffer.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000022#include "SkPaintDefaults.h"
commit-bot@chromium.org97f81672013-09-26 15:16:12 +000023#include "SkPaintOptionsAndroid.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"
35#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000036
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000037enum {
38 kColor_DirtyBit = 1 << 0,
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +000039 kTextSize_DirtyBit = 1 << 1,
40 kTextScaleX_DirtyBit = 1 << 2,
41 kTextSkewX_DirtyBit = 1 << 3,
42 kStrokeWidth_DirtyBit = 1 << 4,
43 kStrokeMiter_DirtyBit = 1 << 5,
44
45 kPOD_DirtyBitMask = 63,
46
47 kPathEffect_DirtyBit = 1 << 6,
48 kShader_DirtyBit = 1 << 7,
49 kXfermode_DirtyBit = 1 << 8,
50 kMaskFilter_DirtyBit = 1 << 9,
51 kColorFilter_DirtyBit = 1 << 10,
52 kRasterizer_DirtyBit = 1 << 11,
53 kLooper_DirtyBit = 1 << 12,
54 kImageFilter_DirtyBit = 1 << 13,
55 kTypeface_DirtyBit = 1 << 14,
56 kAnnotation_DirtyBit = 1 << 15,
57 kPaintOptionsAndroid_DirtyBit = 1 << 16,
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000058};
reed@android.com8a1c16f2008-12-17 15:59:43 +000059
reed@google.coma3237872011-07-05 19:20:48 +000060// define this to get a printf for out-of-range parameter in setters
61// e.g. setTextSize(-1)
62//#define SK_REPORT_API_RANGE_CHECK
63
djsollen@google.com56c69772011-11-08 19:00:26 +000064#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000065#define GEN_ID_INC fGenerationID++
66#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
67#else
68#define GEN_ID_INC
69#define GEN_ID_INC_EVAL(expression)
70#endif
71
reed@android.coma3122b92009-08-13 20:38:25 +000072SkPaint::SkPaint() {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000073 fTypeface = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 fPathEffect = NULL;
75 fShader = NULL;
76 fXfermode = NULL;
77 fMaskFilter = NULL;
78 fColorFilter = NULL;
79 fRasterizer = NULL;
80 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000081 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000082 fAnnotation = NULL;
reed@android.coma3122b92009-08-13 20:38:25 +000083
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000084 fTextSize = SkPaintDefaults_TextSize;
85 fTextScaleX = SK_Scalar1;
86 fTextSkewX = 0;
87 fColor = SK_ColorBLACK;
88 fWidth = 0;
89 fMiterLimit = SkPaintDefaults_MiterLimit;
90
91 // Zero all bitfields, then set some non-zero defaults.
92 fBitfields = 0;
93 fFlags = SkPaintDefaults_Flags;
94 fCapType = kDefault_Cap;
95 fJoinType = kDefault_Join;
96 fTextAlign = kLeft_Align;
97 fStyle = kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 fTextEncoding = kUTF8_TextEncoding;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000099 fHinting = SkPaintDefaults_Hinting;
100
101 fDirtyBits = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +0000102#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000103 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000104 fGenerationID = 0;
105#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106}
107
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000108SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.org40f6e3a2014-05-02 21:24:22 +0000109 // Diagnoistic. May remove later. See crbug.com/364224
110 if (NULL == &src) {
111 sk_throw();
112 }
113
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000114#define COPY(field) field = src.field
115#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000117 REF_COPY(fTypeface);
118 REF_COPY(fPathEffect);
119 REF_COPY(fShader);
120 REF_COPY(fXfermode);
121 REF_COPY(fMaskFilter);
122 REF_COPY(fColorFilter);
123 REF_COPY(fRasterizer);
124 REF_COPY(fLooper);
125 REF_COPY(fImageFilter);
126 REF_COPY(fAnnotation);
127
128 COPY(fTextSize);
129 COPY(fTextScaleX);
130 COPY(fTextSkewX);
131 COPY(fColor);
132 COPY(fWidth);
133 COPY(fMiterLimit);
134 COPY(fBitfields);
135 COPY(fDirtyBits);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000136
137#ifdef SK_BUILD_FOR_ANDROID
138 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000139 COPY(fGenerationID);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000140#endif
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000141
142#undef COPY
143#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144}
145
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000146SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000147 SkSafeUnref(fTypeface);
148 SkSafeUnref(fPathEffect);
149 SkSafeUnref(fShader);
150 SkSafeUnref(fXfermode);
151 SkSafeUnref(fMaskFilter);
152 SkSafeUnref(fColorFilter);
153 SkSafeUnref(fRasterizer);
154 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000155 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000156 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157}
158
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000159SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000160 if (this == &src) {
161 return *this;
162 }
163
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000164#define COPY(field) field = src.field
165#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 SkASSERT(&src);
168
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000169 REF_COPY(fTypeface);
170 REF_COPY(fPathEffect);
171 REF_COPY(fShader);
172 REF_COPY(fXfermode);
173 REF_COPY(fMaskFilter);
174 REF_COPY(fColorFilter);
175 REF_COPY(fRasterizer);
176 REF_COPY(fLooper);
177 REF_COPY(fImageFilter);
178 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000180 COPY(fTextSize);
181 COPY(fTextScaleX);
182 COPY(fTextSkewX);
183 COPY(fColor);
184 COPY(fWidth);
185 COPY(fMiterLimit);
186 COPY(fBitfields);
187 COPY(fDirtyBits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
djsollen@google.com56c69772011-11-08 19:00:26 +0000189#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000190 fPaintOptionsAndroid.~SkPaintOptionsAndroid();
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000191 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000192 ++fGenerationID;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000193#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
195 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000196
197#undef COPY
198#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199}
200
robertphillips@google.comb2657412013-08-07 22:36:29 +0000201bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000202#define EQUAL(field) (a.field == b.field)
203 // Don't check fGenerationID or fDirtyBits, which can be different for logically equal paints.
204 return EQUAL(fTypeface)
205 && EQUAL(fPathEffect)
206 && EQUAL(fShader)
207 && EQUAL(fXfermode)
208 && EQUAL(fMaskFilter)
209 && EQUAL(fColorFilter)
210 && EQUAL(fRasterizer)
211 && EQUAL(fLooper)
212 && EQUAL(fImageFilter)
213 && EQUAL(fAnnotation)
214 && EQUAL(fTextSize)
215 && EQUAL(fTextScaleX)
216 && EQUAL(fTextSkewX)
217 && EQUAL(fColor)
218 && EQUAL(fWidth)
219 && EQUAL(fMiterLimit)
220 && EQUAL(fBitfields)
robertphillips@google.comb2657412013-08-07 22:36:29 +0000221#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000222 && EQUAL(fPaintOptionsAndroid)
robertphillips@google.comb2657412013-08-07 22:36:29 +0000223#endif
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000224 ;
225#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000226}
227
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000228void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 SkPaint init;
230
djsollen@google.com56c69772011-11-08 19:00:26 +0000231#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000232 uint32_t oldGenerationID = fGenerationID;
233#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000235#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000236 fGenerationID = oldGenerationID + 1;
237#endif
238}
239
djsollen@google.com56c69772011-11-08 19:00:26 +0000240#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000241uint32_t SkPaint::getGenerationID() const {
242 return fGenerationID;
243}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000244
245void SkPaint::setGenerationID(uint32_t generationID) {
246 fGenerationID = generationID;
247}
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000248
djsollen@google.com60abb072012-02-15 18:49:15 +0000249unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
bungeman@google.comba3284e2013-01-22 19:49:33 +0000250 SkAutoGlyphCache autoCache(*this, NULL, NULL);
djsollen@google.com60abb072012-02-15 18:49:15 +0000251 SkGlyphCache* cache = autoCache.getCache();
252 return cache->getBaseGlyphCount(text);
253}
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000254
255void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) {
256 if (options != fPaintOptionsAndroid) {
257 fPaintOptionsAndroid = options;
258 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000259 fDirtyBits |= kPaintOptionsAndroid_DirtyBit;
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000260 }
261}
djsollen@google.com60abb072012-02-15 18:49:15 +0000262#endif
263
reed@google.comc9683152013-07-18 13:47:01 +0000264void SkPaint::setFilterLevel(FilterLevel level) {
commit-bot@chromium.org85faf502014-04-16 12:58:02 +0000265 GEN_ID_INC_EVAL((unsigned) level != fFilterLevel);
266 fFilterLevel = level;
reed@google.comc9683152013-07-18 13:47:01 +0000267}
268
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000269void SkPaint::setHinting(Hinting hintingLevel) {
270 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
271 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272}
273
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000274void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000275 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fFlags = flags;
277}
278
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000279void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
281}
282
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000283void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
285}
286
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000287void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
289}
290
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000291void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000292 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
293}
294
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000295void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000296 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
297}
298
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000299void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000300 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
301}
302
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
305}
306
reed@google.com830a23e2011-11-10 15:20:49 +0000307void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000308 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
309}
310
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000311void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
313}
314
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000315void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
317}
318
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000319void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
321}
322
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
325}
326
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000327void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) {
328 this->setFlags(SkSetClearMask(fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag));
329}
330
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331void SkPaint::setStyle(Style style) {
332 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000333 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000335 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000336#ifdef SK_REPORT_API_RANGE_CHECK
337 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
338#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340}
341
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000342void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000343 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 fColor = color;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000345 fDirtyBits |= kColor_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346}
347
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000349 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
350 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351}
352
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000353void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000354 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355}
356
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000357void SkPaint::setStrokeWidth(SkScalar width) {
358 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000359 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fWidth = width;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000361 fDirtyBits |= kStrokeWidth_DirtyBit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000362 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000363#ifdef SK_REPORT_API_RANGE_CHECK
364 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
365#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000366 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367}
368
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000369void SkPaint::setStrokeMiter(SkScalar limit) {
370 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000371 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 fMiterLimit = limit;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000373 fDirtyBits |= kStrokeMiter_DirtyBit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000374 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000375#ifdef SK_REPORT_API_RANGE_CHECK
376 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
377#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000378 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379}
380
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000381void SkPaint::setStrokeCap(Cap ct) {
382 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000383 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000385 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000386#ifdef SK_REPORT_API_RANGE_CHECK
387 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
388#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000389 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390}
391
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000392void SkPaint::setStrokeJoin(Join jt) {
393 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000394 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000396 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000397#ifdef SK_REPORT_API_RANGE_CHECK
398 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
399#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000400 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401}
402
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000403///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000405void SkPaint::setTextAlign(Align align) {
406 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000407 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000409 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000410#ifdef SK_REPORT_API_RANGE_CHECK
411 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
412#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000413 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414}
415
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000416void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000417 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000418 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 fTextSize = ts;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000420 fDirtyBits |= kTextSize_DirtyBit;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000421 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000422#ifdef SK_REPORT_API_RANGE_CHECK
423 SkDebugf("SkPaint::setTextSize() called with negative value\n");
424#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426}
427
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000428void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000429 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 fTextScaleX = scaleX;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000431 fDirtyBits |= kTextScaleX_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432}
433
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000434void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000435 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 fTextSkewX = skewX;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000437 fDirtyBits |= kTextSkewX_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438}
439
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000440void SkPaint::setTextEncoding(TextEncoding encoding) {
441 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000442 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000444 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000445#ifdef SK_REPORT_API_RANGE_CHECK
446 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
447#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449}
450
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000451///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000453// Returns dst with the given bitmask enabled or disabled, depending on value.
454inline static uint32_t set_mask(uint32_t dst, uint32_t bitmask, bool value) {
455 return value ? (dst | bitmask) : (dst & ~bitmask);
456}
457
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000458SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000460 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000461 fDirtyBits = set_mask(fDirtyBits, kTypeface_DirtyBit, font != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 return font;
463}
464
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000465SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000467 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000468 fDirtyBits = set_mask(fDirtyBits, kRasterizer_DirtyBit, r != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 return r;
470}
471
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000472SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000474 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000475 fDirtyBits = set_mask(fDirtyBits, kLooper_DirtyBit, looper != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 return looper;
477}
478
reed@google.com15356a62011-11-03 19:29:08 +0000479SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
480 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
481 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000482 fDirtyBits = set_mask(fDirtyBits, kImageFilter_DirtyBit, imageFilter != NULL);
reed@google.com15356a62011-11-03 19:29:08 +0000483 return imageFilter;
484}
485
reed@google.comb0a34d82012-07-11 19:57:55 +0000486SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
487 SkRefCnt_SafeAssign(fAnnotation, annotation);
488 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000489 fDirtyBits = set_mask(fDirtyBits, kAnnotation_DirtyBit, annotation != NULL);
reed@google.comb0a34d82012-07-11 19:57:55 +0000490 return annotation;
491}
492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493///////////////////////////////////////////////////////////////////////////////
494
reed@google.comed43dff2013-06-04 16:56:27 +0000495static SkScalar mag2(SkScalar x, SkScalar y) {
496 return x * x + y * y;
497}
498
499static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
500 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
501 ||
502 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
503}
504
505bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
506 SkASSERT(!ctm.hasPerspective());
507 SkASSERT(!textM.hasPerspective());
508
509 SkMatrix matrix;
510 matrix.setConcat(ctm, textM);
511 return tooBig(matrix, MaxCacheSize2());
512}
513
514bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const {
515 SkMatrix textM;
516 return TooBigToUseCache(ctm, *this->setTextMatrix(&textM));
517}
518
519bool SkPaint::tooBigToUseCache() const {
520 SkMatrix textM;
521 return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2());
522}
523
524///////////////////////////////////////////////////////////////////////////////
525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526#include "SkGlyphCache.h"
527#include "SkUtils.h"
528
reed@google.com90808e82013-03-19 14:44:54 +0000529static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
530 void* context) {
531 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
535 uint16_t glyphs[]) const {
536 if (byteLength == 0) {
537 return 0;
538 }
reed@google.com72cf4922011-01-04 19:58:20 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 SkASSERT(textData != NULL);
541
542 if (NULL == glyphs) {
543 switch (this->getTextEncoding()) {
544 case kUTF8_TextEncoding:
545 return SkUTF8_CountUnichars((const char*)textData, byteLength);
546 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000547 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000548 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000549 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000551 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000553 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 }
555 return 0;
556 }
reed@google.com72cf4922011-01-04 19:58:20 +0000557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 // handle this encoding before the setup for the glyphcache
561 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
562 // we want to ignore the low bit of byteLength
563 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000564 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 }
reed@google.com72cf4922011-01-04 19:58:20 +0000566
bungeman@google.com532470f2013-01-22 19:25:14 +0000567 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 SkGlyphCache* cache = autoCache.getCache();
569
570 const char* text = (const char*)textData;
571 const char* stop = text + byteLength;
572 uint16_t* gptr = glyphs;
573
574 switch (this->getTextEncoding()) {
575 case SkPaint::kUTF8_TextEncoding:
576 while (text < stop) {
577 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
578 }
579 break;
580 case SkPaint::kUTF16_TextEncoding: {
581 const uint16_t* text16 = (const uint16_t*)text;
582 const uint16_t* stop16 = (const uint16_t*)stop;
583 while (text16 < stop16) {
584 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
585 }
586 break;
587 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000588 case kUTF32_TextEncoding: {
589 const int32_t* text32 = (const int32_t*)text;
590 const int32_t* stop32 = (const int32_t*)stop;
591 while (text32 < stop32) {
592 *gptr++ = cache->unicharToGlyph(*text32++);
593 }
594 break;
595 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000597 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000599 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600}
601
reed@android.coma5dcaf62010-02-05 17:12:32 +0000602bool SkPaint::containsText(const void* textData, size_t byteLength) const {
603 if (0 == byteLength) {
604 return true;
605 }
reed@google.com72cf4922011-01-04 19:58:20 +0000606
reed@android.coma5dcaf62010-02-05 17:12:32 +0000607 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000608
reed@android.coma5dcaf62010-02-05 17:12:32 +0000609 // handle this encoding before the setup for the glyphcache
610 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
611 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
612 size_t count = byteLength >> 1;
613 for (size_t i = 0; i < count; i++) {
614 if (0 == glyphID[i]) {
615 return false;
616 }
617 }
618 return true;
619 }
reed@google.com72cf4922011-01-04 19:58:20 +0000620
bungeman@google.com532470f2013-01-22 19:25:14 +0000621 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000622 SkGlyphCache* cache = autoCache.getCache();
623
624 switch (this->getTextEncoding()) {
625 case SkPaint::kUTF8_TextEncoding: {
626 const char* text = static_cast<const char*>(textData);
627 const char* stop = text + byteLength;
628 while (text < stop) {
629 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
630 return false;
631 }
632 }
633 break;
634 }
635 case SkPaint::kUTF16_TextEncoding: {
636 const uint16_t* text = static_cast<const uint16_t*>(textData);
637 const uint16_t* stop = text + (byteLength >> 1);
638 while (text < stop) {
639 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
640 return false;
641 }
642 }
643 break;
644 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000645 case SkPaint::kUTF32_TextEncoding: {
646 const int32_t* text = static_cast<const int32_t*>(textData);
647 const int32_t* stop = text + (byteLength >> 2);
648 while (text < stop) {
649 if (0 == cache->unicharToGlyph(*text++)) {
650 return false;
651 }
652 }
653 break;
654 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000655 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000656 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000657 return false;
658 }
659 return true;
660}
661
reed@android.com9d3a9852010-01-08 14:07:42 +0000662void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
663 SkUnichar textData[]) const {
664 if (count <= 0) {
665 return;
666 }
667
668 SkASSERT(glyphs != NULL);
669 SkASSERT(textData != NULL);
670
bungeman@google.com532470f2013-01-22 19:25:14 +0000671 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000672 SkGlyphCache* cache = autoCache.getCache();
673
674 for (int index = 0; index < count; index++) {
675 textData[index] = cache->glyphToUnichar(glyphs[index]);
676 }
677}
678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679///////////////////////////////////////////////////////////////////////////////
680
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000681static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
682 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 SkASSERT(cache != NULL);
684 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
687}
688
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000689static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
690 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 SkASSERT(cache != NULL);
692 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
695}
696
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000697static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
698 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 SkASSERT(cache != NULL);
700 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000701
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
703}
704
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000705static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
706 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 SkASSERT(cache != NULL);
708 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
711}
712
reed@google.com68bc6f72012-03-14 19:41:55 +0000713static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
714 const char** text) {
715 SkASSERT(cache != NULL);
716 SkASSERT(text != NULL);
717
718 const int32_t* ptr = *(const int32_t**)text;
719 SkUnichar uni = *ptr++;
720 *text = (const char*)ptr;
721 return cache->getUnicharMetrics(uni);
722}
723
724static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
725 const char** text) {
726 SkASSERT(cache != NULL);
727 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000728
reed@google.com68bc6f72012-03-14 19:41:55 +0000729 const int32_t* ptr = *(const int32_t**)text;
730 SkUnichar uni = *--ptr;
731 *text = (const char*)ptr;
732 return cache->getUnicharMetrics(uni);
733}
734
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
736 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 SkASSERT(cache != NULL);
738 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 const uint16_t* ptr = *(const uint16_t**)text;
741 unsigned glyphID = *ptr;
742 ptr += 1;
743 *text = (const char*)ptr;
744 return cache->getGlyphIDMetrics(glyphID);
745}
746
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000747static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
748 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 SkASSERT(cache != NULL);
750 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000751
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 const uint16_t* ptr = *(const uint16_t**)text;
753 ptr -= 1;
754 unsigned glyphID = *ptr;
755 *text = (const char*)ptr;
756 return cache->getGlyphIDMetrics(glyphID);
757}
758
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000759static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
760 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 SkASSERT(cache != NULL);
762 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
765}
766
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000767static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
768 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 SkASSERT(cache != NULL);
770 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
773}
774
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000775static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
776 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 SkASSERT(cache != NULL);
778 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
781}
782
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000783static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
784 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 SkASSERT(cache != NULL);
786 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
789}
790
reed@google.com68bc6f72012-03-14 19:41:55 +0000791static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
792 const char** text) {
793 SkASSERT(cache != NULL);
794 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000795
reed@google.com68bc6f72012-03-14 19:41:55 +0000796 const int32_t* ptr = *(const int32_t**)text;
797 SkUnichar uni = *ptr++;
798 *text = (const char*)ptr;
799 return cache->getUnicharAdvance(uni);
800}
801
802static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
803 const char** text) {
804 SkASSERT(cache != NULL);
805 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000806
reed@google.com68bc6f72012-03-14 19:41:55 +0000807 const int32_t* ptr = *(const int32_t**)text;
808 SkUnichar uni = *--ptr;
809 *text = (const char*)ptr;
810 return cache->getUnicharAdvance(uni);
811}
812
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000813static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
814 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 SkASSERT(cache != NULL);
816 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 const uint16_t* ptr = *(const uint16_t**)text;
819 unsigned glyphID = *ptr;
820 ptr += 1;
821 *text = (const char*)ptr;
822 return cache->getGlyphIDAdvance(glyphID);
823}
824
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000825static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
826 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 SkASSERT(cache != NULL);
828 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 const uint16_t* ptr = *(const uint16_t**)text;
831 ptr -= 1;
832 unsigned glyphID = *ptr;
833 *text = (const char*)ptr;
834 return cache->getGlyphIDAdvance(glyphID);
835}
836
837SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000838 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
840 sk_getMetrics_utf8_next,
841 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000842 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000844
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 sk_getMetrics_utf8_prev,
846 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000847 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 sk_getAdvance_utf8_next,
851 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000852 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 sk_getAdvance_utf8_prev,
856 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000857 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 sk_getAdvance_glyph_prev
859 };
reed@google.com72cf4922011-01-04 19:58:20 +0000860
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 unsigned index = this->getTextEncoding();
862
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000863 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000864 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000865 }
866 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000867 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000868 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869
870 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
871 return gMeasureCacheProcs[index];
872}
873
874///////////////////////////////////////////////////////////////////////////////
875
876static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000877 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 SkASSERT(cache != NULL);
879 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
882}
883
884static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000885 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 SkASSERT(cache != NULL);
887 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000888
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
890}
891
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000892static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
893 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 SkASSERT(cache != NULL);
895 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000896
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
898}
899
900static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000901 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 SkASSERT(cache != NULL);
903 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000904
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
906 x, y);
907}
908
reed@google.com68bc6f72012-03-14 19:41:55 +0000909static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
910 const char** text, SkFixed, SkFixed) {
911 SkASSERT(cache != NULL);
912 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000913
reed@google.com68bc6f72012-03-14 19:41:55 +0000914 const int32_t* ptr = *(const int32_t**)text;
915 SkUnichar uni = *ptr++;
916 *text = (const char*)ptr;
917 return cache->getUnicharMetrics(uni);
918}
919
920static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
921 const char** text, SkFixed x, SkFixed y) {
922 SkASSERT(cache != NULL);
923 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000924
reed@google.com68bc6f72012-03-14 19:41:55 +0000925 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000926 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000927 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000928 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000929}
930
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000931static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
932 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 SkASSERT(cache != NULL);
934 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000935
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 const uint16_t* ptr = *(const uint16_t**)text;
937 unsigned glyphID = *ptr;
938 ptr += 1;
939 *text = (const char*)ptr;
940 return cache->getGlyphIDMetrics(glyphID);
941}
942
943static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000944 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 SkASSERT(cache != NULL);
946 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000947
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 const uint16_t* ptr = *(const uint16_t**)text;
949 unsigned glyphID = *ptr;
950 ptr += 1;
951 *text = (const char*)ptr;
952 return cache->getGlyphIDMetrics(glyphID, x, y);
953}
954
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000955SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 static const SkDrawCacheProc gDrawCacheProcs[] = {
957 sk_getMetrics_utf8_00,
958 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000959 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000961
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 sk_getMetrics_utf8_xy,
963 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000964 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 sk_getMetrics_glyph_xy
966 };
reed@google.com72cf4922011-01-04 19:58:20 +0000967
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000969 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000970 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000971 }
reed@google.com72cf4922011-01-04 19:58:20 +0000972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
974 return gDrawCacheProcs[index];
975}
976
977///////////////////////////////////////////////////////////////////////////////
978
reed@google.comed43dff2013-06-04 16:56:27 +0000979#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
980SkPaint::kDevKernText_Flag | \
981SkPaint::kLinearText_Flag | \
982SkPaint::kLCDRenderText_Flag | \
983SkPaint::kEmbeddedBitmapText_Flag | \
984SkPaint::kAutoHinting_Flag | \
985SkPaint::kGenA8FromLCD_Flag )
986
987SkScalar SkPaint::setupForAsPaths() {
988 uint32_t flags = this->getFlags();
989 // clear the flags we don't care about
990 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
991 // set the flags we do care about
992 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000993
reed@google.comed43dff2013-06-04 16:56:27 +0000994 this->setFlags(flags);
995 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000996
reed@google.comed43dff2013-06-04 16:56:27 +0000997 SkScalar textSize = fTextSize;
998 this->setTextSize(kCanonicalTextSizeForPaths);
999 return textSize / kCanonicalTextSizeForPaths;
1000}
1001
1002class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003public:
reed@google.comed43dff2013-06-04 16:56:27 +00001004 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
1005 if (paint.isLinearText() || paint.tooBigToUseCache()) {
1006 SkPaint* p = fLazy.set(paint);
1007 fScale = p->setupForAsPaths();
1008 fPaint = p;
1009 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001011
reed@google.comed43dff2013-06-04 16:56:27 +00001012 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001013
reed@google.comed43dff2013-06-04 16:56:27 +00001014 /**
1015 * Returns 0 if the paint was unmodified, or the scale factor need to
1016 * the original textSize
1017 */
1018 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +00001019
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020private:
reed@google.comed43dff2013-06-04 16:56:27 +00001021 const SkPaint* fPaint;
1022 SkScalar fScale;
1023 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024};
1025
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001026static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 bounds->set(SkIntToScalar(g.fLeft),
1028 SkIntToScalar(g.fTop),
1029 SkIntToScalar(g.fLeft + g.fWidth),
1030 SkIntToScalar(g.fTop + g.fHeight));
1031}
1032
reed@android.come88f5512010-03-19 14:42:28 +00001033// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
1034// we don't overflow along the way
1035typedef int64_t Sk48Dot16;
1036
reed@google.com8f4d2302013-12-17 16:44:46 +00001037static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
1038 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
1039}
reed@android.come88f5512010-03-19 14:42:28 +00001040
reed@google.com44da42e2011-11-10 20:04:47 +00001041static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +00001042 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 bounds->join(SkIntToScalar(g.fLeft) + sx,
1044 SkIntToScalar(g.fTop),
1045 SkIntToScalar(g.fLeft + g.fWidth) + sx,
1046 SkIntToScalar(g.fTop + g.fHeight));
1047}
1048
reed@google.com44da42e2011-11-10 20:04:47 +00001049static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
1050 SkScalar sy = Sk48Dot16ToScalar(dy);
1051 bounds->join(SkIntToScalar(g.fLeft),
1052 SkIntToScalar(g.fTop) + sy,
1053 SkIntToScalar(g.fLeft + g.fWidth),
1054 SkIntToScalar(g.fTop + g.fHeight) + sy);
1055}
1056
1057typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
1058
1059// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
1060static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
1061 SkASSERT(0 == xyIndex || 1 == xyIndex);
1062 return (&glyph.fAdvanceX)[xyIndex];
1063}
1064
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065SkScalar SkPaint::measure_text(SkGlyphCache* cache,
1066 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001067 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001069 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001071 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001073 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 return 0;
1075 }
1076
1077 SkMeasureCacheProc glyphCacheProc;
1078 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1079 NULL != bounds);
1080
reed@google.com44da42e2011-11-10 20:04:47 +00001081 int xyIndex;
1082 JoinBoundsProc joinBoundsProc;
1083 if (this->isVerticalText()) {
1084 xyIndex = 1;
1085 joinBoundsProc = join_bounds_y;
1086 } else {
1087 xyIndex = 0;
1088 joinBoundsProc = join_bounds_x;
1089 }
1090
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 int n = 1;
1092 const char* stop = (const char*)text + byteLength;
1093 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001094 // our accumulated fixed-point advances might overflow 16.16, so we use
1095 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1096 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001097 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098
1099 SkAutoKern autokern;
1100
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001101 if (NULL == bounds) {
1102 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 int rsb;
1104 for (; text < stop; n++) {
1105 rsb = g->fRsbDelta;
1106 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001107 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001109 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001111 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 }
1113 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001114 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001116 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 int rsb;
1118 for (; text < stop; n++) {
1119 rsb = g->fRsbDelta;
1120 g = &glyphCacheProc(cache, &text);
1121 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001122 joinBoundsProc(*g, bounds, x);
1123 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001125 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 for (; text < stop; n++) {
1127 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001128 joinBoundsProc(*g, bounds, x);
1129 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 }
1131 }
1132 }
1133 SkASSERT(text == stop);
1134
1135 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001136 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137}
1138
1139SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001140 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 const char* text = (const char*)textData;
1142 SkASSERT(text != NULL || length == 0);
1143
reed@google.comed43dff2013-06-04 16:56:27 +00001144 SkCanonicalizePaint canon(*this);
1145 const SkPaint& paint = canon.getPaint();
1146 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001147
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001148 SkMatrix zoomMatrix, *zoomPtr = NULL;
1149 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 zoomMatrix.setScale(zoom, zoom);
1151 zoomPtr = &zoomMatrix;
1152 }
1153
reed@google.comed43dff2013-06-04 16:56:27 +00001154 SkAutoGlyphCache autoCache(paint, NULL, zoomPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 SkGlyphCache* cache = autoCache.getCache();
1156
1157 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001158
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001159 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 int tempCount;
1161
reed@google.comed43dff2013-06-04 16:56:27 +00001162 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001163 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001165 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1167 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1168 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1169 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1170 }
1171 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001172 } else if (bounds) {
1173 // ensure that even if we don't measure_text we still update the bounds
1174 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 }
1176 return width;
1177}
1178
1179typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1180
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001181static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 return text < stop;
1183}
1184
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001185static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 return text > stop;
1187}
1188
1189static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001190 const char** text, size_t length,
1191 const char** stop) {
1192 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 *stop = *text + length;
1194 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001195 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 // text should point to the end of the buffer, and stop to the beginning
1197 *stop = *text;
1198 *text += length;
1199 return backward_textBufferPred;
1200 }
1201}
1202
1203size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1204 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001205 TextBufferDirection tbd) const {
1206 if (0 == length || 0 >= maxWidth) {
1207 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 return 0;
1211 }
1212
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001213 if (0 == fTextSize) {
1214 if (measuredWidth) {
1215 *measuredWidth = 0;
1216 }
1217 return length;
1218 }
1219
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 SkASSERT(textD != NULL);
1221 const char* text = (const char*)textD;
1222
reed@google.comed43dff2013-06-04 16:56:27 +00001223 SkCanonicalizePaint canon(*this);
1224 const SkPaint& paint = canon.getPaint();
1225 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226
reed@google.comed43dff2013-06-04 16:56:27 +00001227 // adjust max in case we changed the textSize in paint
1228 if (scale) {
1229 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
reed@google.com72cf4922011-01-04 19:58:20 +00001231
reed@google.comed43dff2013-06-04 16:56:27 +00001232 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 SkGlyphCache* cache = autoCache.getCache();
1234
reed@google.comed43dff2013-06-04 16:56:27 +00001235 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 const char* stop;
1237 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001238 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001239 // use 64bits for our accumulator, to avoid overflowing 16.16
1240 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1241 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242
1243 SkAutoKern autokern;
1244
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001245 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001247 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 const char* curr = text;
1249 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001250 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001251 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 width -= x;
1253 text = curr;
1254 break;
1255 }
1256 rsb = g.fRsbDelta;
1257 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001258 } else {
1259 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001261 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001262 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 width -= x;
1264 text = curr;
1265 break;
1266 }
1267 }
1268 }
reed@google.com72cf4922011-01-04 19:58:20 +00001269
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001270 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001271 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001272 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 *measuredWidth = scalarWidth;
1276 }
reed@google.com72cf4922011-01-04 19:58:20 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 // return the number of bytes measured
1279 return (kForward_TextBufferDirection == tbd) ?
1280 text - stop + length : stop - text + length;
1281}
1282
1283///////////////////////////////////////////////////////////////////////////////
1284
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001285static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001286 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 return false; // don't detach the cache
1288}
1289
reed@google.com90808e82013-03-19 14:44:54 +00001290static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1291 void* context) {
1292 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293}
1294
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001295SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001296 SkCanonicalizePaint canon(*this);
1297 const SkPaint& paint = canon.getPaint();
1298 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001299
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001300 SkMatrix zoomMatrix, *zoomPtr = NULL;
1301 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 zoomMatrix.setScale(zoom, zoom);
1303 zoomPtr = &zoomMatrix;
1304 }
1305
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001307 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001309 }
reed@google.com72cf4922011-01-04 19:58:20 +00001310
reed@google.comed43dff2013-06-04 16:56:27 +00001311 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001313 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1315 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1316 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1317 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1318 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001319 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1320 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1321 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1322 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001323 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1324 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 }
1326 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1327}
1328
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001329///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001331static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 bounds->set(g.fLeft * scale,
1333 g.fTop * scale,
1334 (g.fLeft + g.fWidth) * scale,
1335 (g.fTop + g.fHeight) * scale);
1336}
1337
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001338int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1339 SkScalar widths[], SkRect bounds[]) const {
1340 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001342 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343
1344 SkASSERT(NULL != textData);
1345
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001346 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349
reed@google.comed43dff2013-06-04 16:56:27 +00001350 SkCanonicalizePaint canon(*this);
1351 const SkPaint& paint = canon.getPaint();
1352 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353
reed@google.comed43dff2013-06-04 16:56:27 +00001354 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 SkGlyphCache* cache = autoCache.getCache();
1356 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001357 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 NULL != bounds);
1359
1360 const char* text = (const char*)textData;
1361 const char* stop = text + byteLength;
1362 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001363 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001365 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 // we adjust the widths returned here through auto-kerning
1367 SkAutoKern autokern;
1368 SkFixed prevWidth = 0;
1369
1370 if (scale) {
1371 while (text < stop) {
1372 const SkGlyph& g = glyphCacheProc(cache, &text);
1373 if (widths) {
1374 SkFixed adjust = autokern.adjust(g);
1375
1376 if (count > 0) {
1377 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1378 *widths++ = SkScalarMul(w, scale);
1379 }
reed@google.com44da42e2011-11-10 20:04:47 +00001380 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 }
1382 if (bounds) {
1383 set_bounds(g, bounds++, scale);
1384 }
1385 ++count;
1386 }
1387 if (count > 0 && widths) {
1388 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1389 }
1390 } else {
1391 while (text < stop) {
1392 const SkGlyph& g = glyphCacheProc(cache, &text);
1393 if (widths) {
1394 SkFixed adjust = autokern.adjust(g);
1395
1396 if (count > 0) {
1397 *widths++ = SkFixedToScalar(prevWidth + adjust);
1398 }
reed@google.com44da42e2011-11-10 20:04:47 +00001399 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 }
1401 if (bounds) {
1402 set_bounds(g, bounds++);
1403 }
1404 ++count;
1405 }
1406 if (count > 0 && widths) {
1407 *widths = SkFixedToScalar(prevWidth);
1408 }
1409 }
1410 } else { // no devkern
1411 if (scale) {
1412 while (text < stop) {
1413 const SkGlyph& g = glyphCacheProc(cache, &text);
1414 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001415 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 scale);
1417 }
1418 if (bounds) {
1419 set_bounds(g, bounds++, scale);
1420 }
1421 ++count;
1422 }
1423 } else {
1424 while (text < stop) {
1425 const SkGlyph& g = glyphCacheProc(cache, &text);
1426 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001427 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 }
1429 if (bounds) {
1430 set_bounds(g, bounds++);
1431 }
1432 ++count;
1433 }
1434 }
1435 }
1436
1437 SkASSERT(text == stop);
1438 return count;
1439}
1440
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001441///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442
1443#include "SkDraw.h"
1444
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001445void SkPaint::getTextPath(const void* textData, size_t length,
1446 SkScalar x, SkScalar y, SkPath* path) const {
1447 SkASSERT(length == 0 || textData != NULL);
1448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001450 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453
djsollen@google.com166e6532012-03-20 14:24:38 +00001454 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 SkMatrix matrix;
1456 SkScalar prevXPos = 0;
1457
1458 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1459 matrix.postTranslate(x, y);
1460 path->reset();
1461
1462 SkScalar xpos;
1463 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001464 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001466 if (iterPath) {
1467 path->addPath(*iterPath, matrix);
1468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 prevXPos = xpos;
1470 }
1471}
1472
reed@google.comca0062e2012-07-20 11:20:32 +00001473void SkPaint::getPosTextPath(const void* textData, size_t length,
1474 const SkPoint pos[], SkPath* path) const {
1475 SkASSERT(length == 0 || textData != NULL);
1476
1477 const char* text = (const char*)textData;
1478 if (text == NULL || length == 0 || path == NULL) {
1479 return;
1480 }
1481
1482 SkTextToPathIter iter(text, length, *this, false);
1483 SkMatrix matrix;
1484 SkPoint prevPos;
1485 prevPos.set(0, 0);
1486
1487 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1488 path->reset();
1489
1490 unsigned int i = 0;
1491 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001492 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001493 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001494 if (iterPath) {
1495 path->addPath(*iterPath, matrix);
1496 }
reed@google.comca0062e2012-07-20 11:20:32 +00001497 prevPos = pos[i];
1498 i++;
1499 }
1500}
1501
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001503 SkWriteBuffer* buffer) {
1504 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
reed@google.com2739b272011-09-28 17:26:42 +00001507// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001508static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001511 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001512 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001513 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001514 }
1515
reed@google.com65dd8f82011-03-14 13:31:16 +00001516 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001517 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001518 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001519
1520 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
reed@android.com1cdcb512009-08-24 19:11:00 +00001523// if linear-text is on, then we force hinting to be off (since that's sort of
1524// the point of linear-text.
1525static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1526 SkPaint::Hinting h = paint.getHinting();
1527 if (paint.isLinearText()) {
1528 h = SkPaint::kNo_Hinting;
1529 }
1530 return h;
1531}
1532
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001533// return true if the paint is just a single color (i.e. not a shader). If its
1534// a shader, then we can't compute a const luminance for it :(
1535static bool justAColor(const SkPaint& paint, SkColor* color) {
1536 if (paint.getShader()) {
1537 return false;
1538 }
1539 SkColor c = paint.getColor();
1540 if (paint.getColorFilter()) {
1541 c = paint.getColorFilter()->filterColor(c);
1542 }
1543 if (color) {
1544 *color = c;
1545 }
1546 return true;
1547}
1548
reed@google.comce6dbb62012-02-10 22:01:45 +00001549static SkColor computeLuminanceColor(const SkPaint& paint) {
1550 SkColor c;
1551 if (!justAColor(paint, &c)) {
1552 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1553 }
1554 return c;
1555}
reed@google.com813d38b2012-02-13 21:37:57 +00001556
reed@google.comdd43df92012-02-15 14:50:29 +00001557#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1558
reed@google.com4f79b9b2011-09-13 13:23:26 +00001559// Beyond this size, LCD doesn't appreciably improve quality, but it always
1560// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001561#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1562 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1563#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001564
1565static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
commit-bot@chromium.orge78f7cf2014-03-14 22:59:05 +00001566 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1567 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
1568 SkScalar size = SkScalarSqrt(SkScalarAbs(area)) * rec.fTextSize;
1569 return size > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001570}
1571
reed@google.com72cf4922011-01-04 19:58:20 +00001572/*
1573 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1574 * that vary only slightly when we create our key into the font cache, since the font scaler
1575 * typically returns the same looking resuts for tiny changes in the matrix.
1576 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001577static SkScalar sk_relax(SkScalar x) {
1578 int n = sk_float_round2int(x * 1024);
1579 return n / 1024.0f;
1580}
1581
reed@android.com36a4c2a2009-07-22 19:52:11 +00001582void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001583 const SkDeviceProperties* deviceProperties,
1584 const SkMatrix* deviceMatrix,
1585 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001586 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001588 SkTypeface* typeface = paint.getTypeface();
1589 if (NULL == typeface) {
1590 typeface = SkTypeface::GetDefaultTypeface();
1591 }
1592 rec->fOrigFontID = typeface->uniqueID();
reed@google.com7d26c592011-06-13 13:01:10 +00001593 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 rec->fTextSize = paint.getTextSize();
1595 rec->fPreScaleX = paint.getTextScaleX();
1596 rec->fPreSkewX = paint.getTextSkewX();
1597
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001598 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001599 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1600 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1601 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1602 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001603 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1605 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1606 }
reed@google.com72cf4922011-01-04 19:58:20 +00001607
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 SkPaint::Style style = paint.getStyle();
1609 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001610
reed@google.comffe49f52011-11-22 19:42:41 +00001611 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001612
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001613 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001614#ifdef SK_USE_FREETYPE_EMBOLDEN
1615 flags |= SkScalerContext::kEmbolden_Flag;
1616#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001617 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1618 kStdFakeBoldInterpKeys,
1619 kStdFakeBoldInterpValues,
1620 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001622
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001623 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 style = SkPaint::kStrokeAndFill_Style;
1625 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001626 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001628 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001629#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630 }
1631
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001632 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001634 }
reed@google.com72cf4922011-01-04 19:58:20 +00001635
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001636 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637 rec->fFrameWidth = strokeWidth;
1638 rec->fMiterLimit = paint.getStrokeMiter();
1639 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1640
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001641 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001643 }
1644 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 rec->fFrameWidth = 0;
1646 rec->fMiterLimit = 0;
1647 rec->fStrokeJoin = 0;
1648 }
1649
reed@google.com02b53312011-05-18 19:00:53 +00001650 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1651
bungeman@google.com532470f2013-01-22 19:25:14 +00001652 SkDeviceProperties::Geometry geometry = deviceProperties
1653 ? deviceProperties->fGeometry
1654 : SkDeviceProperties::Geometry::MakeDefault();
1655 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
1656 if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001657 // eeek, can't support LCD
1658 rec->fMaskFormat = SkMask::kA8_Format;
1659 } else {
bungeman@google.com532470f2013-01-22 19:25:14 +00001660 if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
reed@google.com02b53312011-05-18 19:00:53 +00001661 flags |= SkScalerContext::kLCD_Vertical_Flag;
1662 }
bungeman@google.com532470f2013-01-22 19:25:14 +00001663 if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
reed@google.com02b53312011-05-18 19:00:53 +00001664 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1665 }
1666 }
1667 }
1668
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001669 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001670 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001671 }
1672 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001673 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001674 }
1675 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001676 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001677 }
reed@google.com830a23e2011-11-10 15:20:49 +00001678 if (paint.isVerticalText()) {
1679 flags |= SkScalerContext::kVertical_Flag;
1680 }
reed@google.com8351aab2012-01-18 17:06:35 +00001681 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1682 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1683 }
reed@google.com02b53312011-05-18 19:00:53 +00001684 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001685
reed@google.comffe49f52011-11-22 19:42:41 +00001686 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001687 rec->setHinting(computeHinting(paint));
1688
bungeman@google.com97efada2012-07-30 20:40:50 +00001689 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001690
1691 if (NULL == deviceProperties) {
1692 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1693 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1694 } else {
1695 rec->setDeviceGamma(deviceProperties->fGamma);
1696
1697 //For now always set the paint gamma equal to the device gamma.
1698 //The math in SkMaskGamma can handle them being different,
1699 //but it requires superluminous masks when
1700 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1701 rec->setPaintGamma(deviceProperties->fGamma);
1702 }
1703
1704#ifdef SK_GAMMA_CONTRAST
1705 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001706#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001707 /**
1708 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1709 * With lower values small text appears washed out (though correctly so).
1710 * With higher values lcd fringing is worse and the smoothing effect of
1711 * partial coverage is diminished.
1712 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001713 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001714#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001715
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001716 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001717
reed@android.com36a4c2a2009-07-22 19:52:11 +00001718 /* Allow the fonthost to modify our rec before we use it as a key into the
1719 cache. This way if we're asking for something that they will ignore,
1720 they can modify our rec up front, so we don't create duplicate cache
1721 entries.
1722 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001723 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001724
reed@google.com10d2d4d2012-03-01 22:32:51 +00001725 // be sure to call PostMakeRec(rec) before you actually use it!
1726}
1727
1728/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001729 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1730 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1731 * to hold it until the returned pointer is refed or forgotten.
1732 */
1733SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1734
bungeman@google.comae30f562012-09-11 18:44:55 +00001735static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001736static SkMaskGamma* gMaskGamma = NULL;
1737static SkScalar gContrast = SK_ScalarMin;
1738static SkScalar gPaintGamma = SK_ScalarMin;
1739static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001740/**
1741 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1742 * the returned SkMaskGamma pointer is refed or forgotten.
1743 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001744static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001745 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1746 if (NULL == gLinearMaskGamma) {
1747 gLinearMaskGamma = SkNEW(SkMaskGamma);
1748 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001749 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001750 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001751 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1752 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001753 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001754 gContrast = contrast;
1755 gPaintGamma = paintGamma;
1756 gDeviceGamma = deviceGamma;
1757 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001758 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001759}
1760
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001761/*static*/ void SkPaint::Term() {
1762 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1763
bungeman@google.comae30f562012-09-11 18:44:55 +00001764 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001765 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001766 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001767 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001768 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1769 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1770 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001771}
1772
bungeman@google.com97efada2012-07-30 20:40:50 +00001773/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001774 * We ensure that the rec is self-consistent and efficient (where possible)
1775 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001776void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001777 /**
1778 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001779 * limits the number of unique entries, and the scaler will only look at
1780 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001781 */
reed@google.comdd43df92012-02-15 14:50:29 +00001782 switch (rec->fMaskFormat) {
1783 case SkMask::kLCD16_Format:
1784 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001785 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001786 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001787 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001788 break;
1789 }
1790 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001791 // filter down the luminance to a single component, since A8 can't
1792 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001793
bungeman@google.com97efada2012-07-30 20:40:50 +00001794 SkColor color = rec->getLuminanceColor();
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001795 U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001796 //If we are asked to look like LCD, look like LCD.
1797 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1798 // HACK: Prevents green from being pre-blended as white.
1799 lum -= ((255 - lum) * lum) / 255;
1800 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001801
reed@google.comdd43df92012-02-15 14:50:29 +00001802 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001803 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001804 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001805 break;
1806 }
1807 case SkMask::kBW_Format:
1808 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001809 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001810 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001811 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812}
1813
1814#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1815
reed@google.com17fb3872011-05-04 14:31:07 +00001816#ifdef SK_DEBUG
1817 #define TEST_DESC
1818#endif
1819
reed@google.comffe49f52011-11-22 19:42:41 +00001820/*
1821 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1822 * by gamma correction, so we jam the luminance field to 0 (most common value
1823 * for black text) in hopes that we get a cache hit easier. A better solution
1824 * would be for the fontcache lookup to know to ignore the luminance field
1825 * entirely, but not sure how to do that and keep it fast.
1826 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001827void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1828 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001829 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001830 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 SkScalerContext::Rec rec;
1832
bungeman@google.com532470f2013-01-22 19:25:14 +00001833 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001834 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001835 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001836 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837
1838 size_t descSize = sizeof(rec);
1839 int entryCount = 1;
1840 SkPathEffect* pe = this->getPathEffect();
1841 SkMaskFilter* mf = this->getMaskFilter();
1842 SkRasterizer* ra = this->getRasterizer();
1843
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001844 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845
1846 if (pe) {
1847 peBuffer.writeFlattenable(pe);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001848 descSize += peBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 entryCount += 1;
1850 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1851 // seems like we could support kLCD as well at this point...
1852 }
1853 if (mf) {
1854 mfBuffer.writeFlattenable(mf);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001855 descSize += mfBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856 entryCount += 1;
1857 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001858 /* Pre-blend is not currently applied to filtered text.
1859 The primary filter is blur, for which contrast makes no sense,
1860 and for which the destination guess error is more visible.
1861 Also, all existing users of blur have calibrated for linear. */
1862 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863 }
1864 if (ra) {
1865 raBuffer.writeFlattenable(ra);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001866 descSize += raBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 entryCount += 1;
1868 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1869 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001870
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001871#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001872 SkWriteBuffer androidBuffer;
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001873 fPaintOptionsAndroid.flatten(androidBuffer);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001874 descSize += androidBuffer.bytesWritten();
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001875 entryCount += 1;
1876#endif
1877
reed@google.com10d2d4d2012-03-01 22:32:51 +00001878 ///////////////////////////////////////////////////////////////////////////
1879 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001880 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001881
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 descSize += SkDescriptor::ComputeOverhead(entryCount);
1883
1884 SkAutoDescriptor ad(descSize);
1885 SkDescriptor* desc = ad.getDesc();
1886
1887 desc->init();
1888 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1889
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001890#ifdef SK_BUILD_FOR_ANDROID
1891 add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1892#endif
1893
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894 if (pe) {
1895 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1896 }
1897 if (mf) {
1898 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1899 }
1900 if (ra) {
1901 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1902 }
1903
1904 SkASSERT(descSize == desc->getLength());
1905 desc->computeChecksum();
1906
reed@google.com17fb3872011-05-04 14:31:07 +00001907#ifdef TEST_DESC
1908 {
1909 // Check that we completely write the bytes in desc (our key), and that
1910 // there are no uninitialized bytes. If there were, then we would get
1911 // false-misses (or worse, false-hits) in our fontcache.
1912 //
1913 // We do this buy filling 2 others, one with 0s and the other with 1s
1914 // and create those, and then check that all 3 are identical.
1915 SkAutoDescriptor ad1(descSize);
1916 SkAutoDescriptor ad2(descSize);
1917 SkDescriptor* desc1 = ad1.getDesc();
1918 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001919
reed@google.com17fb3872011-05-04 14:31:07 +00001920 memset(desc1, 0x00, descSize);
1921 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001922
reed@google.com17fb3872011-05-04 14:31:07 +00001923 desc1->init();
1924 desc2->init();
1925 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1926 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001927
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001928#ifdef SK_BUILD_FOR_ANDROID
1929 add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1930 add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1931#endif
1932
reed@google.com17fb3872011-05-04 14:31:07 +00001933 if (pe) {
1934 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1935 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1936 }
1937 if (mf) {
1938 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1939 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1940 }
1941 if (ra) {
1942 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1943 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1944 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001945
reed@google.com17fb3872011-05-04 14:31:07 +00001946 SkASSERT(descSize == desc1->getLength());
1947 SkASSERT(descSize == desc2->getLength());
1948 desc1->computeChecksum();
1949 desc2->computeChecksum();
1950 SkASSERT(!memcmp(desc, desc1, descSize));
1951 SkASSERT(!memcmp(desc, desc2, descSize));
1952 }
1953#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001954
reed@google.com90808e82013-03-19 14:44:54 +00001955 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956}
1957
bungeman@google.com532470f2013-01-22 19:25:14 +00001958SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
1959 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 SkGlyphCache* cache;
bungeman@google.com532470f2013-01-22 19:25:14 +00001961 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962 return cache;
1963}
1964
bungeman@google.com97efada2012-07-30 20:40:50 +00001965/**
1966 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1967 */
1968//static
1969SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1970 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001971 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1972 rec.getPaintGamma(),
1973 rec.getDeviceGamma());
1974 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001975}
1976
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977///////////////////////////////////////////////////////////////////////////////
1978
1979#include "SkStream.h"
1980
reed@android.comaefd2bc2009-03-30 21:02:14 +00001981static uintptr_t asint(const void* p) {
1982 return reinterpret_cast<uintptr_t>(p);
1983}
1984
1985union Scalar32 {
1986 SkScalar fScalar;
1987 uint32_t f32;
1988};
1989
1990static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1991 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1992 Scalar32 tmp;
1993 tmp.fScalar = value;
1994 *ptr = tmp.f32;
1995 return ptr + 1;
1996}
1997
1998static SkScalar read_scalar(const uint32_t*& ptr) {
1999 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
2000 Scalar32 tmp;
2001 tmp.f32 = *ptr++;
2002 return tmp.fScalar;
2003}
2004
2005static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
2006 SkASSERT(a == (uint8_t)a);
2007 SkASSERT(b == (uint8_t)b);
2008 SkASSERT(c == (uint8_t)c);
2009 SkASSERT(d == (uint8_t)d);
2010 return (a << 24) | (b << 16) | (c << 8) | d;
2011}
2012
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002013#ifdef SK_DEBUG
2014 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
2015 SkASSERT(bitCount > 0 && bitCount <= 32);
2016 uint32_t mask = ~0U;
2017 mask >>= (32 - bitCount);
2018 SkASSERT(0 == (value & ~mask));
2019 }
2020#else
2021 #define ASSERT_FITS_IN(value, bitcount)
2022#endif
2023
reed@android.comaefd2bc2009-03-30 21:02:14 +00002024enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002025 kHasTypeface_FlatFlag = 0x01,
2026 kHasEffects_FlatFlag = 0x02,
2027 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002028
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002029 kFlatFlagMask = 0x7,
reed@android.comaefd2bc2009-03-30 21:02:14 +00002030};
2031
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002032enum BitsPerField {
2033 kFlags_BPF = 16,
2034 kHint_BPF = 2,
2035 kAlign_BPF = 2,
2036 kFilter_BPF = 2,
2037 kFlatFlags_BPF = 3,
2038};
2039
2040static inline int BPF_Mask(int bits) {
2041 return (1 << bits) - 1;
2042}
2043
2044static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
2045 unsigned filter, unsigned flatFlags) {
2046 ASSERT_FITS_IN(flags, kFlags_BPF);
2047 ASSERT_FITS_IN(hint, kHint_BPF);
2048 ASSERT_FITS_IN(align, kAlign_BPF);
2049 ASSERT_FITS_IN(filter, kFilter_BPF);
2050 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
2051
2052 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
2053 // add more bits in the future.
2054 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
2055}
2056
2057static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
2058 paint->setFlags(packed >> 16);
2059 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
2060 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
2061 paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
2062 return (FlatFlags)(packed & kFlatFlagMask);
2063}
2064
2065// V22_COMPATIBILITY_CODE
2066static FlatFlags unpack_paint_flags_v22(SkPaint* paint, uint32_t packed) {
2067 enum {
2068 kFilterBitmap_Flag = 0x02,
2069 kHighQualityFilterBitmap_Flag = 0x4000,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002070
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002071 kAll_Flags = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag
2072 };
2073
2074 // previously flags:16, textAlign:8, flatFlags:8
2075 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2076 unsigned flags = packed >> 16;
2077 int filter = 0;
2078 if (flags & kFilterBitmap_Flag) {
2079 filter |= 1;
2080 }
2081 if (flags & kHighQualityFilterBitmap_Flag) {
2082 filter |= 2;
2083 }
2084 paint->setFilterLevel((SkPaint::FilterLevel)filter);
2085 flags &= ~kAll_Flags; // remove these (now dead) bit flags
2086
2087 paint->setFlags(flags);
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002088
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002089 // hinting added later. 0 in this nibble means use the default.
2090 uint32_t hinting = (packed >> 12) & 0xF;
2091 paint->setHinting(0 == hinting ? SkPaint::kNormal_Hinting : static_cast<SkPaint::Hinting>(hinting-1));
2092 paint->setTextAlign(static_cast<SkPaint::Align>((packed >> 8) & 0xF));
2093 return (FlatFlags)(packed & kFlatFlagMask);
2094}
2095
reed@android.comaefd2bc2009-03-30 21:02:14 +00002096// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00002097static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00002098 1 * sizeof(SkColor) +
2099 1 * sizeof(uint16_t) +
2100 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002101
2102/* To save space/time, we analyze the paint, and write a truncated version of
2103 it if there are not tricky elements like shaders, etc.
2104 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002105void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002106 uint8_t flatFlags = 0;
2107 if (this->getTypeface()) {
2108 flatFlags |= kHasTypeface_FlatFlag;
2109 }
2110 if (asint(this->getPathEffect()) |
2111 asint(this->getShader()) |
2112 asint(this->getXfermode()) |
2113 asint(this->getMaskFilter()) |
2114 asint(this->getColorFilter()) |
2115 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002116 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002117 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002118 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002119 flatFlags |= kHasEffects_FlatFlag;
2120 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002121#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002122 if (this->getPaintOptionsAndroid() != SkPaintOptionsAndroid()) {
2123 flatFlags |= kHasNonDefaultPaintOptionsAndroid_FlatFlag;
2124 }
2125#endif
reed@android.comaefd2bc2009-03-30 21:02:14 +00002126
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002127 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2128 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002129
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002130 ptr = write_scalar(ptr, this->getTextSize());
2131 ptr = write_scalar(ptr, this->getTextScaleX());
2132 ptr = write_scalar(ptr, this->getTextSkewX());
2133 ptr = write_scalar(ptr, this->getStrokeWidth());
2134 ptr = write_scalar(ptr, this->getStrokeMiter());
2135 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002136
2137 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
2138 this->getFilterLevel(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002139 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2140 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002141
2142 // now we're done with ptr and the (pre)reserved space. If we need to write
2143 // additional fields, use the buffer directly
2144 if (flatFlags & kHasTypeface_FlatFlag) {
2145 buffer.writeTypeface(this->getTypeface());
2146 }
2147 if (flatFlags & kHasEffects_FlatFlag) {
2148 buffer.writeFlattenable(this->getPathEffect());
2149 buffer.writeFlattenable(this->getShader());
2150 buffer.writeFlattenable(this->getXfermode());
2151 buffer.writeFlattenable(this->getMaskFilter());
2152 buffer.writeFlattenable(this->getColorFilter());
2153 buffer.writeFlattenable(this->getRasterizer());
2154 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002155 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00002156
2157 if (fAnnotation) {
2158 buffer.writeBool(true);
2159 fAnnotation->writeToBuffer(buffer);
2160 } else {
2161 buffer.writeBool(false);
2162 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002163 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002164#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002165 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2166 this->getPaintOptionsAndroid().flatten(buffer);
2167 }
2168#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169}
2170
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002171void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002172 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2173 const void* podData = buffer.skip(kPODPaintSize);
2174 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002175
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002176 // the order we read must match the order we wrote in flatten()
2177 this->setTextSize(read_scalar(pod));
2178 this->setTextScaleX(read_scalar(pod));
2179 this->setTextSkewX(read_scalar(pod));
2180 this->setStrokeWidth(read_scalar(pod));
2181 this->setStrokeMiter(read_scalar(pod));
2182 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002183
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002184 unsigned flatFlags = 0;
commit-bot@chromium.org7ed173b2014-05-20 17:31:08 +00002185 if (buffer.isVersionLT(SkReadBuffer::kFilterLevelIsEnum_Version)) {
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002186 flatFlags = unpack_paint_flags_v22(this, *pod++);
2187 } else {
2188 flatFlags = unpack_paint_flags(this, *pod++);
2189 }
2190
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002191 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002192 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2193 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2194 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2195 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002196
2197 if (flatFlags & kHasTypeface_FlatFlag) {
2198 this->setTypeface(buffer.readTypeface());
2199 } else {
2200 this->setTypeface(NULL);
2201 }
2202
2203 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002204 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2205 SkSafeUnref(this->setShader(buffer.readShader()));
2206 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2207 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2208 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2209 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2210 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2211 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002212
reed@google.com0cd2ac62013-10-14 20:02:44 +00002213 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002214 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00002215 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002216 } else {
2217 this->setPathEffect(NULL);
2218 this->setShader(NULL);
2219 this->setXfermode(NULL);
2220 this->setMaskFilter(NULL);
2221 this->setColorFilter(NULL);
2222 this->setRasterizer(NULL);
2223 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002224 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002225 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002226
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002227#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002228 this->setPaintOptionsAndroid(SkPaintOptionsAndroid());
2229#endif
2230 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2231 SkPaintOptionsAndroid options;
2232 options.unflatten(buffer);
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002233#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002234 this->setPaintOptionsAndroid(options);
2235#endif
2236 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237}
2238
2239///////////////////////////////////////////////////////////////////////////////
2240
reed@google.com82065d62011-02-07 15:30:46 +00002241SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002242 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243 SkRefCnt_SafeAssign(fShader, shader);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002244 fDirtyBits = set_mask(fDirtyBits, kShader_DirtyBit, shader != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 return shader;
2246}
2247
reed@google.com82065d62011-02-07 15:30:46 +00002248SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002249 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 SkRefCnt_SafeAssign(fColorFilter, filter);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002251 fDirtyBits = set_mask(fDirtyBits, kColorFilter_DirtyBit, filter != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 return filter;
2253}
2254
reed@google.com82065d62011-02-07 15:30:46 +00002255SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002256 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257 SkRefCnt_SafeAssign(fXfermode, mode);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002258 fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, mode != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 return mode;
2260}
2261
reed@android.com0baf1932009-06-24 12:41:42 +00002262SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002263 SkSafeUnref(fXfermode);
2264 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002265 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002266 fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, fXfermode != NULL);
reed@android.comd66eef72009-06-24 12:29:16 +00002267 return fXfermode;
2268}
2269
reed@google.com82065d62011-02-07 15:30:46 +00002270SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002271 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 SkRefCnt_SafeAssign(fPathEffect, effect);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002273 fDirtyBits = set_mask(fDirtyBits, kPathEffect_DirtyBit, effect != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274 return effect;
2275}
2276
reed@google.com82065d62011-02-07 15:30:46 +00002277SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002278 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 SkRefCnt_SafeAssign(fMaskFilter, filter);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002280 fDirtyBits = set_mask(fDirtyBits, kMaskFilter_DirtyBit, filter != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 return filter;
2282}
2283
reed@google.com82065d62011-02-07 15:30:46 +00002284///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285
reed@google.com4bbdeac2013-01-24 21:03:11 +00002286bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2287 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002288 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289
reed@google.comfd4be262012-05-25 01:04:12 +00002290 const SkPath* srcPtr = &src;
2291 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002292
reed@google.com4bbdeac2013-01-24 21:03:11 +00002293 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002294 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
2296
reed@google.comfd4be262012-05-25 01:04:12 +00002297 if (!rec.applyToPath(dst, *srcPtr)) {
2298 if (srcPtr == &tmpPath) {
2299 // If path's were copy-on-write, this trick would not be needed.
2300 // As it is, we want to save making a deep-copy from tmpPath -> dst
2301 // since we know we're just going to delete tmpPath when we return,
2302 // so the swap saves that copy.
2303 dst->swap(tmpPath);
2304 } else {
2305 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 }
2307 }
reed@google.comfd4be262012-05-25 01:04:12 +00002308 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309}
2310
reed@google.come4f10a72012-05-15 20:47:50 +00002311const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002312 SkRect* storage,
2313 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002315
reed@google.come4f10a72012-05-15 20:47:50 +00002316 const SkRect* src = &origSrc;
2317
reed@google.com9efd9a02012-01-30 15:41:43 +00002318 if (this->getLooper()) {
2319 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002320 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002321 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002323
reed@google.come4f10a72012-05-15 20:47:50 +00002324 SkRect tmpSrc;
2325 if (this->getPathEffect()) {
2326 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2327 src = &tmpSrc;
2328 }
2329
reed@google.coma584aed2012-05-16 14:06:02 +00002330 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002331 // since we're stroked, outset the rect by the radius (and join type)
2332 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2333 if (0 == radius) { // hairline
2334 radius = SK_Scalar1;
2335 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2336 SkScalar scale = this->getStrokeMiter();
2337 if (scale > SK_Scalar1) {
2338 radius = SkScalarMul(radius, scale);
2339 }
2340 }
reed@google.come4f10a72012-05-15 20:47:50 +00002341 storage->set(src->fLeft - radius, src->fTop - radius,
2342 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002343 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002344 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002345 }
2346
reed@google.com9efd9a02012-01-30 15:41:43 +00002347 if (this->getMaskFilter()) {
2348 this->getMaskFilter()->computeFastBounds(*storage, storage);
2349 }
2350
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002351 if (this->getImageFilter()) {
2352 this->getImageFilter()->computeFastBounds(*storage, storage);
2353 }
2354
reed@android.comd252db02009-04-01 18:31:44 +00002355 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356}
2357
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002358#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002359void SkPaint::toString(SkString* str) const {
2360 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2361
2362 SkTypeface* typeface = this->getTypeface();
2363 if (NULL != typeface) {
2364 SkDynamicMemoryWStream ostream;
2365 typeface->serialize(&ostream);
2366 SkAutoTUnref<SkData> data(ostream.copyToData());
2367
2368 SkMemoryStream stream(data);
2369 SkFontDescriptor descriptor(&stream);
2370
2371 str->append("<dt>Font Family Name:</dt><dd>");
2372 str->append(descriptor.getFamilyName());
2373 str->append("</dd><dt>Font Full Name:</dt><dd>");
2374 str->append(descriptor.getFullName());
2375 str->append("</dd><dt>Font PS Name:</dt><dd>");
2376 str->append(descriptor.getPostscriptName());
2377 str->append("</dd><dt>Font File Name:</dt><dd>");
2378 str->append(descriptor.getFontFileName());
2379 str->append("</dd>");
2380 }
2381
2382 str->append("<dt>TextSize:</dt><dd>");
2383 str->appendScalar(this->getTextSize());
2384 str->append("</dd>");
2385
2386 str->append("<dt>TextScaleX:</dt><dd>");
2387 str->appendScalar(this->getTextScaleX());
2388 str->append("</dd>");
2389
2390 str->append("<dt>TextSkewX:</dt><dd>");
2391 str->appendScalar(this->getTextSkewX());
2392 str->append("</dd>");
2393
2394 SkPathEffect* pathEffect = this->getPathEffect();
2395 if (NULL != pathEffect) {
2396 str->append("<dt>PathEffect:</dt><dd>");
2397 str->append("</dd>");
2398 }
2399
2400 SkShader* shader = this->getShader();
2401 if (NULL != shader) {
2402 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002403 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002404 str->append("</dd>");
2405 }
2406
2407 SkXfermode* xfer = this->getXfermode();
2408 if (NULL != xfer) {
2409 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002410 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002411 str->append("</dd>");
2412 }
2413
2414 SkMaskFilter* maskFilter = this->getMaskFilter();
2415 if (NULL != maskFilter) {
2416 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002417 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002418 str->append("</dd>");
2419 }
2420
2421 SkColorFilter* colorFilter = this->getColorFilter();
2422 if (NULL != colorFilter) {
2423 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002424 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002425 str->append("</dd>");
2426 }
2427
2428 SkRasterizer* rasterizer = this->getRasterizer();
2429 if (NULL != rasterizer) {
2430 str->append("<dt>Rasterizer:</dt><dd>");
2431 str->append("</dd>");
2432 }
2433
2434 SkDrawLooper* looper = this->getLooper();
2435 if (NULL != looper) {
2436 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002437 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002438 str->append("</dd>");
2439 }
2440
2441 SkImageFilter* imageFilter = this->getImageFilter();
2442 if (NULL != imageFilter) {
2443 str->append("<dt>ImageFilter:</dt><dd>");
2444 str->append("</dd>");
2445 }
2446
2447 SkAnnotation* annotation = this->getAnnotation();
2448 if (NULL != annotation) {
2449 str->append("<dt>Annotation:</dt><dd>");
2450 str->append("</dd>");
2451 }
2452
2453 str->append("<dt>Color:</dt><dd>0x");
2454 SkColor color = this->getColor();
2455 str->appendHex(color);
2456 str->append("</dd>");
2457
2458 str->append("<dt>Stroke Width:</dt><dd>");
2459 str->appendScalar(this->getStrokeWidth());
2460 str->append("</dd>");
2461
2462 str->append("<dt>Stroke Miter:</dt><dd>");
2463 str->appendScalar(this->getStrokeMiter());
2464 str->append("</dd>");
2465
2466 str->append("<dt>Flags:</dt><dd>(");
2467 if (this->getFlags()) {
2468 bool needSeparator = false;
2469 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002470 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2471 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2472 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2473 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2474 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2475 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2476 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2477 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2478 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2479 "EmbeddedBitmapText", &needSeparator);
2480 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2481 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2482 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2483 "GenA8FromLCD", &needSeparator);
2484 } else {
2485 str->append("None");
2486 }
2487 str->append(")</dd>");
2488
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002489 str->append("<dt>FilterLevel:</dt><dd>");
2490 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2491 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2492 str->append("</dd>");
2493
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002494 str->append("<dt>TextAlign:</dt><dd>");
2495 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2496 str->append(gTextAlignStrings[this->getTextAlign()]);
2497 str->append("</dd>");
2498
2499 str->append("<dt>CapType:</dt><dd>");
2500 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2501 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2502 str->append("</dd>");
2503
2504 str->append("<dt>JoinType:</dt><dd>");
2505 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2506 str->append(gJoinStrings[this->getStrokeJoin()]);
2507 str->append("</dd>");
2508
2509 str->append("<dt>Style:</dt><dd>");
2510 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2511 str->append(gStyleStrings[this->getStyle()]);
2512 str->append("</dd>");
2513
2514 str->append("<dt>TextEncoding:</dt><dd>");
2515 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2516 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2517 str->append("</dd>");
2518
2519 str->append("<dt>Hinting:</dt><dd>");
2520 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2521 str->append(gHintingStrings[this->getHinting()]);
2522 str->append("</dd>");
2523
2524 str->append("</dd></dl></dl>");
2525}
2526#endif
2527
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002528///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002529
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002530static bool has_thick_frame(const SkPaint& paint) {
2531 return paint.getStrokeWidth() > 0 &&
2532 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002533}
2534
2535SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2536 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002537 bool applyStrokeAndPathEffects)
2538 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2540 true);
2541
djsollen@google.com166e6532012-03-20 14:24:38 +00002542 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2544
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002545 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002546 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002547 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548
djsollen@google.com166e6532012-03-20 14:24:38 +00002549 // can't use our canonical size if we need to apply patheffects
2550 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2552 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002553 if (has_thick_frame(fPaint)) {
2554 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2555 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002556 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002557 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002558 }
reed@google.com72cf4922011-01-04 19:58:20 +00002559
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002560 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561 fPaint.setStyle(SkPaint::kFill_Style);
2562 fPaint.setPathEffect(NULL);
2563 }
2564
bungeman@google.com532470f2013-01-22 19:25:14 +00002565 fCache = fPaint.detachCache(NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002566
2567 SkPaint::Style style = SkPaint::kFill_Style;
2568 SkPathEffect* pe = NULL;
2569
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002570 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571 style = paint.getStyle(); // restore
2572 pe = paint.getPathEffect(); // restore
2573 }
2574 fPaint.setStyle(style);
2575 fPaint.setPathEffect(pe);
2576 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2577
2578 // now compute fXOffset if needed
2579
2580 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002581 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002582 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002583 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2584 &count, NULL), fScale);
2585 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002586 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002587 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002588 xOffset = -width;
2589 }
2590 fXPos = xOffset;
2591 fPrevAdvance = 0;
2592
2593 fText = text;
2594 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002595
reed@google.com44da42e2011-11-10 20:04:47 +00002596 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002597}
2598
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002599SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002600 SkGlyphCache::AttachCache(fCache);
2601}
2602
reed@google.com7b4531f2012-08-07 15:53:00 +00002603bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2604 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002605 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2606
2607 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002608 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002609
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002610 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002611 if (path) {
2612 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002613 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002614 } else {
2615 if (path) {
2616 *path = NULL;
2617 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002619 if (xpos) {
2620 *xpos = fXPos;
2621 }
2622 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002623 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002624 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002625}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002626
2627///////////////////////////////////////////////////////////////////////////////
2628
2629bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002630 if (fLooper) {
2631 return false;
2632 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002633 SkXfermode::Mode mode;
2634 if (SkXfermode::AsMode(fXfermode, &mode)) {
2635 switch (mode) {
2636 case SkXfermode::kSrcOver_Mode:
2637 case SkXfermode::kSrcATop_Mode:
2638 case SkXfermode::kDstOut_Mode:
2639 case SkXfermode::kDstOver_Mode:
2640 case SkXfermode::kPlus_Mode:
2641 return 0 == this->getAlpha();
2642 case SkXfermode::kDst_Mode:
2643 return true;
2644 default:
2645 break;
2646 }
2647 }
2648 return false;
2649}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002650
2651void SkPaint::setBitfields(uint32_t bitfields) {
2652 fBitfields = bitfields;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002653}
2654
2655inline static unsigned popcount(uint8_t x) {
2656 // As in Hacker's delight, adapted for just 8 bits.
2657 x = (x & 0x55) + ((x >> 1) & 0x55); // a b c d w x y z -> a+b c+d w+x y+z
2658 x = (x & 0x33) + ((x >> 2) & 0x33); // a+b c+d w+x y+z -> a+b+c+d w+x+y+z
2659 x = (x & 0x0F) + ((x >> 4) & 0x0F); // a+b+c+d w+x+y+z -> a+b+c+d+w+x+y+z
2660 return x;
2661}
2662
2663void SkPaint::FlatteningTraits::Flatten(SkWriteBuffer& buffer, const SkPaint& paint) {
2664 const uint32_t dirty = paint.fDirtyBits;
2665
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +00002666 // Each of the low 7 dirty bits corresponds to a 4-byte flat value,
2667 // plus one for the dirty bits and one for the bitfields
2668 const size_t flatBytes = 4 * (popcount(dirty & kPOD_DirtyBitMask) + 2);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002669 SkASSERT(flatBytes <= 32);
2670 uint32_t* u32 = buffer.reserve(flatBytes);
2671 *u32++ = dirty;
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +00002672 *u32++ = paint.getBitfields();
2673 if (0 == dirty) {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002674 return;
2675 }
2676
2677#define F(dst, field) if (dirty & k##field##_DirtyBit) *dst++ = paint.get##field()
2678 F(u32, Color);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002679 SkScalar* f32 = reinterpret_cast<SkScalar*>(u32);
2680 F(f32, TextSize);
2681 F(f32, TextScaleX);
2682 F(f32, TextSkewX);
2683 F(f32, StrokeWidth);
2684 F(f32, StrokeMiter);
2685#undef F
2686#define F(field) if (dirty & k##field##_DirtyBit) buffer.writeFlattenable(paint.get##field())
2687 F(PathEffect);
2688 F(Shader);
2689 F(Xfermode);
2690 F(MaskFilter);
2691 F(ColorFilter);
2692 F(Rasterizer);
2693 F(Looper);
2694 F(ImageFilter);
2695#undef F
2696 if (dirty & kTypeface_DirtyBit) buffer.writeTypeface(paint.getTypeface());
2697 if (dirty & kAnnotation_DirtyBit) paint.getAnnotation()->writeToBuffer(buffer);
2698#ifdef SK_BUILD_FOR_ANDROID
2699 if (dirty & kPaintOptionsAndroid_DirtyBit) paint.getPaintOptionsAndroid().flatten(buffer);
2700#endif
2701}
2702
2703void SkPaint::FlatteningTraits::Unflatten(SkReadBuffer& buffer, SkPaint* paint) {
2704 const uint32_t dirty = buffer.readUInt();
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +00002705 paint->setBitfields(buffer.readUInt());
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002706 if (dirty == 0) {
2707 return;
2708 }
2709#define F(field, reader) if (dirty & k##field##_DirtyBit) paint->set##field(buffer.reader())
2710// Same function, except it unrefs the object newly set on the paint:
2711#define F_UNREF(field, reader) \
2712 if (dirty & k##field##_DirtyBit) \
2713 paint->set##field(buffer.reader())->unref()
2714
2715 F(Color, readUInt);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002716 F(TextSize, readScalar);
2717 F(TextScaleX, readScalar);
2718 F(TextSkewX, readScalar);
2719 F(StrokeWidth, readScalar);
2720 F(StrokeMiter, readScalar);
2721 F_UNREF(PathEffect, readPathEffect);
2722 F_UNREF(Shader, readShader);
2723 F_UNREF(Xfermode, readXfermode);
2724 F_UNREF(MaskFilter, readMaskFilter);
2725 F_UNREF(ColorFilter, readColorFilter);
2726 F_UNREF(Rasterizer, readRasterizer);
2727 F_UNREF(Looper, readDrawLooper);
2728 F_UNREF(ImageFilter, readImageFilter);
2729 F(Typeface, readTypeface);
2730#undef F
2731#undef F_UNREF
2732 if (dirty & kAnnotation_DirtyBit) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002733 paint->setAnnotation(SkAnnotation::Create(buffer))->unref();
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002734 }
2735#ifdef SK_BUILD_FOR_ANDROID
2736 if (dirty & kPaintOptionsAndroid_DirtyBit) {
2737 SkPaintOptionsAndroid options;
2738 options.unflatten(buffer);
2739 paint->setPaintOptionsAndroid(options);
2740 }
2741#endif
2742 SkASSERT(dirty == paint->fDirtyBits);
2743}