blob: 42144c102992e779fe0b7d883a1f43ab7c18aaaf [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"
rmistryc4b84ae2014-06-23 06:59:15 -070014#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000015#include "SkFontDescriptor.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkFontHost.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000017#include "SkGlyphCache.h"
reed@google.com15356a62011-11-03 19:29:08 +000018#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000020#include "SkMaskGamma.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000021#include "SkReadBuffer.h"
22#include "SkWriteBuffer.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000023#include "SkPaintDefaults.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPathEffect.h"
25#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000026#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000028#include "SkShader.h"
29#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000032#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000033#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkTypeface.h"
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,
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000057};
reed@android.com8a1c16f2008-12-17 15:59:43 +000058
reed@google.coma3237872011-07-05 19:20:48 +000059// define this to get a printf for out-of-range parameter in setters
60// e.g. setTextSize(-1)
61//#define SK_REPORT_API_RANGE_CHECK
62
djsollen@google.com56c69772011-11-08 19:00:26 +000063#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000064#define GEN_ID_INC fGenerationID++
65#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
66#else
67#define GEN_ID_INC
68#define GEN_ID_INC_EVAL(expression)
69#endif
70
reed@android.coma3122b92009-08-13 20:38:25 +000071SkPaint::SkPaint() {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000072 fTypeface = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 fPathEffect = NULL;
74 fShader = NULL;
75 fXfermode = NULL;
76 fMaskFilter = NULL;
77 fColorFilter = NULL;
78 fRasterizer = NULL;
79 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000080 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000081 fAnnotation = NULL;
reed@android.coma3122b92009-08-13 20:38:25 +000082
reedf59eab22014-07-14 14:39:15 -070083 fTextSize = SkPaintDefaults_TextSize;
84 fTextScaleX = SK_Scalar1;
85 fTextSkewX = 0;
86 fColor = SK_ColorBLACK;
87 fWidth = 0;
88 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000089
90 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070091 fBitfieldsUInt = 0;
92 fBitfields.fFlags = SkPaintDefaults_Flags;
93 fBitfields.fCapType = kDefault_Cap;
94 fBitfields.fJoinType = kDefault_Join;
95 fBitfields.fTextAlign = kLeft_Align;
96 fBitfields.fStyle = kFill_Style;
97 fBitfields.fTextEncoding = kUTF8_TextEncoding;
98 fBitfields.fHinting = SkPaintDefaults_Hinting;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000099
100 fDirtyBits = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +0000101#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000102 fGenerationID = 0;
103#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104}
105
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000106SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000107#define COPY(field) field = src.field
108#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000110 REF_COPY(fTypeface);
111 REF_COPY(fPathEffect);
112 REF_COPY(fShader);
113 REF_COPY(fXfermode);
114 REF_COPY(fMaskFilter);
115 REF_COPY(fColorFilter);
116 REF_COPY(fRasterizer);
117 REF_COPY(fLooper);
118 REF_COPY(fImageFilter);
119 REF_COPY(fAnnotation);
120
121 COPY(fTextSize);
122 COPY(fTextScaleX);
123 COPY(fTextSkewX);
124 COPY(fColor);
125 COPY(fWidth);
126 COPY(fMiterLimit);
127 COPY(fBitfields);
128 COPY(fDirtyBits);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000129
130#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000131 COPY(fGenerationID);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000132#endif
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000133
134#undef COPY
135#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136}
137
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000138SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000139 SkSafeUnref(fTypeface);
140 SkSafeUnref(fPathEffect);
141 SkSafeUnref(fShader);
142 SkSafeUnref(fXfermode);
143 SkSafeUnref(fMaskFilter);
144 SkSafeUnref(fColorFilter);
145 SkSafeUnref(fRasterizer);
146 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000147 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000148 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149}
150
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000151SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000152 if (this == &src) {
153 return *this;
154 }
155
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000156#define COPY(field) field = src.field
157#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
158
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 SkASSERT(&src);
160
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000161 REF_COPY(fTypeface);
162 REF_COPY(fPathEffect);
163 REF_COPY(fShader);
164 REF_COPY(fXfermode);
165 REF_COPY(fMaskFilter);
166 REF_COPY(fColorFilter);
167 REF_COPY(fRasterizer);
168 REF_COPY(fLooper);
169 REF_COPY(fImageFilter);
170 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000172 COPY(fTextSize);
173 COPY(fTextScaleX);
174 COPY(fTextSkewX);
175 COPY(fColor);
176 COPY(fWidth);
177 COPY(fMiterLimit);
178 COPY(fBitfields);
179 COPY(fDirtyBits);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
djsollen@google.com56c69772011-11-08 19:00:26 +0000181#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000182 ++fGenerationID;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000183#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000186
187#undef COPY
188#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189}
190
robertphillips@google.comb2657412013-08-07 22:36:29 +0000191bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000192#define EQUAL(field) (a.field == b.field)
193 // Don't check fGenerationID or fDirtyBits, which can be different for logically equal paints.
194 return EQUAL(fTypeface)
195 && EQUAL(fPathEffect)
196 && EQUAL(fShader)
197 && EQUAL(fXfermode)
198 && EQUAL(fMaskFilter)
199 && EQUAL(fColorFilter)
200 && EQUAL(fRasterizer)
201 && EQUAL(fLooper)
202 && EQUAL(fImageFilter)
203 && EQUAL(fAnnotation)
204 && EQUAL(fTextSize)
205 && EQUAL(fTextScaleX)
206 && EQUAL(fTextSkewX)
207 && EQUAL(fColor)
208 && EQUAL(fWidth)
209 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700210 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000211 ;
212#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000213}
214
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000215void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 SkPaint init;
217
djsollen@google.com56c69772011-11-08 19:00:26 +0000218#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000219 uint32_t oldGenerationID = fGenerationID;
220#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000222#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000223 fGenerationID = oldGenerationID + 1;
224#endif
225}
226
djsollen@google.com56c69772011-11-08 19:00:26 +0000227#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000228uint32_t SkPaint::getGenerationID() const {
229 return fGenerationID;
230}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000231
232void SkPaint::setGenerationID(uint32_t generationID) {
233 fGenerationID = generationID;
234}
djsollen@google.com60abb072012-02-15 18:49:15 +0000235#endif
236
reed@google.comc9683152013-07-18 13:47:01 +0000237void SkPaint::setFilterLevel(FilterLevel level) {
reedf59eab22014-07-14 14:39:15 -0700238 GEN_ID_INC_EVAL((unsigned) level != fBitfields.fFilterLevel);
239 fBitfields.fFilterLevel = level;
reed@google.comc9683152013-07-18 13:47:01 +0000240}
241
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000242void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700243 GEN_ID_INC_EVAL((unsigned) hintingLevel != fBitfields.fHinting);
244 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245}
246
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000247void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700248 GEN_ID_INC_EVAL(flags != fBitfields.fFlags);
249 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250}
251
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000252void SkPaint::setAntiAlias(bool doAA) {
reedf59eab22014-07-14 14:39:15 -0700253 this->setFlags(SkSetClearMask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254}
255
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000256void SkPaint::setDither(bool doDither) {
reedf59eab22014-07-14 14:39:15 -0700257 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258}
259
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000260void SkPaint::setSubpixelText(bool doSubpixel) {
reedf59eab22014-07-14 14:39:15 -0700261 this->setFlags(SkSetClearMask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262}
263
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000264void SkPaint::setLCDRenderText(bool doLCDRender) {
reedf59eab22014-07-14 14:39:15 -0700265 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000266}
267
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000268void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reedf59eab22014-07-14 14:39:15 -0700269 this->setFlags(SkSetClearMask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000270}
271
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000272void SkPaint::setAutohinted(bool useAutohinter) {
reedf59eab22014-07-14 14:39:15 -0700273 this->setFlags(SkSetClearMask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000274}
275
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000276void SkPaint::setLinearText(bool doLinearText) {
reedf59eab22014-07-14 14:39:15 -0700277 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278}
279
reed@google.com830a23e2011-11-10 15:20:49 +0000280void SkPaint::setVerticalText(bool doVertical) {
reedf59eab22014-07-14 14:39:15 -0700281 this->setFlags(SkSetClearMask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000282}
283
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000284void SkPaint::setUnderlineText(bool doUnderline) {
reedf59eab22014-07-14 14:39:15 -0700285 this->setFlags(SkSetClearMask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286}
287
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000288void SkPaint::setStrikeThruText(bool doStrikeThru) {
reedf59eab22014-07-14 14:39:15 -0700289 this->setFlags(SkSetClearMask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290}
291
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000292void SkPaint::setFakeBoldText(bool doFakeBold) {
reedf59eab22014-07-14 14:39:15 -0700293 this->setFlags(SkSetClearMask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294}
295
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000296void SkPaint::setDevKernText(bool doDevKern) {
reedf59eab22014-07-14 14:39:15 -0700297 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298}
299
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000300void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) {
reedf59eab22014-07-14 14:39:15 -0700301 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag));
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000302}
303
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000304void SkPaint::setStyle(Style style) {
305 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700306 GEN_ID_INC_EVAL((unsigned)style != fBitfields.fStyle);
307 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000308 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000309#ifdef SK_REPORT_API_RANGE_CHECK
310 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
311#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313}
314
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000315void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000316 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 fColor = color;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000318 fDirtyBits |= kColor_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319}
320
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000321void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000322 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
323 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324}
325
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000326void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000327 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328}
329
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000330void SkPaint::setStrokeWidth(SkScalar width) {
331 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000332 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 fWidth = width;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000334 fDirtyBits |= kStrokeWidth_DirtyBit;
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::setStrokeWidth() called with negative value\n");
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::setStrokeMiter(SkScalar limit) {
343 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000344 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 fMiterLimit = limit;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000346 fDirtyBits |= kStrokeMiter_DirtyBit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000347 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000348#ifdef SK_REPORT_API_RANGE_CHECK
349 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
350#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000351 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352}
353
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000354void SkPaint::setStrokeCap(Cap ct) {
355 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700356 GEN_ID_INC_EVAL((unsigned)ct != fBitfields.fCapType);
357 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000358 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000359#ifdef SK_REPORT_API_RANGE_CHECK
360 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
361#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363}
364
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000365void SkPaint::setStrokeJoin(Join jt) {
366 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700367 GEN_ID_INC_EVAL((unsigned)jt != fBitfields.fJoinType);
368 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000369 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000370#ifdef SK_REPORT_API_RANGE_CHECK
371 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
372#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000373 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374}
375
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000376///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000378void SkPaint::setTextAlign(Align align) {
379 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700380 GEN_ID_INC_EVAL((unsigned)align != fBitfields.fTextAlign);
381 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000382 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000383#ifdef SK_REPORT_API_RANGE_CHECK
384 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
385#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387}
388
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000389void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000390 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000391 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 fTextSize = ts;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000393 fDirtyBits |= kTextSize_DirtyBit;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000394 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000395#ifdef SK_REPORT_API_RANGE_CHECK
396 SkDebugf("SkPaint::setTextSize() called with negative value\n");
397#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000398 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399}
400
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000401void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000402 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 fTextScaleX = scaleX;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000404 fDirtyBits |= kTextScaleX_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405}
406
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000407void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000408 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 fTextSkewX = skewX;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000410 fDirtyBits |= kTextSkewX_DirtyBit;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411}
412
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000413void SkPaint::setTextEncoding(TextEncoding encoding) {
414 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700415 GEN_ID_INC_EVAL((unsigned)encoding != fBitfields.fTextEncoding);
416 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000417 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000418#ifdef SK_REPORT_API_RANGE_CHECK
419 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
420#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000421 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422}
423
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000424///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000426// Returns dst with the given bitmask enabled or disabled, depending on value.
427inline static uint32_t set_mask(uint32_t dst, uint32_t bitmask, bool value) {
428 return value ? (dst | bitmask) : (dst & ~bitmask);
429}
430
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000431SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000433 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000434 fDirtyBits = set_mask(fDirtyBits, kTypeface_DirtyBit, font != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 return font;
436}
437
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000438SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000440 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000441 fDirtyBits = set_mask(fDirtyBits, kRasterizer_DirtyBit, r != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 return r;
443}
444
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000445SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000447 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000448 fDirtyBits = set_mask(fDirtyBits, kLooper_DirtyBit, looper != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 return looper;
450}
451
reed@google.com15356a62011-11-03 19:29:08 +0000452SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
453 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
454 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000455 fDirtyBits = set_mask(fDirtyBits, kImageFilter_DirtyBit, imageFilter != NULL);
reed@google.com15356a62011-11-03 19:29:08 +0000456 return imageFilter;
457}
458
reed@google.comb0a34d82012-07-11 19:57:55 +0000459SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
460 SkRefCnt_SafeAssign(fAnnotation, annotation);
461 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +0000462 fDirtyBits = set_mask(fDirtyBits, kAnnotation_DirtyBit, annotation != NULL);
reed@google.comb0a34d82012-07-11 19:57:55 +0000463 return annotation;
464}
465
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466///////////////////////////////////////////////////////////////////////////////
467
reed@google.comed43dff2013-06-04 16:56:27 +0000468static SkScalar mag2(SkScalar x, SkScalar y) {
469 return x * x + y * y;
470}
471
472static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
473 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
474 ||
475 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
476}
477
478bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
479 SkASSERT(!ctm.hasPerspective());
480 SkASSERT(!textM.hasPerspective());
481
482 SkMatrix matrix;
483 matrix.setConcat(ctm, textM);
484 return tooBig(matrix, MaxCacheSize2());
485}
486
reed@google.comed43dff2013-06-04 16:56:27 +0000487
488///////////////////////////////////////////////////////////////////////////////
489
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490#include "SkGlyphCache.h"
491#include "SkUtils.h"
492
reed@google.com90808e82013-03-19 14:44:54 +0000493static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
494 void* context) {
495 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000496}
497
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
499 uint16_t glyphs[]) const {
500 if (byteLength == 0) {
501 return 0;
502 }
reed@google.com72cf4922011-01-04 19:58:20 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 SkASSERT(textData != NULL);
505
506 if (NULL == glyphs) {
507 switch (this->getTextEncoding()) {
508 case kUTF8_TextEncoding:
509 return SkUTF8_CountUnichars((const char*)textData, byteLength);
510 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000511 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000512 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000513 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000515 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000517 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 }
519 return 0;
520 }
reed@google.com72cf4922011-01-04 19:58:20 +0000521
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 // handle this encoding before the setup for the glyphcache
525 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
526 // we want to ignore the low bit of byteLength
527 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000528 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 }
reed@google.com72cf4922011-01-04 19:58:20 +0000530
bungeman@google.com532470f2013-01-22 19:25:14 +0000531 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 SkGlyphCache* cache = autoCache.getCache();
533
534 const char* text = (const char*)textData;
535 const char* stop = text + byteLength;
536 uint16_t* gptr = glyphs;
537
538 switch (this->getTextEncoding()) {
539 case SkPaint::kUTF8_TextEncoding:
540 while (text < stop) {
541 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
542 }
543 break;
544 case SkPaint::kUTF16_TextEncoding: {
545 const uint16_t* text16 = (const uint16_t*)text;
546 const uint16_t* stop16 = (const uint16_t*)stop;
547 while (text16 < stop16) {
548 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
549 }
550 break;
551 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000552 case kUTF32_TextEncoding: {
553 const int32_t* text32 = (const int32_t*)text;
554 const int32_t* stop32 = (const int32_t*)stop;
555 while (text32 < stop32) {
556 *gptr++ = cache->unicharToGlyph(*text32++);
557 }
558 break;
559 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000561 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000563 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564}
565
reed@android.coma5dcaf62010-02-05 17:12:32 +0000566bool SkPaint::containsText(const void* textData, size_t byteLength) const {
567 if (0 == byteLength) {
568 return true;
569 }
reed@google.com72cf4922011-01-04 19:58:20 +0000570
reed@android.coma5dcaf62010-02-05 17:12:32 +0000571 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000572
reed@android.coma5dcaf62010-02-05 17:12:32 +0000573 // handle this encoding before the setup for the glyphcache
574 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
575 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
576 size_t count = byteLength >> 1;
577 for (size_t i = 0; i < count; i++) {
578 if (0 == glyphID[i]) {
579 return false;
580 }
581 }
582 return true;
583 }
reed@google.com72cf4922011-01-04 19:58:20 +0000584
bungeman@google.com532470f2013-01-22 19:25:14 +0000585 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000586 SkGlyphCache* cache = autoCache.getCache();
587
588 switch (this->getTextEncoding()) {
589 case SkPaint::kUTF8_TextEncoding: {
590 const char* text = static_cast<const char*>(textData);
591 const char* stop = text + byteLength;
592 while (text < stop) {
593 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
594 return false;
595 }
596 }
597 break;
598 }
599 case SkPaint::kUTF16_TextEncoding: {
600 const uint16_t* text = static_cast<const uint16_t*>(textData);
601 const uint16_t* stop = text + (byteLength >> 1);
602 while (text < stop) {
603 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
604 return false;
605 }
606 }
607 break;
608 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000609 case SkPaint::kUTF32_TextEncoding: {
610 const int32_t* text = static_cast<const int32_t*>(textData);
611 const int32_t* stop = text + (byteLength >> 2);
612 while (text < stop) {
613 if (0 == cache->unicharToGlyph(*text++)) {
614 return false;
615 }
616 }
617 break;
618 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000619 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000620 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000621 return false;
622 }
623 return true;
624}
625
reed@android.com9d3a9852010-01-08 14:07:42 +0000626void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
627 SkUnichar textData[]) const {
628 if (count <= 0) {
629 return;
630 }
631
632 SkASSERT(glyphs != NULL);
633 SkASSERT(textData != NULL);
634
bungeman@google.com532470f2013-01-22 19:25:14 +0000635 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000636 SkGlyphCache* cache = autoCache.getCache();
637
638 for (int index = 0; index < count; index++) {
639 textData[index] = cache->glyphToUnichar(glyphs[index]);
640 }
641}
642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643///////////////////////////////////////////////////////////////////////////////
644
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000645static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
646 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 SkASSERT(cache != NULL);
648 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
651}
652
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000653static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
654 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 SkASSERT(cache != NULL);
656 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
659}
660
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000661static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
662 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 SkASSERT(cache != NULL);
664 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
667}
668
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000669static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
670 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkASSERT(cache != NULL);
672 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
675}
676
reed@google.com68bc6f72012-03-14 19:41:55 +0000677static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
678 const char** text) {
679 SkASSERT(cache != NULL);
680 SkASSERT(text != NULL);
681
682 const int32_t* ptr = *(const int32_t**)text;
683 SkUnichar uni = *ptr++;
684 *text = (const char*)ptr;
685 return cache->getUnicharMetrics(uni);
686}
687
688static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
689 const char** text) {
690 SkASSERT(cache != NULL);
691 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000692
reed@google.com68bc6f72012-03-14 19:41:55 +0000693 const int32_t* ptr = *(const int32_t**)text;
694 SkUnichar uni = *--ptr;
695 *text = (const char*)ptr;
696 return cache->getUnicharMetrics(uni);
697}
698
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000699static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
700 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 SkASSERT(cache != NULL);
702 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 const uint16_t* ptr = *(const uint16_t**)text;
705 unsigned glyphID = *ptr;
706 ptr += 1;
707 *text = (const char*)ptr;
708 return cache->getGlyphIDMetrics(glyphID);
709}
710
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000711static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
712 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 SkASSERT(cache != NULL);
714 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 const uint16_t* ptr = *(const uint16_t**)text;
717 ptr -= 1;
718 unsigned glyphID = *ptr;
719 *text = (const char*)ptr;
720 return cache->getGlyphIDMetrics(glyphID);
721}
722
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000723static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
724 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 SkASSERT(cache != NULL);
726 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
729}
730
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000731static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
732 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 SkASSERT(cache != NULL);
734 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000735
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
737}
738
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000739static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
740 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 SkASSERT(cache != NULL);
742 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000743
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
745}
746
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000747static const SkGlyph& sk_getAdvance_utf16_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 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
753}
754
reed@google.com68bc6f72012-03-14 19:41:55 +0000755static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
756 const char** text) {
757 SkASSERT(cache != NULL);
758 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000759
reed@google.com68bc6f72012-03-14 19:41:55 +0000760 const int32_t* ptr = *(const int32_t**)text;
761 SkUnichar uni = *ptr++;
762 *text = (const char*)ptr;
763 return cache->getUnicharAdvance(uni);
764}
765
766static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
767 const char** text) {
768 SkASSERT(cache != NULL);
769 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000770
reed@google.com68bc6f72012-03-14 19:41:55 +0000771 const int32_t* ptr = *(const int32_t**)text;
772 SkUnichar uni = *--ptr;
773 *text = (const char*)ptr;
774 return cache->getUnicharAdvance(uni);
775}
776
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000777static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
778 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 SkASSERT(cache != NULL);
780 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 const uint16_t* ptr = *(const uint16_t**)text;
783 unsigned glyphID = *ptr;
784 ptr += 1;
785 *text = (const char*)ptr;
786 return cache->getGlyphIDAdvance(glyphID);
787}
788
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000789static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
790 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 SkASSERT(cache != NULL);
792 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 const uint16_t* ptr = *(const uint16_t**)text;
795 ptr -= 1;
796 unsigned glyphID = *ptr;
797 *text = (const char*)ptr;
798 return cache->getGlyphIDAdvance(glyphID);
799}
800
801SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000802 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
804 sk_getMetrics_utf8_next,
805 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000806 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 sk_getMetrics_utf8_prev,
810 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000811 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 sk_getAdvance_utf8_next,
815 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000816 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 sk_getAdvance_utf8_prev,
820 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000821 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 sk_getAdvance_glyph_prev
823 };
reed@google.com72cf4922011-01-04 19:58:20 +0000824
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 unsigned index = this->getTextEncoding();
826
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000827 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000828 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000829 }
830 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000831 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000832 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833
834 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
835 return gMeasureCacheProcs[index];
836}
837
838///////////////////////////////////////////////////////////////////////////////
839
840static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000841 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 SkASSERT(cache != NULL);
843 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000844
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
846}
847
848static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000849 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 SkASSERT(cache != NULL);
851 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000852
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
854}
855
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000856static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
857 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 SkASSERT(cache != NULL);
859 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000860
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
862}
863
864static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000865 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 SkASSERT(cache != NULL);
867 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000868
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
870 x, y);
871}
872
reed@google.com68bc6f72012-03-14 19:41:55 +0000873static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
874 const char** text, SkFixed, SkFixed) {
875 SkASSERT(cache != NULL);
876 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000877
reed@google.com68bc6f72012-03-14 19:41:55 +0000878 const int32_t* ptr = *(const int32_t**)text;
879 SkUnichar uni = *ptr++;
880 *text = (const char*)ptr;
881 return cache->getUnicharMetrics(uni);
882}
883
884static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
885 const char** text, SkFixed x, SkFixed y) {
886 SkASSERT(cache != NULL);
887 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000888
reed@google.com68bc6f72012-03-14 19:41:55 +0000889 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000890 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000891 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000892 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000893}
894
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000895static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
896 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 SkASSERT(cache != NULL);
898 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000899
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 const uint16_t* ptr = *(const uint16_t**)text;
901 unsigned glyphID = *ptr;
902 ptr += 1;
903 *text = (const char*)ptr;
904 return cache->getGlyphIDMetrics(glyphID);
905}
906
907static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000908 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 SkASSERT(cache != NULL);
910 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 const uint16_t* ptr = *(const uint16_t**)text;
913 unsigned glyphID = *ptr;
914 ptr += 1;
915 *text = (const char*)ptr;
916 return cache->getGlyphIDMetrics(glyphID, x, y);
917}
918
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000919SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 static const SkDrawCacheProc gDrawCacheProcs[] = {
921 sk_getMetrics_utf8_00,
922 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000923 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000925
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 sk_getMetrics_utf8_xy,
927 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000928 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 sk_getMetrics_glyph_xy
930 };
reed@google.com72cf4922011-01-04 19:58:20 +0000931
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 unsigned index = this->getTextEncoding();
reedf59eab22014-07-14 14:39:15 -0700933 if (fBitfields.fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000934 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000935 }
reed@google.com72cf4922011-01-04 19:58:20 +0000936
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
938 return gDrawCacheProcs[index];
939}
940
941///////////////////////////////////////////////////////////////////////////////
942
reed@google.comed43dff2013-06-04 16:56:27 +0000943#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
944SkPaint::kDevKernText_Flag | \
945SkPaint::kLinearText_Flag | \
946SkPaint::kLCDRenderText_Flag | \
947SkPaint::kEmbeddedBitmapText_Flag | \
948SkPaint::kAutoHinting_Flag | \
949SkPaint::kGenA8FromLCD_Flag )
950
951SkScalar SkPaint::setupForAsPaths() {
952 uint32_t flags = this->getFlags();
953 // clear the flags we don't care about
954 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
955 // set the flags we do care about
956 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000957
reed@google.comed43dff2013-06-04 16:56:27 +0000958 this->setFlags(flags);
959 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000960
reed@google.comed43dff2013-06-04 16:56:27 +0000961 SkScalar textSize = fTextSize;
962 this->setTextSize(kCanonicalTextSizeForPaths);
963 return textSize / kCanonicalTextSizeForPaths;
964}
965
966class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967public:
reed@google.comed43dff2013-06-04 16:56:27 +0000968 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700969 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000970 SkPaint* p = fLazy.set(paint);
971 fScale = p->setupForAsPaths();
972 fPaint = p;
973 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000975
reed@google.comed43dff2013-06-04 16:56:27 +0000976 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000977
reed@google.comed43dff2013-06-04 16:56:27 +0000978 /**
979 * Returns 0 if the paint was unmodified, or the scale factor need to
980 * the original textSize
981 */
982 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000983
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984private:
reed@google.comed43dff2013-06-04 16:56:27 +0000985 const SkPaint* fPaint;
986 SkScalar fScale;
987 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988};
989
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000990static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 bounds->set(SkIntToScalar(g.fLeft),
992 SkIntToScalar(g.fTop),
993 SkIntToScalar(g.fLeft + g.fWidth),
994 SkIntToScalar(g.fTop + g.fHeight));
995}
996
reed@android.come88f5512010-03-19 14:42:28 +0000997// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
998// we don't overflow along the way
999typedef int64_t Sk48Dot16;
1000
reed@google.com8f4d2302013-12-17 16:44:46 +00001001static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
1002 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
1003}
reed@android.come88f5512010-03-19 14:42:28 +00001004
reed@google.com44da42e2011-11-10 20:04:47 +00001005static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +00001006 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 bounds->join(SkIntToScalar(g.fLeft) + sx,
1008 SkIntToScalar(g.fTop),
1009 SkIntToScalar(g.fLeft + g.fWidth) + sx,
1010 SkIntToScalar(g.fTop + g.fHeight));
1011}
1012
reed@google.com44da42e2011-11-10 20:04:47 +00001013static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
1014 SkScalar sy = Sk48Dot16ToScalar(dy);
1015 bounds->join(SkIntToScalar(g.fLeft),
1016 SkIntToScalar(g.fTop) + sy,
1017 SkIntToScalar(g.fLeft + g.fWidth),
1018 SkIntToScalar(g.fTop + g.fHeight) + sy);
1019}
1020
1021typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
1022
1023// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
1024static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
1025 SkASSERT(0 == xyIndex || 1 == xyIndex);
1026 return (&glyph.fAdvanceX)[xyIndex];
1027}
1028
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029SkScalar SkPaint::measure_text(SkGlyphCache* cache,
1030 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001031 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001033 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001035 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001037 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 return 0;
1039 }
1040
1041 SkMeasureCacheProc glyphCacheProc;
1042 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
bsalomon49f085d2014-09-05 13:34:00 -07001043 bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044
reed@google.com44da42e2011-11-10 20:04:47 +00001045 int xyIndex;
1046 JoinBoundsProc joinBoundsProc;
1047 if (this->isVerticalText()) {
1048 xyIndex = 1;
1049 joinBoundsProc = join_bounds_y;
1050 } else {
1051 xyIndex = 0;
1052 joinBoundsProc = join_bounds_x;
1053 }
1054
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 int n = 1;
1056 const char* stop = (const char*)text + byteLength;
1057 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001058 // our accumulated fixed-point advances might overflow 16.16, so we use
1059 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1060 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001061 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062
1063 SkAutoKern autokern;
1064
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001065 if (NULL == bounds) {
1066 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 int rsb;
1068 for (; text < stop; n++) {
1069 rsb = g->fRsbDelta;
1070 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001071 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001073 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001075 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 }
1077 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001078 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001080 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 int rsb;
1082 for (; text < stop; n++) {
1083 rsb = g->fRsbDelta;
1084 g = &glyphCacheProc(cache, &text);
1085 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001086 joinBoundsProc(*g, bounds, x);
1087 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001089 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 for (; text < stop; n++) {
1091 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001092 joinBoundsProc(*g, bounds, x);
1093 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 }
1095 }
1096 }
1097 SkASSERT(text == stop);
1098
1099 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001100 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101}
1102
reed99ae8812014-08-26 11:30:01 -07001103SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 const char* text = (const char*)textData;
1105 SkASSERT(text != NULL || length == 0);
1106
reed@google.comed43dff2013-06-04 16:56:27 +00001107 SkCanonicalizePaint canon(*this);
1108 const SkPaint& paint = canon.getPaint();
1109 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001110
reed99ae8812014-08-26 11:30:01 -07001111 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 SkGlyphCache* cache = autoCache.getCache();
1113
1114 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001115
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001116 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 int tempCount;
1118
reed@google.comed43dff2013-06-04 16:56:27 +00001119 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001120 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001122 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1124 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1125 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1126 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1127 }
1128 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001129 } else if (bounds) {
1130 // ensure that even if we don't measure_text we still update the bounds
1131 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133 return width;
1134}
1135
1136typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1137
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001138static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 return text < stop;
1140}
1141
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001142static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 return text > stop;
1144}
1145
1146static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001147 const char** text, size_t length,
1148 const char** stop) {
1149 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 *stop = *text + length;
1151 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001152 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 // text should point to the end of the buffer, and stop to the beginning
1154 *stop = *text;
1155 *text += length;
1156 return backward_textBufferPred;
1157 }
1158}
1159
1160size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1161 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001162 TextBufferDirection tbd) const {
1163 if (0 == length || 0 >= maxWidth) {
1164 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001166 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 return 0;
1168 }
1169
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001170 if (0 == fTextSize) {
1171 if (measuredWidth) {
1172 *measuredWidth = 0;
1173 }
1174 return length;
1175 }
1176
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 SkASSERT(textD != NULL);
1178 const char* text = (const char*)textD;
1179
reed@google.comed43dff2013-06-04 16:56:27 +00001180 SkCanonicalizePaint canon(*this);
1181 const SkPaint& paint = canon.getPaint();
1182 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183
reed@google.comed43dff2013-06-04 16:56:27 +00001184 // adjust max in case we changed the textSize in paint
1185 if (scale) {
1186 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 }
reed@google.com72cf4922011-01-04 19:58:20 +00001188
reed@google.comed43dff2013-06-04 16:56:27 +00001189 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 SkGlyphCache* cache = autoCache.getCache();
1191
reed@google.comed43dff2013-06-04 16:56:27 +00001192 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 const char* stop;
1194 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001195 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001196 // use 64bits for our accumulator, to avoid overflowing 16.16
1197 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1198 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199
1200 SkAutoKern autokern;
1201
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001202 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001204 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 const char* curr = text;
1206 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001207 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001208 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 width -= x;
1210 text = curr;
1211 break;
1212 }
1213 rsb = g.fRsbDelta;
1214 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001215 } else {
1216 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001218 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001219 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 width -= x;
1221 text = curr;
1222 break;
1223 }
1224 }
1225 }
reed@google.com72cf4922011-01-04 19:58:20 +00001226
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001227 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001228 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001229 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001231 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 *measuredWidth = scalarWidth;
1233 }
reed@google.com72cf4922011-01-04 19:58:20 +00001234
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 // return the number of bytes measured
1236 return (kForward_TextBufferDirection == tbd) ?
1237 text - stop + length : stop - text + length;
1238}
1239
1240///////////////////////////////////////////////////////////////////////////////
1241
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001242static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001243 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 return false; // don't detach the cache
1245}
1246
reed@google.com90808e82013-03-19 14:44:54 +00001247static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1248 void* context) {
1249 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250}
1251
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001252SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001253 SkCanonicalizePaint canon(*this);
1254 const SkPaint& paint = canon.getPaint();
1255 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001256
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001257 SkMatrix zoomMatrix, *zoomPtr = NULL;
1258 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259 zoomMatrix.setScale(zoom, zoom);
1260 zoomPtr = &zoomMatrix;
1261 }
1262
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001264 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001266 }
reed@google.com72cf4922011-01-04 19:58:20 +00001267
reed@google.comed43dff2013-06-04 16:56:27 +00001268 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001270 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1272 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1273 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1274 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1275 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001276 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1277 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1278 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1279 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001280 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1281 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 }
1283 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1284}
1285
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001286///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001288static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 bounds->set(g.fLeft * scale,
1290 g.fTop * scale,
1291 (g.fLeft + g.fWidth) * scale,
1292 (g.fTop + g.fHeight) * scale);
1293}
1294
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001295int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1296 SkScalar widths[], SkRect bounds[]) const {
1297 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300
bsalomon49f085d2014-09-05 13:34:00 -07001301 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001303 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306
reed@google.comed43dff2013-06-04 16:56:27 +00001307 SkCanonicalizePaint canon(*this);
1308 const SkPaint& paint = canon.getPaint();
1309 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310
reed@google.comed43dff2013-06-04 16:56:27 +00001311 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 SkGlyphCache* cache = autoCache.getCache();
1313 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001314 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
bsalomon49f085d2014-09-05 13:34:00 -07001315 bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316
1317 const char* text = (const char*)textData;
1318 const char* stop = text + byteLength;
1319 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001320 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001322 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 // we adjust the widths returned here through auto-kerning
1324 SkAutoKern autokern;
1325 SkFixed prevWidth = 0;
1326
1327 if (scale) {
1328 while (text < stop) {
1329 const SkGlyph& g = glyphCacheProc(cache, &text);
1330 if (widths) {
1331 SkFixed adjust = autokern.adjust(g);
1332
1333 if (count > 0) {
1334 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1335 *widths++ = SkScalarMul(w, scale);
1336 }
reed@google.com44da42e2011-11-10 20:04:47 +00001337 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 }
1339 if (bounds) {
1340 set_bounds(g, bounds++, scale);
1341 }
1342 ++count;
1343 }
1344 if (count > 0 && widths) {
1345 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1346 }
1347 } else {
1348 while (text < stop) {
1349 const SkGlyph& g = glyphCacheProc(cache, &text);
1350 if (widths) {
1351 SkFixed adjust = autokern.adjust(g);
1352
1353 if (count > 0) {
1354 *widths++ = SkFixedToScalar(prevWidth + adjust);
1355 }
reed@google.com44da42e2011-11-10 20:04:47 +00001356 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 }
1358 if (bounds) {
1359 set_bounds(g, bounds++);
1360 }
1361 ++count;
1362 }
1363 if (count > 0 && widths) {
1364 *widths = SkFixedToScalar(prevWidth);
1365 }
1366 }
1367 } else { // no devkern
1368 if (scale) {
1369 while (text < stop) {
1370 const SkGlyph& g = glyphCacheProc(cache, &text);
1371 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001372 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 scale);
1374 }
1375 if (bounds) {
1376 set_bounds(g, bounds++, scale);
1377 }
1378 ++count;
1379 }
1380 } else {
1381 while (text < stop) {
1382 const SkGlyph& g = glyphCacheProc(cache, &text);
1383 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001384 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385 }
1386 if (bounds) {
1387 set_bounds(g, bounds++);
1388 }
1389 ++count;
1390 }
1391 }
1392 }
1393
1394 SkASSERT(text == stop);
1395 return count;
1396}
1397
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001398///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399
1400#include "SkDraw.h"
1401
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001402void SkPaint::getTextPath(const void* textData, size_t length,
1403 SkScalar x, SkScalar y, SkPath* path) const {
1404 SkASSERT(length == 0 || textData != NULL);
1405
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001407 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001409 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
djsollen@google.com166e6532012-03-20 14:24:38 +00001411 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 SkMatrix matrix;
1413 SkScalar prevXPos = 0;
1414
1415 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1416 matrix.postTranslate(x, y);
1417 path->reset();
1418
1419 SkScalar xpos;
1420 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001421 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001423 if (iterPath) {
1424 path->addPath(*iterPath, matrix);
1425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 prevXPos = xpos;
1427 }
1428}
1429
reed@google.comca0062e2012-07-20 11:20:32 +00001430void SkPaint::getPosTextPath(const void* textData, size_t length,
1431 const SkPoint pos[], SkPath* path) const {
1432 SkASSERT(length == 0 || textData != NULL);
1433
1434 const char* text = (const char*)textData;
1435 if (text == NULL || length == 0 || path == NULL) {
1436 return;
1437 }
1438
1439 SkTextToPathIter iter(text, length, *this, false);
1440 SkMatrix matrix;
1441 SkPoint prevPos;
1442 prevPos.set(0, 0);
1443
1444 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1445 path->reset();
1446
1447 unsigned int i = 0;
1448 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001449 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001450 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001451 if (iterPath) {
1452 path->addPath(*iterPath, matrix);
1453 }
reed@google.comca0062e2012-07-20 11:20:32 +00001454 prevPos = pos[i];
1455 i++;
1456 }
1457}
1458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001460 SkWriteBuffer* buffer) {
1461 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
reed@google.com2739b272011-09-28 17:26:42 +00001464// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001465static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001468 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001469 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001470 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001471 }
1472
reed@google.com65dd8f82011-03-14 13:31:16 +00001473 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001474 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001475 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001476
1477 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
reed@android.com1cdcb512009-08-24 19:11:00 +00001480// if linear-text is on, then we force hinting to be off (since that's sort of
1481// the point of linear-text.
1482static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1483 SkPaint::Hinting h = paint.getHinting();
1484 if (paint.isLinearText()) {
1485 h = SkPaint::kNo_Hinting;
1486 }
1487 return h;
1488}
1489
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001490// return true if the paint is just a single color (i.e. not a shader). If its
1491// a shader, then we can't compute a const luminance for it :(
1492static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001493 SkColor c = paint.getColor();
1494
1495 SkShader* shader = paint.getShader();
1496 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001497 return false;
1498 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001499 if (paint.getColorFilter()) {
1500 c = paint.getColorFilter()->filterColor(c);
1501 }
1502 if (color) {
1503 *color = c;
1504 }
1505 return true;
1506}
1507
reed@google.comce6dbb62012-02-10 22:01:45 +00001508static SkColor computeLuminanceColor(const SkPaint& paint) {
1509 SkColor c;
1510 if (!justAColor(paint, &c)) {
1511 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1512 }
1513 return c;
1514}
reed@google.com813d38b2012-02-13 21:37:57 +00001515
reed@google.comdd43df92012-02-15 14:50:29 +00001516#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1517
reed@google.com4f79b9b2011-09-13 13:23:26 +00001518// Beyond this size, LCD doesn't appreciably improve quality, but it always
1519// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001520#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1521 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1522#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001523
1524static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
commit-bot@chromium.orge78f7cf2014-03-14 22:59:05 +00001525 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1526 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
1527 SkScalar size = SkScalarSqrt(SkScalarAbs(area)) * rec.fTextSize;
1528 return size > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001529}
1530
reed@google.com72cf4922011-01-04 19:58:20 +00001531/*
1532 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1533 * that vary only slightly when we create our key into the font cache, since the font scaler
1534 * typically returns the same looking resuts for tiny changes in the matrix.
1535 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001536static SkScalar sk_relax(SkScalar x) {
1537 int n = sk_float_round2int(x * 1024);
1538 return n / 1024.0f;
1539}
1540
reed@android.com36a4c2a2009-07-22 19:52:11 +00001541void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001542 const SkDeviceProperties* deviceProperties,
1543 const SkMatrix* deviceMatrix,
1544 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001545 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001547 SkTypeface* typeface = paint.getTypeface();
1548 if (NULL == typeface) {
1549 typeface = SkTypeface::GetDefaultTypeface();
1550 }
bungemanec730b92014-08-18 07:57:35 -07001551 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552 rec->fTextSize = paint.getTextSize();
1553 rec->fPreScaleX = paint.getTextScaleX();
1554 rec->fPreSkewX = paint.getTextSkewX();
1555
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001556 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001557 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1558 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1559 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1560 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001561 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1563 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1564 }
reed@google.com72cf4922011-01-04 19:58:20 +00001565
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 SkPaint::Style style = paint.getStyle();
1567 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001568
reed@google.comffe49f52011-11-22 19:42:41 +00001569 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001570
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001571 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001572#ifdef SK_USE_FREETYPE_EMBOLDEN
1573 flags |= SkScalerContext::kEmbolden_Flag;
1574#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001575 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1576 kStdFakeBoldInterpKeys,
1577 kStdFakeBoldInterpValues,
1578 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001580
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001581 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 style = SkPaint::kStrokeAndFill_Style;
1583 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001584 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001586 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001587#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 }
1589
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001590 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001592 }
reed@google.com72cf4922011-01-04 19:58:20 +00001593
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001594 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595 rec->fFrameWidth = strokeWidth;
1596 rec->fMiterLimit = paint.getStrokeMiter();
1597 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1598
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001599 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001601 }
1602 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 rec->fFrameWidth = 0;
1604 rec->fMiterLimit = 0;
1605 rec->fStrokeJoin = 0;
1606 }
1607
reed@google.com02b53312011-05-18 19:00:53 +00001608 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1609
bungeman@google.com532470f2013-01-22 19:25:14 +00001610 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
reed3716fd02014-09-21 09:39:55 -07001611 if (tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001612 rec->fMaskFormat = SkMask::kA8_Format;
reed3716fd02014-09-21 09:39:55 -07001613 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001614 } else {
reed3716fd02014-09-21 09:39:55 -07001615 SkPixelGeometry geometry = deviceProperties
1616 ? deviceProperties->fPixelGeometry
1617 : SkSurfacePropsDefaultPixelGeometry();
1618 switch (geometry) {
1619 case kUnknown_SkPixelGeometry:
1620 // eeek, can't support LCD
1621 rec->fMaskFormat = SkMask::kA8_Format;
1622 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1623 break;
1624 case kRGB_H_SkPixelGeometry:
1625 // our default, do nothing.
1626 break;
1627 case kBGR_H_SkPixelGeometry:
1628 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1629 break;
1630 case kRGB_V_SkPixelGeometry:
1631 flags |= SkScalerContext::kLCD_Vertical_Flag;
1632 break;
1633 case kBGR_V_SkPixelGeometry:
1634 flags |= SkScalerContext::kLCD_Vertical_Flag;
1635 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1636 break;
reed@google.com02b53312011-05-18 19:00:53 +00001637 }
1638 }
1639 }
1640
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001641 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001642 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001643 }
1644 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001645 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001646 }
1647 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001648 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001649 }
reed@google.com830a23e2011-11-10 15:20:49 +00001650 if (paint.isVerticalText()) {
1651 flags |= SkScalerContext::kVertical_Flag;
1652 }
reed@google.com8351aab2012-01-18 17:06:35 +00001653 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1654 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1655 }
reed@google.com02b53312011-05-18 19:00:53 +00001656 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001657
reed@google.comffe49f52011-11-22 19:42:41 +00001658 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001659 rec->setHinting(computeHinting(paint));
1660
bungeman@google.com97efada2012-07-30 20:40:50 +00001661 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001662
1663 if (NULL == deviceProperties) {
1664 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1665 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1666 } else {
reed3716fd02014-09-21 09:39:55 -07001667 rec->setDeviceGamma(deviceProperties->getGamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001668
1669 //For now always set the paint gamma equal to the device gamma.
1670 //The math in SkMaskGamma can handle them being different,
1671 //but it requires superluminous masks when
1672 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
reed3716fd02014-09-21 09:39:55 -07001673 rec->setPaintGamma(deviceProperties->getGamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001674 }
1675
1676#ifdef SK_GAMMA_CONTRAST
1677 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001678#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001679 /**
1680 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1681 * With lower values small text appears washed out (though correctly so).
1682 * With higher values lcd fringing is worse and the smoothing effect of
1683 * partial coverage is diminished.
1684 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001685 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001686#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001687
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001688 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001689
reed@android.com36a4c2a2009-07-22 19:52:11 +00001690 /* Allow the fonthost to modify our rec before we use it as a key into the
1691 cache. This way if we're asking for something that they will ignore,
1692 they can modify our rec up front, so we don't create duplicate cache
1693 entries.
1694 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001695 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001696
reed@google.com10d2d4d2012-03-01 22:32:51 +00001697 // be sure to call PostMakeRec(rec) before you actually use it!
1698}
1699
1700/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001701 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1702 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1703 * to hold it until the returned pointer is refed or forgotten.
1704 */
1705SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1706
bungeman@google.comae30f562012-09-11 18:44:55 +00001707static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001708static SkMaskGamma* gMaskGamma = NULL;
1709static SkScalar gContrast = SK_ScalarMin;
1710static SkScalar gPaintGamma = SK_ScalarMin;
1711static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001712/**
1713 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1714 * the returned SkMaskGamma pointer is refed or forgotten.
1715 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001716static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001717 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001718 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1719 if (NULL == gLinearMaskGamma) {
1720 gLinearMaskGamma = SkNEW(SkMaskGamma);
1721 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001722 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001723 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001724 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1725 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001726 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001727 gContrast = contrast;
1728 gPaintGamma = paintGamma;
1729 gDeviceGamma = deviceGamma;
1730 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001731 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001732}
1733
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001734/*static*/ void SkPaint::Term() {
1735 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1736
bungeman@google.comae30f562012-09-11 18:44:55 +00001737 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001738 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001739 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001740 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001741 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1742 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1743 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001744}
1745
bungeman@google.com97efada2012-07-30 20:40:50 +00001746/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001747 * We ensure that the rec is self-consistent and efficient (where possible)
1748 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001749void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001750 /**
1751 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001752 * limits the number of unique entries, and the scaler will only look at
1753 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001754 */
reed@google.comdd43df92012-02-15 14:50:29 +00001755 switch (rec->fMaskFormat) {
1756 case SkMask::kLCD16_Format:
1757 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001758 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001759 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001760 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001761 break;
1762 }
1763 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001764 // filter down the luminance to a single component, since A8 can't
1765 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001766 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001767 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1768 SkColorGetG(color),
1769 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001770 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001771 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001772 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001773 break;
1774 }
1775 case SkMask::kBW_Format:
1776 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001777 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001778 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780}
1781
1782#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1783
reed@google.com17fb3872011-05-04 14:31:07 +00001784#ifdef SK_DEBUG
1785 #define TEST_DESC
1786#endif
1787
reed@google.comffe49f52011-11-22 19:42:41 +00001788/*
1789 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001790 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1791 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001792 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001793void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1794 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001795 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001796 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 SkScalerContext::Rec rec;
1798
bungeman@google.com532470f2013-01-22 19:25:14 +00001799 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001800 if (ignoreGamma) {
jvanverth2d2a68c2014-06-10 06:42:56 -07001801 rec.ignorePreBlend();
djsollen@google.com57f49692011-02-23 20:46:31 +00001802 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803
1804 size_t descSize = sizeof(rec);
1805 int entryCount = 1;
1806 SkPathEffect* pe = this->getPathEffect();
1807 SkMaskFilter* mf = this->getMaskFilter();
1808 SkRasterizer* ra = this->getRasterizer();
1809
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001810 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811
1812 if (pe) {
1813 peBuffer.writeFlattenable(pe);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001814 descSize += peBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 entryCount += 1;
1816 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1817 // seems like we could support kLCD as well at this point...
1818 }
1819 if (mf) {
1820 mfBuffer.writeFlattenable(mf);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001821 descSize += mfBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 entryCount += 1;
1823 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001824 /* Pre-blend is not currently applied to filtered text.
1825 The primary filter is blur, for which contrast makes no sense,
1826 and for which the destination guess error is more visible.
1827 Also, all existing users of blur have calibrated for linear. */
1828 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 }
1830 if (ra) {
1831 raBuffer.writeFlattenable(ra);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001832 descSize += raBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 entryCount += 1;
1834 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1835 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001836
1837 ///////////////////////////////////////////////////////////////////////////
1838 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001839 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001840
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841 descSize += SkDescriptor::ComputeOverhead(entryCount);
1842
1843 SkAutoDescriptor ad(descSize);
1844 SkDescriptor* desc = ad.getDesc();
1845
1846 desc->init();
1847 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1848
1849 if (pe) {
1850 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1851 }
1852 if (mf) {
1853 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1854 }
1855 if (ra) {
1856 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1857 }
1858
1859 SkASSERT(descSize == desc->getLength());
1860 desc->computeChecksum();
1861
reed@google.com17fb3872011-05-04 14:31:07 +00001862#ifdef TEST_DESC
1863 {
1864 // Check that we completely write the bytes in desc (our key), and that
1865 // there are no uninitialized bytes. If there were, then we would get
1866 // false-misses (or worse, false-hits) in our fontcache.
1867 //
1868 // We do this buy filling 2 others, one with 0s and the other with 1s
1869 // and create those, and then check that all 3 are identical.
1870 SkAutoDescriptor ad1(descSize);
1871 SkAutoDescriptor ad2(descSize);
1872 SkDescriptor* desc1 = ad1.getDesc();
1873 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001874
reed@google.com17fb3872011-05-04 14:31:07 +00001875 memset(desc1, 0x00, descSize);
1876 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001877
reed@google.com17fb3872011-05-04 14:31:07 +00001878 desc1->init();
1879 desc2->init();
1880 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1881 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001882
reed@google.com17fb3872011-05-04 14:31:07 +00001883 if (pe) {
1884 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1885 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1886 }
1887 if (mf) {
1888 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1889 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1890 }
1891 if (ra) {
1892 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1893 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1894 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001895
reed@google.com17fb3872011-05-04 14:31:07 +00001896 SkASSERT(descSize == desc1->getLength());
1897 SkASSERT(descSize == desc2->getLength());
1898 desc1->computeChecksum();
1899 desc2->computeChecksum();
1900 SkASSERT(!memcmp(desc, desc1, descSize));
1901 SkASSERT(!memcmp(desc, desc2, descSize));
1902 }
1903#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001904
reed@google.com90808e82013-03-19 14:44:54 +00001905 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906}
1907
bungeman@google.com532470f2013-01-22 19:25:14 +00001908SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
jvanverth2d2a68c2014-06-10 06:42:56 -07001909 const SkMatrix* deviceMatrix,
1910 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911 SkGlyphCache* cache;
jvanverth2d2a68c2014-06-10 06:42:56 -07001912 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 return cache;
1914}
1915
bungeman@google.com97efada2012-07-30 20:40:50 +00001916/**
1917 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1918 */
1919//static
1920SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1921 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001922 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1923 rec.getPaintGamma(),
1924 rec.getDeviceGamma());
1925 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001926}
1927
jvanverth2d2a68c2014-06-10 06:42:56 -07001928size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1929 SkScalar deviceGamma, int* width, int* height) {
1930 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1931 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1932 paintGamma,
1933 deviceGamma);
1934
1935 maskGamma.getGammaTableDimensions(width, height);
1936 size_t size = (*width)*(*height)*sizeof(uint8_t);
1937
1938 return size;
1939}
1940
1941void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1942 void* data) {
1943 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1944 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1945 paintGamma,
1946 deviceGamma);
1947 int width, height;
1948 maskGamma.getGammaTableDimensions(&width, &height);
1949 size_t size = width*height*sizeof(uint8_t);
1950 const uint8_t* gammaTables = maskGamma.getGammaTables();
1951 memcpy(data, gammaTables, size);
1952}
1953
1954
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955///////////////////////////////////////////////////////////////////////////////
1956
1957#include "SkStream.h"
1958
reed@android.comaefd2bc2009-03-30 21:02:14 +00001959static uintptr_t asint(const void* p) {
1960 return reinterpret_cast<uintptr_t>(p);
1961}
1962
1963union Scalar32 {
1964 SkScalar fScalar;
1965 uint32_t f32;
1966};
1967
1968static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1969 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1970 Scalar32 tmp;
1971 tmp.fScalar = value;
1972 *ptr = tmp.f32;
1973 return ptr + 1;
1974}
1975
1976static SkScalar read_scalar(const uint32_t*& ptr) {
1977 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1978 Scalar32 tmp;
1979 tmp.f32 = *ptr++;
1980 return tmp.fScalar;
1981}
1982
1983static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1984 SkASSERT(a == (uint8_t)a);
1985 SkASSERT(b == (uint8_t)b);
1986 SkASSERT(c == (uint8_t)c);
1987 SkASSERT(d == (uint8_t)d);
1988 return (a << 24) | (b << 16) | (c << 8) | d;
1989}
1990
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001991#ifdef SK_DEBUG
1992 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1993 SkASSERT(bitCount > 0 && bitCount <= 32);
1994 uint32_t mask = ~0U;
1995 mask >>= (32 - bitCount);
1996 SkASSERT(0 == (value & ~mask));
1997 }
1998#else
1999 #define ASSERT_FITS_IN(value, bitcount)
2000#endif
2001
reed@android.comaefd2bc2009-03-30 21:02:14 +00002002enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002003 kHasTypeface_FlatFlag = 0x01,
2004 kHasEffects_FlatFlag = 0x02,
2005 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002006
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002007 kFlatFlagMask = 0x7,
reed@android.comaefd2bc2009-03-30 21:02:14 +00002008};
2009
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002010enum BitsPerField {
2011 kFlags_BPF = 16,
2012 kHint_BPF = 2,
2013 kAlign_BPF = 2,
2014 kFilter_BPF = 2,
2015 kFlatFlags_BPF = 3,
2016};
2017
2018static inline int BPF_Mask(int bits) {
2019 return (1 << bits) - 1;
2020}
2021
2022static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
2023 unsigned filter, unsigned flatFlags) {
2024 ASSERT_FITS_IN(flags, kFlags_BPF);
2025 ASSERT_FITS_IN(hint, kHint_BPF);
2026 ASSERT_FITS_IN(align, kAlign_BPF);
2027 ASSERT_FITS_IN(filter, kFilter_BPF);
2028 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
2029
2030 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
2031 // add more bits in the future.
2032 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
2033}
2034
2035static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
2036 paint->setFlags(packed >> 16);
2037 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
2038 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
2039 paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
2040 return (FlatFlags)(packed & kFlatFlagMask);
2041}
2042
2043// V22_COMPATIBILITY_CODE
2044static FlatFlags unpack_paint_flags_v22(SkPaint* paint, uint32_t packed) {
2045 enum {
2046 kFilterBitmap_Flag = 0x02,
2047 kHighQualityFilterBitmap_Flag = 0x4000,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002048
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002049 kAll_Flags = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag
2050 };
2051
2052 // previously flags:16, textAlign:8, flatFlags:8
2053 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2054 unsigned flags = packed >> 16;
2055 int filter = 0;
2056 if (flags & kFilterBitmap_Flag) {
2057 filter |= 1;
2058 }
2059 if (flags & kHighQualityFilterBitmap_Flag) {
2060 filter |= 2;
2061 }
2062 paint->setFilterLevel((SkPaint::FilterLevel)filter);
2063 flags &= ~kAll_Flags; // remove these (now dead) bit flags
2064
2065 paint->setFlags(flags);
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002066
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002067 // hinting added later. 0 in this nibble means use the default.
2068 uint32_t hinting = (packed >> 12) & 0xF;
2069 paint->setHinting(0 == hinting ? SkPaint::kNormal_Hinting : static_cast<SkPaint::Hinting>(hinting-1));
2070 paint->setTextAlign(static_cast<SkPaint::Align>((packed >> 8) & 0xF));
2071 return (FlatFlags)(packed & kFlatFlagMask);
2072}
2073
reed@android.comaefd2bc2009-03-30 21:02:14 +00002074// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00002075static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00002076 1 * sizeof(SkColor) +
2077 1 * sizeof(uint16_t) +
2078 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002079
2080/* To save space/time, we analyze the paint, and write a truncated version of
2081 it if there are not tricky elements like shaders, etc.
2082 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002083void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002084 uint8_t flatFlags = 0;
2085 if (this->getTypeface()) {
2086 flatFlags |= kHasTypeface_FlatFlag;
2087 }
2088 if (asint(this->getPathEffect()) |
2089 asint(this->getShader()) |
2090 asint(this->getXfermode()) |
2091 asint(this->getMaskFilter()) |
2092 asint(this->getColorFilter()) |
2093 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002094 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002095 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002096 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002097 flatFlags |= kHasEffects_FlatFlag;
2098 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002099
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002100 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2101 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002102
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002103 ptr = write_scalar(ptr, this->getTextSize());
2104 ptr = write_scalar(ptr, this->getTextScaleX());
2105 ptr = write_scalar(ptr, this->getTextSkewX());
2106 ptr = write_scalar(ptr, this->getStrokeWidth());
2107 ptr = write_scalar(ptr, this->getStrokeMiter());
2108 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002109
2110 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
2111 this->getFilterLevel(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002112 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2113 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002114
2115 // now we're done with ptr and the (pre)reserved space. If we need to write
2116 // additional fields, use the buffer directly
2117 if (flatFlags & kHasTypeface_FlatFlag) {
2118 buffer.writeTypeface(this->getTypeface());
2119 }
2120 if (flatFlags & kHasEffects_FlatFlag) {
2121 buffer.writeFlattenable(this->getPathEffect());
2122 buffer.writeFlattenable(this->getShader());
2123 buffer.writeFlattenable(this->getXfermode());
2124 buffer.writeFlattenable(this->getMaskFilter());
2125 buffer.writeFlattenable(this->getColorFilter());
2126 buffer.writeFlattenable(this->getRasterizer());
2127 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002128 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00002129
2130 if (fAnnotation) {
2131 buffer.writeBool(true);
2132 fAnnotation->writeToBuffer(buffer);
2133 } else {
2134 buffer.writeBool(false);
2135 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002136 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137}
2138
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002139void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002140 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2141 const void* podData = buffer.skip(kPODPaintSize);
2142 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002143
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002144 // the order we read must match the order we wrote in flatten()
2145 this->setTextSize(read_scalar(pod));
2146 this->setTextScaleX(read_scalar(pod));
2147 this->setTextSkewX(read_scalar(pod));
2148 this->setStrokeWidth(read_scalar(pod));
2149 this->setStrokeMiter(read_scalar(pod));
2150 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002151
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002152 unsigned flatFlags = 0;
commit-bot@chromium.org7ed173b2014-05-20 17:31:08 +00002153 if (buffer.isVersionLT(SkReadBuffer::kFilterLevelIsEnum_Version)) {
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002154 flatFlags = unpack_paint_flags_v22(this, *pod++);
2155 } else {
2156 flatFlags = unpack_paint_flags(this, *pod++);
2157 }
2158
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002159 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002160 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2161 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2162 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2163 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002164
2165 if (flatFlags & kHasTypeface_FlatFlag) {
2166 this->setTypeface(buffer.readTypeface());
2167 } else {
2168 this->setTypeface(NULL);
2169 }
2170
2171 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002172 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2173 SkSafeUnref(this->setShader(buffer.readShader()));
2174 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2175 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2176 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2177 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2178 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2179 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002180
reed@google.com0cd2ac62013-10-14 20:02:44 +00002181 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002182 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00002183 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002184 } else {
2185 this->setPathEffect(NULL);
2186 this->setShader(NULL);
2187 this->setXfermode(NULL);
2188 this->setMaskFilter(NULL);
2189 this->setColorFilter(NULL);
2190 this->setRasterizer(NULL);
2191 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002192 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002193 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002194
djsollen3b625542014-08-14 06:29:02 -07002195 if (buffer.isVersionLT(SkReadBuffer::kRemoveAndroidPaintOpts_Version) &&
2196 flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2197 SkString tag;
2198 buffer.readUInt();
2199 buffer.readString(&tag);
2200 buffer.readBool();
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002201 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202}
2203
2204///////////////////////////////////////////////////////////////////////////////
2205
reed@google.com82065d62011-02-07 15:30:46 +00002206SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002207 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 SkRefCnt_SafeAssign(fShader, shader);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002209 fDirtyBits = set_mask(fDirtyBits, kShader_DirtyBit, shader != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 return shader;
2211}
2212
reed@google.com82065d62011-02-07 15:30:46 +00002213SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002214 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 SkRefCnt_SafeAssign(fColorFilter, filter);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002216 fDirtyBits = set_mask(fDirtyBits, kColorFilter_DirtyBit, filter != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217 return filter;
2218}
2219
reed@google.com82065d62011-02-07 15:30:46 +00002220SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002221 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 SkRefCnt_SafeAssign(fXfermode, mode);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002223 fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, mode != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 return mode;
2225}
2226
reed@android.com0baf1932009-06-24 12:41:42 +00002227SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002228 SkSafeUnref(fXfermode);
2229 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002230 GEN_ID_INC;
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002231 fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, fXfermode != NULL);
reed@android.comd66eef72009-06-24 12:29:16 +00002232 return fXfermode;
2233}
2234
reed@google.com82065d62011-02-07 15:30:46 +00002235SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002236 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 SkRefCnt_SafeAssign(fPathEffect, effect);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002238 fDirtyBits = set_mask(fDirtyBits, kPathEffect_DirtyBit, effect != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239 return effect;
2240}
2241
reed@google.com82065d62011-02-07 15:30:46 +00002242SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002243 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 SkRefCnt_SafeAssign(fMaskFilter, filter);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002245 fDirtyBits = set_mask(fDirtyBits, kMaskFilter_DirtyBit, filter != NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 return filter;
2247}
2248
reed@google.com82065d62011-02-07 15:30:46 +00002249///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250
reed@google.com4bbdeac2013-01-24 21:03:11 +00002251bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2252 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002253 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254
reed@google.comfd4be262012-05-25 01:04:12 +00002255 const SkPath* srcPtr = &src;
2256 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002257
reed@google.com4bbdeac2013-01-24 21:03:11 +00002258 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002259 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
2261
reed@google.comfd4be262012-05-25 01:04:12 +00002262 if (!rec.applyToPath(dst, *srcPtr)) {
2263 if (srcPtr == &tmpPath) {
2264 // If path's were copy-on-write, this trick would not be needed.
2265 // As it is, we want to save making a deep-copy from tmpPath -> dst
2266 // since we know we're just going to delete tmpPath when we return,
2267 // so the swap saves that copy.
2268 dst->swap(tmpPath);
2269 } else {
2270 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 }
2272 }
reed@google.comfd4be262012-05-25 01:04:12 +00002273 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002274}
2275
reed@google.come4f10a72012-05-15 20:47:50 +00002276const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002277 SkRect* storage,
2278 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002280
reed@google.come4f10a72012-05-15 20:47:50 +00002281 const SkRect* src = &origSrc;
2282
reed@google.com9efd9a02012-01-30 15:41:43 +00002283 if (this->getLooper()) {
2284 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002285 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002286 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002288
reed@google.come4f10a72012-05-15 20:47:50 +00002289 SkRect tmpSrc;
2290 if (this->getPathEffect()) {
2291 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2292 src = &tmpSrc;
2293 }
2294
reed@google.coma584aed2012-05-16 14:06:02 +00002295 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002296 // since we're stroked, outset the rect by the radius (and join type)
2297 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2298 if (0 == radius) { // hairline
2299 radius = SK_Scalar1;
2300 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2301 SkScalar scale = this->getStrokeMiter();
2302 if (scale > SK_Scalar1) {
2303 radius = SkScalarMul(radius, scale);
2304 }
2305 }
reed@google.come4f10a72012-05-15 20:47:50 +00002306 storage->set(src->fLeft - radius, src->fTop - radius,
2307 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002308 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002309 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002310 }
2311
reed@google.com9efd9a02012-01-30 15:41:43 +00002312 if (this->getMaskFilter()) {
2313 this->getMaskFilter()->computeFastBounds(*storage, storage);
2314 }
2315
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002316 if (this->getImageFilter()) {
2317 this->getImageFilter()->computeFastBounds(*storage, storage);
2318 }
2319
reed@android.comd252db02009-04-01 18:31:44 +00002320 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321}
2322
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002323#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002324
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002325void SkPaint::toString(SkString* str) const {
2326 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2327
2328 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002329 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002330 SkDynamicMemoryWStream ostream;
2331 typeface->serialize(&ostream);
2332 SkAutoTUnref<SkStreamAsset> istream(ostream.detachAsStream());
2333 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002334
2335 str->append("<dt>Font Family Name:</dt><dd>");
2336 str->append(descriptor.getFamilyName());
2337 str->append("</dd><dt>Font Full Name:</dt><dd>");
2338 str->append(descriptor.getFullName());
2339 str->append("</dd><dt>Font PS Name:</dt><dd>");
2340 str->append(descriptor.getPostscriptName());
2341 str->append("</dd><dt>Font File Name:</dt><dd>");
2342 str->append(descriptor.getFontFileName());
2343 str->append("</dd>");
2344 }
2345
2346 str->append("<dt>TextSize:</dt><dd>");
2347 str->appendScalar(this->getTextSize());
2348 str->append("</dd>");
2349
2350 str->append("<dt>TextScaleX:</dt><dd>");
2351 str->appendScalar(this->getTextScaleX());
2352 str->append("</dd>");
2353
2354 str->append("<dt>TextSkewX:</dt><dd>");
2355 str->appendScalar(this->getTextSkewX());
2356 str->append("</dd>");
2357
2358 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002359 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002360 str->append("<dt>PathEffect:</dt><dd>");
2361 str->append("</dd>");
2362 }
2363
2364 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002365 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002366 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002367 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002368 str->append("</dd>");
2369 }
2370
2371 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002372 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002373 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002374 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002375 str->append("</dd>");
2376 }
2377
2378 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002379 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002380 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002381 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002382 str->append("</dd>");
2383 }
2384
2385 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002386 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002387 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002388 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002389 str->append("</dd>");
2390 }
2391
2392 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002393 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002394 str->append("<dt>Rasterizer:</dt><dd>");
2395 str->append("</dd>");
2396 }
2397
2398 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002399 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002400 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002401 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002402 str->append("</dd>");
2403 }
2404
2405 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002406 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002407 str->append("<dt>ImageFilter:</dt><dd>");
2408 str->append("</dd>");
2409 }
2410
2411 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002412 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002413 str->append("<dt>Annotation:</dt><dd>");
2414 str->append("</dd>");
2415 }
2416
2417 str->append("<dt>Color:</dt><dd>0x");
2418 SkColor color = this->getColor();
2419 str->appendHex(color);
2420 str->append("</dd>");
2421
2422 str->append("<dt>Stroke Width:</dt><dd>");
2423 str->appendScalar(this->getStrokeWidth());
2424 str->append("</dd>");
2425
2426 str->append("<dt>Stroke Miter:</dt><dd>");
2427 str->appendScalar(this->getStrokeMiter());
2428 str->append("</dd>");
2429
2430 str->append("<dt>Flags:</dt><dd>(");
2431 if (this->getFlags()) {
2432 bool needSeparator = false;
2433 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002434 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2435 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2436 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2437 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2438 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2439 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2440 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2441 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2442 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2443 "EmbeddedBitmapText", &needSeparator);
2444 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2445 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2446 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2447 "GenA8FromLCD", &needSeparator);
2448 } else {
2449 str->append("None");
2450 }
2451 str->append(")</dd>");
2452
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002453 str->append("<dt>FilterLevel:</dt><dd>");
2454 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2455 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2456 str->append("</dd>");
2457
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002458 str->append("<dt>TextAlign:</dt><dd>");
2459 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2460 str->append(gTextAlignStrings[this->getTextAlign()]);
2461 str->append("</dd>");
2462
2463 str->append("<dt>CapType:</dt><dd>");
2464 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2465 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2466 str->append("</dd>");
2467
2468 str->append("<dt>JoinType:</dt><dd>");
2469 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2470 str->append(gJoinStrings[this->getStrokeJoin()]);
2471 str->append("</dd>");
2472
2473 str->append("<dt>Style:</dt><dd>");
2474 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2475 str->append(gStyleStrings[this->getStyle()]);
2476 str->append("</dd>");
2477
2478 str->append("<dt>TextEncoding:</dt><dd>");
2479 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2480 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2481 str->append("</dd>");
2482
2483 str->append("<dt>Hinting:</dt><dd>");
2484 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2485 str->append(gHintingStrings[this->getHinting()]);
2486 str->append("</dd>");
2487
2488 str->append("</dd></dl></dl>");
2489}
2490#endif
2491
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002492///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002493
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002494static bool has_thick_frame(const SkPaint& paint) {
2495 return paint.getStrokeWidth() > 0 &&
2496 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497}
2498
2499SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2500 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002501 bool applyStrokeAndPathEffects)
2502 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002503 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2504 true);
2505
djsollen@google.com166e6532012-03-20 14:24:38 +00002506 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002507 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2508
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002509 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002511 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002512
djsollen@google.com166e6532012-03-20 14:24:38 +00002513 // can't use our canonical size if we need to apply patheffects
2514 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002515 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2516 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002517 if (has_thick_frame(fPaint)) {
2518 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2519 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002520 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002522 }
reed@google.com72cf4922011-01-04 19:58:20 +00002523
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002524 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 fPaint.setStyle(SkPaint::kFill_Style);
2526 fPaint.setPathEffect(NULL);
2527 }
2528
jvanverth2d2a68c2014-06-10 06:42:56 -07002529 fCache = fPaint.detachCache(NULL, NULL, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002530
2531 SkPaint::Style style = SkPaint::kFill_Style;
2532 SkPathEffect* pe = NULL;
2533
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002534 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535 style = paint.getStyle(); // restore
2536 pe = paint.getPathEffect(); // restore
2537 }
2538 fPaint.setStyle(style);
2539 fPaint.setPathEffect(pe);
2540 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2541
2542 // now compute fXOffset if needed
2543
2544 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002545 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002546 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002547 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2548 &count, NULL), fScale);
2549 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002550 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002551 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552 xOffset = -width;
2553 }
2554 fXPos = xOffset;
2555 fPrevAdvance = 0;
2556
2557 fText = text;
2558 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002559
reed@google.com44da42e2011-11-10 20:04:47 +00002560 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561}
2562
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002563SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564 SkGlyphCache::AttachCache(fCache);
2565}
2566
reed@google.com7b4531f2012-08-07 15:53:00 +00002567bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2568 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2570
2571 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002572 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002573
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002574 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002575 if (path) {
2576 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002577 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002578 } else {
2579 if (path) {
2580 *path = NULL;
2581 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002582 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002583 if (xpos) {
2584 *xpos = fXPos;
2585 }
2586 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002588 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002589}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002590
2591///////////////////////////////////////////////////////////////////////////////
2592
2593bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002594 if (fLooper) {
2595 return false;
2596 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002597 SkXfermode::Mode mode;
2598 if (SkXfermode::AsMode(fXfermode, &mode)) {
2599 switch (mode) {
2600 case SkXfermode::kSrcOver_Mode:
2601 case SkXfermode::kSrcATop_Mode:
2602 case SkXfermode::kDstOut_Mode:
2603 case SkXfermode::kDstOver_Mode:
2604 case SkXfermode::kPlus_Mode:
2605 return 0 == this->getAlpha();
2606 case SkXfermode::kDst_Mode:
2607 return true;
2608 default:
2609 break;
2610 }
2611 }
2612 return false;
2613}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002614
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002615inline static unsigned popcount(uint8_t x) {
2616 // As in Hacker's delight, adapted for just 8 bits.
2617 x = (x & 0x55) + ((x >> 1) & 0x55); // a b c d w x y z -> a+b c+d w+x y+z
2618 x = (x & 0x33) + ((x >> 2) & 0x33); // a+b c+d w+x y+z -> a+b+c+d w+x+y+z
2619 x = (x & 0x0F) + ((x >> 4) & 0x0F); // a+b+c+d w+x+y+z -> a+b+c+d+w+x+y+z
2620 return x;
2621}
2622
2623void SkPaint::FlatteningTraits::Flatten(SkWriteBuffer& buffer, const SkPaint& paint) {
2624 const uint32_t dirty = paint.fDirtyBits;
2625
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +00002626 // Each of the low 7 dirty bits corresponds to a 4-byte flat value,
2627 // plus one for the dirty bits and one for the bitfields
2628 const size_t flatBytes = 4 * (popcount(dirty & kPOD_DirtyBitMask) + 2);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002629 SkASSERT(flatBytes <= 32);
2630 uint32_t* u32 = buffer.reserve(flatBytes);
2631 *u32++ = dirty;
reedf59eab22014-07-14 14:39:15 -07002632 *u32++ = paint.fBitfieldsUInt;
commit-bot@chromium.org45d86e72014-04-16 20:48:10 +00002633 if (0 == dirty) {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002634 return;
2635 }
2636
2637#define F(dst, field) if (dirty & k##field##_DirtyBit) *dst++ = paint.get##field()
2638 F(u32, Color);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002639 SkScalar* f32 = reinterpret_cast<SkScalar*>(u32);
2640 F(f32, TextSize);
2641 F(f32, TextScaleX);
2642 F(f32, TextSkewX);
2643 F(f32, StrokeWidth);
2644 F(f32, StrokeMiter);
2645#undef F
2646#define F(field) if (dirty & k##field##_DirtyBit) buffer.writeFlattenable(paint.get##field())
2647 F(PathEffect);
2648 F(Shader);
2649 F(Xfermode);
2650 F(MaskFilter);
2651 F(ColorFilter);
2652 F(Rasterizer);
2653 F(Looper);
2654 F(ImageFilter);
2655#undef F
2656 if (dirty & kTypeface_DirtyBit) buffer.writeTypeface(paint.getTypeface());
2657 if (dirty & kAnnotation_DirtyBit) paint.getAnnotation()->writeToBuffer(buffer);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002658}
2659
2660void SkPaint::FlatteningTraits::Unflatten(SkReadBuffer& buffer, SkPaint* paint) {
2661 const uint32_t dirty = buffer.readUInt();
reedf59eab22014-07-14 14:39:15 -07002662 paint->fBitfieldsUInt = buffer.readUInt();
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002663 if (dirty == 0) {
2664 return;
2665 }
2666#define F(field, reader) if (dirty & k##field##_DirtyBit) paint->set##field(buffer.reader())
2667// Same function, except it unrefs the object newly set on the paint:
2668#define F_UNREF(field, reader) \
2669 if (dirty & k##field##_DirtyBit) \
2670 paint->set##field(buffer.reader())->unref()
2671
2672 F(Color, readUInt);
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002673 F(TextSize, readScalar);
2674 F(TextScaleX, readScalar);
2675 F(TextSkewX, readScalar);
2676 F(StrokeWidth, readScalar);
2677 F(StrokeMiter, readScalar);
2678 F_UNREF(PathEffect, readPathEffect);
2679 F_UNREF(Shader, readShader);
2680 F_UNREF(Xfermode, readXfermode);
2681 F_UNREF(MaskFilter, readMaskFilter);
2682 F_UNREF(ColorFilter, readColorFilter);
2683 F_UNREF(Rasterizer, readRasterizer);
2684 F_UNREF(Looper, readDrawLooper);
2685 F_UNREF(ImageFilter, readImageFilter);
2686 F(Typeface, readTypeface);
2687#undef F
2688#undef F_UNREF
2689 if (dirty & kAnnotation_DirtyBit) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002690 paint->setAnnotation(SkAnnotation::Create(buffer))->unref();
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002691 }
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002692 SkASSERT(dirty == paint->fDirtyBits);
2693}