blob: 7c6c8e0109d8f71778f218aec053cf3a16fa9540 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkPaint.h"
reed@google.comb0a34d82012-07-11 19:57:55 +00009#include "SkAnnotation.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000010#include "SkAutoKern.h"
mtkleinfb1fe4f2014-10-07 09:26:10 -070011#include "SkChecksum.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000013#include "SkData.h"
bungeman@google.com532470f2013-01-22 19:25:14 +000014#include "SkDeviceProperties.h"
rmistryc4b84ae2014-06-23 06:59:15 -070015#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000016#include "SkFontDescriptor.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
reed@google.coma3237872011-07-05 19:20:48 +000037// define this to get a printf for out-of-range parameter in setters
38// e.g. setTextSize(-1)
39//#define SK_REPORT_API_RANGE_CHECK
40
reed@android.coma3122b92009-08-13 20:38:25 +000041SkPaint::SkPaint() {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000042 fTypeface = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 fPathEffect = NULL;
44 fShader = NULL;
45 fXfermode = NULL;
46 fMaskFilter = NULL;
47 fColorFilter = NULL;
48 fRasterizer = NULL;
49 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000050 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000051 fAnnotation = NULL;
reed@android.coma3122b92009-08-13 20:38:25 +000052
reedf59eab22014-07-14 14:39:15 -070053 fTextSize = SkPaintDefaults_TextSize;
54 fTextScaleX = SK_Scalar1;
55 fTextSkewX = 0;
56 fColor = SK_ColorBLACK;
57 fWidth = 0;
58 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000059
60 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070061 fBitfieldsUInt = 0;
62 fBitfields.fFlags = SkPaintDefaults_Flags;
63 fBitfields.fCapType = kDefault_Cap;
64 fBitfields.fJoinType = kDefault_Join;
65 fBitfields.fTextAlign = kLeft_Align;
66 fBitfields.fStyle = kFill_Style;
67 fBitfields.fTextEncoding = kUTF8_TextEncoding;
68 fBitfields.fHinting = SkPaintDefaults_Hinting;
reed@android.com8a1c16f2008-12-17 15:59:43 +000069}
70
reed@google.com6fb7e2e2011-02-08 22:22:52 +000071SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000072#define COPY(field) field = src.field
73#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000075 REF_COPY(fTypeface);
76 REF_COPY(fPathEffect);
77 REF_COPY(fShader);
78 REF_COPY(fXfermode);
79 REF_COPY(fMaskFilter);
80 REF_COPY(fColorFilter);
81 REF_COPY(fRasterizer);
82 REF_COPY(fLooper);
83 REF_COPY(fImageFilter);
84 REF_COPY(fAnnotation);
85
86 COPY(fTextSize);
87 COPY(fTextScaleX);
88 COPY(fTextSkewX);
89 COPY(fColor);
90 COPY(fWidth);
91 COPY(fMiterLimit);
92 COPY(fBitfields);
djsollen@google.com40078cb2013-05-24 20:31:57 +000093
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000094#undef COPY
95#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +000096}
97
reed@google.com6fb7e2e2011-02-08 22:22:52 +000098SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000099 SkSafeUnref(fTypeface);
100 SkSafeUnref(fPathEffect);
101 SkSafeUnref(fShader);
102 SkSafeUnref(fXfermode);
103 SkSafeUnref(fMaskFilter);
104 SkSafeUnref(fColorFilter);
105 SkSafeUnref(fRasterizer);
106 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000107 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000108 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109}
110
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000111SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000112 if (this == &src) {
113 return *this;
114 }
115
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000116#define COPY(field) field = src.field
117#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
118
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkASSERT(&src);
120
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000121 REF_COPY(fTypeface);
122 REF_COPY(fPathEffect);
123 REF_COPY(fShader);
124 REF_COPY(fXfermode);
125 REF_COPY(fMaskFilter);
126 REF_COPY(fColorFilter);
127 REF_COPY(fRasterizer);
128 REF_COPY(fLooper);
129 REF_COPY(fImageFilter);
130 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000132 COPY(fTextSize);
133 COPY(fTextScaleX);
134 COPY(fTextSkewX);
135 COPY(fColor);
136 COPY(fWidth);
137 COPY(fMiterLimit);
138 COPY(fBitfields);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139
140 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000141
142#undef COPY
143#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144}
145
robertphillips@google.comb2657412013-08-07 22:36:29 +0000146bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000147#define EQUAL(field) (a.field == b.field)
mtklein610a0152014-09-25 11:57:53 -0700148 return EQUAL(fTypeface)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000149 && EQUAL(fPathEffect)
150 && EQUAL(fShader)
151 && EQUAL(fXfermode)
152 && EQUAL(fMaskFilter)
153 && EQUAL(fColorFilter)
154 && EQUAL(fRasterizer)
155 && EQUAL(fLooper)
156 && EQUAL(fImageFilter)
157 && EQUAL(fAnnotation)
158 && EQUAL(fTextSize)
159 && EQUAL(fTextScaleX)
160 && EQUAL(fTextSkewX)
161 && EQUAL(fColor)
162 && EQUAL(fWidth)
163 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700164 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000165 ;
166#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000167}
168
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000169void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 SkPaint init;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000172}
173
reedf803da12015-01-23 05:58:07 -0800174void SkPaint::setFilterQuality(SkFilterQuality quality) {
175 fBitfields.fFilterQuality = quality;
reed@google.comc9683152013-07-18 13:47:01 +0000176}
177
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000178void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700179 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180}
181
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000182void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700183 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184}
185
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000186void SkPaint::setAntiAlias(bool doAA) {
reedf59eab22014-07-14 14:39:15 -0700187 this->setFlags(SkSetClearMask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188}
189
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000190void SkPaint::setDither(bool doDither) {
reedf59eab22014-07-14 14:39:15 -0700191 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192}
193
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000194void SkPaint::setSubpixelText(bool doSubpixel) {
reedf59eab22014-07-14 14:39:15 -0700195 this->setFlags(SkSetClearMask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196}
197
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000198void SkPaint::setLCDRenderText(bool doLCDRender) {
reedf59eab22014-07-14 14:39:15 -0700199 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000200}
201
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000202void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reedf59eab22014-07-14 14:39:15 -0700203 this->setFlags(SkSetClearMask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000204}
205
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000206void SkPaint::setAutohinted(bool useAutohinter) {
reedf59eab22014-07-14 14:39:15 -0700207 this->setFlags(SkSetClearMask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000208}
209
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000210void SkPaint::setLinearText(bool doLinearText) {
reedf59eab22014-07-14 14:39:15 -0700211 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212}
213
reed@google.com830a23e2011-11-10 15:20:49 +0000214void SkPaint::setVerticalText(bool doVertical) {
reedf59eab22014-07-14 14:39:15 -0700215 this->setFlags(SkSetClearMask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000216}
217
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000218void SkPaint::setUnderlineText(bool doUnderline) {
reedf59eab22014-07-14 14:39:15 -0700219 this->setFlags(SkSetClearMask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220}
221
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000222void SkPaint::setStrikeThruText(bool doStrikeThru) {
reedf59eab22014-07-14 14:39:15 -0700223 this->setFlags(SkSetClearMask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224}
225
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000226void SkPaint::setFakeBoldText(bool doFakeBold) {
reedf59eab22014-07-14 14:39:15 -0700227 this->setFlags(SkSetClearMask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228}
229
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000230void SkPaint::setDevKernText(bool doDevKern) {
reedf59eab22014-07-14 14:39:15 -0700231 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232}
233
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000234void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) {
reedf59eab22014-07-14 14:39:15 -0700235 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag));
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000236}
237
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000238void SkPaint::setStyle(Style style) {
239 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700240 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000241 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000242#ifdef SK_REPORT_API_RANGE_CHECK
243 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
244#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246}
247
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000248void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 fColor = color;
250}
251
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000252void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000253 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
254 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255}
256
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000257void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000258 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259}
260
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000261void SkPaint::setStrokeWidth(SkScalar width) {
262 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000264 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000265#ifdef SK_REPORT_API_RANGE_CHECK
266 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
267#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000268 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269}
270
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000271void SkPaint::setStrokeMiter(SkScalar limit) {
272 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000274 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000275#ifdef SK_REPORT_API_RANGE_CHECK
276 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
277#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000281void SkPaint::setStrokeCap(Cap ct) {
282 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700283 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000284 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000285#ifdef SK_REPORT_API_RANGE_CHECK
286 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
287#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000288 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289}
290
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000291void SkPaint::setStrokeJoin(Join jt) {
292 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700293 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000295#ifdef SK_REPORT_API_RANGE_CHECK
296 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
297#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299}
300
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000301///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303void SkPaint::setTextAlign(Align align) {
304 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700305 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000306 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000307#ifdef SK_REPORT_API_RANGE_CHECK
308 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
309#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311}
312
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000313void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000314 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000316 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000317#ifdef SK_REPORT_API_RANGE_CHECK
318 SkDebugf("SkPaint::setTextSize() called with negative value\n");
319#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321}
322
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 fTextScaleX = scaleX;
325}
326
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000327void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 fTextSkewX = skewX;
329}
330
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331void SkPaint::setTextEncoding(TextEncoding encoding) {
332 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700333 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000334 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000335#ifdef SK_REPORT_API_RANGE_CHECK
336 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
337#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000338 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339}
340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000343SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 SkRefCnt_SafeAssign(fTypeface, font);
345 return font;
346}
347
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 SkRefCnt_SafeAssign(fRasterizer, r);
350 return r;
351}
352
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000353SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 SkRefCnt_SafeAssign(fLooper, looper);
355 return looper;
356}
357
reed@google.com15356a62011-11-03 19:29:08 +0000358SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
359 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
reed@google.com15356a62011-11-03 19:29:08 +0000360 return imageFilter;
361}
362
reed@google.comb0a34d82012-07-11 19:57:55 +0000363SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
364 SkRefCnt_SafeAssign(fAnnotation, annotation);
reed@google.comb0a34d82012-07-11 19:57:55 +0000365 return annotation;
366}
367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368///////////////////////////////////////////////////////////////////////////////
369
reed@google.comed43dff2013-06-04 16:56:27 +0000370static SkScalar mag2(SkScalar x, SkScalar y) {
371 return x * x + y * y;
372}
373
374static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
375 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
376 ||
377 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
378}
379
380bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
381 SkASSERT(!ctm.hasPerspective());
382 SkASSERT(!textM.hasPerspective());
383
384 SkMatrix matrix;
385 matrix.setConcat(ctm, textM);
386 return tooBig(matrix, MaxCacheSize2());
387}
388
reed@google.comed43dff2013-06-04 16:56:27 +0000389
390///////////////////////////////////////////////////////////////////////////////
391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392#include "SkGlyphCache.h"
393#include "SkUtils.h"
394
reed@google.com90808e82013-03-19 14:44:54 +0000395static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
396 void* context) {
397 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000398}
399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
401 uint16_t glyphs[]) const {
402 if (byteLength == 0) {
403 return 0;
404 }
reed@google.com72cf4922011-01-04 19:58:20 +0000405
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 SkASSERT(textData != NULL);
407
408 if (NULL == glyphs) {
409 switch (this->getTextEncoding()) {
410 case kUTF8_TextEncoding:
411 return SkUTF8_CountUnichars((const char*)textData, byteLength);
412 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000413 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000414 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000415 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000417 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000419 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 }
421 return 0;
422 }
reed@google.com72cf4922011-01-04 19:58:20 +0000423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000425
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 // handle this encoding before the setup for the glyphcache
427 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
428 // we want to ignore the low bit of byteLength
429 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000430 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
reed@google.com72cf4922011-01-04 19:58:20 +0000432
bungeman@google.com532470f2013-01-22 19:25:14 +0000433 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 SkGlyphCache* cache = autoCache.getCache();
435
436 const char* text = (const char*)textData;
437 const char* stop = text + byteLength;
438 uint16_t* gptr = glyphs;
439
440 switch (this->getTextEncoding()) {
441 case SkPaint::kUTF8_TextEncoding:
442 while (text < stop) {
443 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
444 }
445 break;
446 case SkPaint::kUTF16_TextEncoding: {
447 const uint16_t* text16 = (const uint16_t*)text;
448 const uint16_t* stop16 = (const uint16_t*)stop;
449 while (text16 < stop16) {
450 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
451 }
452 break;
453 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000454 case kUTF32_TextEncoding: {
455 const int32_t* text32 = (const int32_t*)text;
456 const int32_t* stop32 = (const int32_t*)stop;
457 while (text32 < stop32) {
458 *gptr++ = cache->unicharToGlyph(*text32++);
459 }
460 break;
461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000463 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000465 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466}
467
reed@android.coma5dcaf62010-02-05 17:12:32 +0000468bool SkPaint::containsText(const void* textData, size_t byteLength) const {
469 if (0 == byteLength) {
470 return true;
471 }
reed@google.com72cf4922011-01-04 19:58:20 +0000472
reed@android.coma5dcaf62010-02-05 17:12:32 +0000473 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000474
reed@android.coma5dcaf62010-02-05 17:12:32 +0000475 // handle this encoding before the setup for the glyphcache
476 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
477 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
478 size_t count = byteLength >> 1;
479 for (size_t i = 0; i < count; i++) {
480 if (0 == glyphID[i]) {
481 return false;
482 }
483 }
484 return true;
485 }
reed@google.com72cf4922011-01-04 19:58:20 +0000486
bungeman@google.com532470f2013-01-22 19:25:14 +0000487 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000488 SkGlyphCache* cache = autoCache.getCache();
489
490 switch (this->getTextEncoding()) {
491 case SkPaint::kUTF8_TextEncoding: {
492 const char* text = static_cast<const char*>(textData);
493 const char* stop = text + byteLength;
494 while (text < stop) {
495 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
496 return false;
497 }
498 }
499 break;
500 }
501 case SkPaint::kUTF16_TextEncoding: {
502 const uint16_t* text = static_cast<const uint16_t*>(textData);
503 const uint16_t* stop = text + (byteLength >> 1);
504 while (text < stop) {
505 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
506 return false;
507 }
508 }
509 break;
510 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000511 case SkPaint::kUTF32_TextEncoding: {
512 const int32_t* text = static_cast<const int32_t*>(textData);
513 const int32_t* stop = text + (byteLength >> 2);
514 while (text < stop) {
515 if (0 == cache->unicharToGlyph(*text++)) {
516 return false;
517 }
518 }
519 break;
520 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000521 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000522 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000523 return false;
524 }
525 return true;
526}
527
reed@android.com9d3a9852010-01-08 14:07:42 +0000528void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
529 SkUnichar textData[]) const {
530 if (count <= 0) {
531 return;
532 }
533
534 SkASSERT(glyphs != NULL);
535 SkASSERT(textData != NULL);
536
bungeman@google.com532470f2013-01-22 19:25:14 +0000537 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000538 SkGlyphCache* cache = autoCache.getCache();
539
540 for (int index = 0; index < count; index++) {
541 textData[index] = cache->glyphToUnichar(glyphs[index]);
542 }
543}
544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545///////////////////////////////////////////////////////////////////////////////
546
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000547static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
548 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(cache != NULL);
550 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
553}
554
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000555static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
556 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 SkASSERT(cache != NULL);
558 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
561}
562
reed@google.com68bc6f72012-03-14 19:41:55 +0000563static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
564 const char** text) {
565 SkASSERT(cache != NULL);
566 SkASSERT(text != NULL);
567
568 const int32_t* ptr = *(const int32_t**)text;
569 SkUnichar uni = *ptr++;
570 *text = (const char*)ptr;
571 return cache->getUnicharMetrics(uni);
572}
573
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000574static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
575 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 SkASSERT(cache != NULL);
577 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000578
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 const uint16_t* ptr = *(const uint16_t**)text;
580 unsigned glyphID = *ptr;
581 ptr += 1;
582 *text = (const char*)ptr;
583 return cache->getGlyphIDMetrics(glyphID);
584}
585
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000586static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
587 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 SkASSERT(cache != NULL);
589 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
592}
593
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000594static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
595 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 SkASSERT(cache != NULL);
597 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
600}
601
reed@google.com68bc6f72012-03-14 19:41:55 +0000602static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
603 const char** text) {
604 SkASSERT(cache != NULL);
605 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000606
reed@google.com68bc6f72012-03-14 19:41:55 +0000607 const int32_t* ptr = *(const int32_t**)text;
608 SkUnichar uni = *ptr++;
609 *text = (const char*)ptr;
610 return cache->getUnicharAdvance(uni);
611}
612
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000613static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
614 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 SkASSERT(cache != NULL);
616 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 const uint16_t* ptr = *(const uint16_t**)text;
619 unsigned glyphID = *ptr;
620 ptr += 1;
621 *text = (const char*)ptr;
622 return cache->getGlyphIDAdvance(glyphID);
623}
624
reed9e96aa02014-10-03 12:44:37 -0700625SkMeasureCacheProc SkPaint::getMeasureCacheProc(bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
627 sk_getMetrics_utf8_next,
628 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000629 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000631
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 sk_getAdvance_utf8_next,
633 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000634 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 sk_getAdvance_glyph_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 };
reed@google.com72cf4922011-01-04 19:58:20 +0000637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 unsigned index = this->getTextEncoding();
639
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000640 if (!needFullMetrics && !this->isDevKernText()) {
reed9e96aa02014-10-03 12:44:37 -0700641 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000642 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643
644 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
645 return gMeasureCacheProcs[index];
646}
647
648///////////////////////////////////////////////////////////////////////////////
649
650static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000651 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 SkASSERT(cache != NULL);
653 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000654
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
656}
657
658static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000659 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 SkASSERT(cache != NULL);
661 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
664}
665
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000666static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
667 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 SkASSERT(cache != NULL);
669 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
672}
673
674static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000675 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 SkASSERT(cache != NULL);
677 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
680 x, y);
681}
682
reed@google.com68bc6f72012-03-14 19:41:55 +0000683static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
684 const char** text, SkFixed, SkFixed) {
685 SkASSERT(cache != NULL);
686 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000687
reed@google.com68bc6f72012-03-14 19:41:55 +0000688 const int32_t* ptr = *(const int32_t**)text;
689 SkUnichar uni = *ptr++;
690 *text = (const char*)ptr;
691 return cache->getUnicharMetrics(uni);
692}
693
694static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
695 const char** text, SkFixed x, SkFixed y) {
696 SkASSERT(cache != NULL);
697 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000698
reed@google.com68bc6f72012-03-14 19:41:55 +0000699 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000700 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000701 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000702 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000703}
704
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000705static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
706 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 SkASSERT(cache != NULL);
708 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 const uint16_t* ptr = *(const uint16_t**)text;
711 unsigned glyphID = *ptr;
712 ptr += 1;
713 *text = (const char*)ptr;
714 return cache->getGlyphIDMetrics(glyphID);
715}
716
717static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000718 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 SkASSERT(cache != NULL);
720 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 const uint16_t* ptr = *(const uint16_t**)text;
723 unsigned glyphID = *ptr;
724 ptr += 1;
725 *text = (const char*)ptr;
726 return cache->getGlyphIDMetrics(glyphID, x, y);
727}
728
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000729SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 static const SkDrawCacheProc gDrawCacheProcs[] = {
731 sk_getMetrics_utf8_00,
732 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000733 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000735
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 sk_getMetrics_utf8_xy,
737 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000738 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 sk_getMetrics_glyph_xy
740 };
reed@google.com72cf4922011-01-04 19:58:20 +0000741
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 unsigned index = this->getTextEncoding();
reedf59eab22014-07-14 14:39:15 -0700743 if (fBitfields.fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000744 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000745 }
reed@google.com72cf4922011-01-04 19:58:20 +0000746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
748 return gDrawCacheProcs[index];
749}
750
751///////////////////////////////////////////////////////////////////////////////
752
reed@google.comed43dff2013-06-04 16:56:27 +0000753#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
754SkPaint::kDevKernText_Flag | \
755SkPaint::kLinearText_Flag | \
756SkPaint::kLCDRenderText_Flag | \
757SkPaint::kEmbeddedBitmapText_Flag | \
758SkPaint::kAutoHinting_Flag | \
759SkPaint::kGenA8FromLCD_Flag )
760
761SkScalar SkPaint::setupForAsPaths() {
762 uint32_t flags = this->getFlags();
763 // clear the flags we don't care about
764 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
765 // set the flags we do care about
766 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000767
reed@google.comed43dff2013-06-04 16:56:27 +0000768 this->setFlags(flags);
769 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000770
reed@google.comed43dff2013-06-04 16:56:27 +0000771 SkScalar textSize = fTextSize;
772 this->setTextSize(kCanonicalTextSizeForPaths);
773 return textSize / kCanonicalTextSizeForPaths;
774}
775
776class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777public:
reed@google.comed43dff2013-06-04 16:56:27 +0000778 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700779 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000780 SkPaint* p = fLazy.set(paint);
781 fScale = p->setupForAsPaths();
782 fPaint = p;
783 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000785
reed@google.comed43dff2013-06-04 16:56:27 +0000786 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000787
reed@google.comed43dff2013-06-04 16:56:27 +0000788 /**
789 * Returns 0 if the paint was unmodified, or the scale factor need to
790 * the original textSize
791 */
792 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794private:
reed@google.comed43dff2013-06-04 16:56:27 +0000795 const SkPaint* fPaint;
796 SkScalar fScale;
797 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798};
799
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000800static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 bounds->set(SkIntToScalar(g.fLeft),
802 SkIntToScalar(g.fTop),
803 SkIntToScalar(g.fLeft + g.fWidth),
804 SkIntToScalar(g.fTop + g.fHeight));
805}
806
reed@android.come88f5512010-03-19 14:42:28 +0000807// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
808// we don't overflow along the way
809typedef int64_t Sk48Dot16;
810
reed@google.com8f4d2302013-12-17 16:44:46 +0000811static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
812 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
813}
reed@android.come88f5512010-03-19 14:42:28 +0000814
reed@google.com44da42e2011-11-10 20:04:47 +0000815static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000816 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 bounds->join(SkIntToScalar(g.fLeft) + sx,
818 SkIntToScalar(g.fTop),
819 SkIntToScalar(g.fLeft + g.fWidth) + sx,
820 SkIntToScalar(g.fTop + g.fHeight));
821}
822
reed@google.com44da42e2011-11-10 20:04:47 +0000823static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
824 SkScalar sy = Sk48Dot16ToScalar(dy);
825 bounds->join(SkIntToScalar(g.fLeft),
826 SkIntToScalar(g.fTop) + sy,
827 SkIntToScalar(g.fLeft + g.fWidth),
828 SkIntToScalar(g.fTop + g.fHeight) + sy);
829}
830
831typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
832
833// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
834static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
835 SkASSERT(0 == xyIndex || 1 == xyIndex);
836 return (&glyph.fAdvanceX)[xyIndex];
837}
838
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839SkScalar SkPaint::measure_text(SkGlyphCache* cache,
840 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000841 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000843 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000845 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000847 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 return 0;
849 }
850
reed9e96aa02014-10-03 12:44:37 -0700851 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852
reed@google.com44da42e2011-11-10 20:04:47 +0000853 int xyIndex;
854 JoinBoundsProc joinBoundsProc;
855 if (this->isVerticalText()) {
856 xyIndex = 1;
857 joinBoundsProc = join_bounds_y;
858 } else {
859 xyIndex = 0;
860 joinBoundsProc = join_bounds_x;
861 }
862
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 int n = 1;
864 const char* stop = (const char*)text + byteLength;
865 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000866 // our accumulated fixed-point advances might overflow 16.16, so we use
867 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
868 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000869 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870
871 SkAutoKern autokern;
872
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000873 if (NULL == bounds) {
874 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 int rsb;
876 for (; text < stop; n++) {
877 rsb = g->fRsbDelta;
878 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000879 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000881 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000883 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 }
885 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000886 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000888 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 int rsb;
890 for (; text < stop; n++) {
891 rsb = g->fRsbDelta;
892 g = &glyphCacheProc(cache, &text);
893 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000894 joinBoundsProc(*g, bounds, x);
895 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000897 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 for (; text < stop; n++) {
899 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000900 joinBoundsProc(*g, bounds, x);
901 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 }
903 }
904 }
905 SkASSERT(text == stop);
906
907 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000908 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909}
910
reed99ae8812014-08-26 11:30:01 -0700911SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 const char* text = (const char*)textData;
913 SkASSERT(text != NULL || length == 0);
914
reed@google.comed43dff2013-06-04 16:56:27 +0000915 SkCanonicalizePaint canon(*this);
916 const SkPaint& paint = canon.getPaint();
917 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000918
reed99ae8812014-08-26 11:30:01 -0700919 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 SkGlyphCache* cache = autoCache.getCache();
921
922 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000923
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000924 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 int tempCount;
926
reed@google.comed43dff2013-06-04 16:56:27 +0000927 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000928 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000930 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
932 bounds->fTop = SkScalarMul(bounds->fTop, scale);
933 bounds->fRight = SkScalarMul(bounds->fRight, scale);
934 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
935 }
936 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000937 } else if (bounds) {
938 // ensure that even if we don't measure_text we still update the bounds
939 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
941 return width;
942}
943
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700945 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000946 if (0 == length || 0 >= maxWidth) {
947 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000949 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 return 0;
951 }
952
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000953 if (0 == fTextSize) {
954 if (measuredWidth) {
955 *measuredWidth = 0;
956 }
957 return length;
958 }
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 SkASSERT(textD != NULL);
961 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -0700962 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963
reed@google.comed43dff2013-06-04 16:56:27 +0000964 SkCanonicalizePaint canon(*this);
965 const SkPaint& paint = canon.getPaint();
966 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967
reed@google.comed43dff2013-06-04 16:56:27 +0000968 // adjust max in case we changed the textSize in paint
969 if (scale) {
970 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 }
reed@google.com72cf4922011-01-04 19:58:20 +0000972
reed@google.comed43dff2013-06-04 16:56:27 +0000973 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 SkGlyphCache* cache = autoCache.getCache();
975
reed9e96aa02014-10-03 12:44:37 -0700976 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(false);
reed@google.comed43dff2013-06-04 16:56:27 +0000977 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +0000978 // use 64bits for our accumulator, to avoid overflowing 16.16
979 Sk48Dot16 max = SkScalarToFixed(maxWidth);
980 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981
982 SkAutoKern autokern;
983
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000984 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -0700986 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 const char* curr = text;
988 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000989 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000990 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 width -= x;
992 text = curr;
993 break;
994 }
995 rsb = g.fRsbDelta;
996 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000997 } else {
reed9e96aa02014-10-03 12:44:37 -0700998 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001000 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001001 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 width -= x;
1003 text = curr;
1004 break;
1005 }
1006 }
1007 }
reed@google.com72cf4922011-01-04 19:58:20 +00001008
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001009 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001010 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001011 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001013 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 *measuredWidth = scalarWidth;
1015 }
reed@google.com72cf4922011-01-04 19:58:20 +00001016
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -07001018 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019}
1020
1021///////////////////////////////////////////////////////////////////////////////
1022
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001023static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001024 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 return false; // don't detach the cache
1026}
1027
reed@google.com90808e82013-03-19 14:44:54 +00001028static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1029 void* context) {
1030 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031}
1032
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001033SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001034 SkCanonicalizePaint canon(*this);
1035 const SkPaint& paint = canon.getPaint();
1036 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001037
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001038 SkMatrix zoomMatrix, *zoomPtr = NULL;
1039 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 zoomMatrix.setScale(zoom, zoom);
1041 zoomPtr = &zoomMatrix;
1042 }
1043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001045 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001047 }
reed@google.com72cf4922011-01-04 19:58:20 +00001048
reed@google.comed43dff2013-06-04 16:56:27 +00001049 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001051 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1053 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1054 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1055 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1056 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001057 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1058 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1059 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1060 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001061 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1062 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 }
1064 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1065}
1066
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001069static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 bounds->set(g.fLeft * scale,
1071 g.fTop * scale,
1072 (g.fLeft + g.fWidth) * scale,
1073 (g.fTop + g.fHeight) * scale);
1074}
1075
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001076int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1077 SkScalar widths[], SkRect bounds[]) const {
1078 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081
bsalomon49f085d2014-09-05 13:34:00 -07001082 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001084 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001086 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087
reed@google.comed43dff2013-06-04 16:56:27 +00001088 SkCanonicalizePaint canon(*this);
1089 const SkPaint& paint = canon.getPaint();
1090 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091
reed@google.comed43dff2013-06-04 16:56:27 +00001092 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 SkGlyphCache* cache = autoCache.getCache();
1094 SkMeasureCacheProc glyphCacheProc;
reed9e96aa02014-10-03 12:44:37 -07001095 glyphCacheProc = paint.getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096
1097 const char* text = (const char*)textData;
1098 const char* stop = text + byteLength;
1099 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001100 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001102 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 // we adjust the widths returned here through auto-kerning
1104 SkAutoKern autokern;
1105 SkFixed prevWidth = 0;
1106
1107 if (scale) {
1108 while (text < stop) {
1109 const SkGlyph& g = glyphCacheProc(cache, &text);
1110 if (widths) {
1111 SkFixed adjust = autokern.adjust(g);
1112
1113 if (count > 0) {
1114 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1115 *widths++ = SkScalarMul(w, scale);
1116 }
reed@google.com44da42e2011-11-10 20:04:47 +00001117 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 }
1119 if (bounds) {
1120 set_bounds(g, bounds++, scale);
1121 }
1122 ++count;
1123 }
1124 if (count > 0 && widths) {
1125 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1126 }
1127 } else {
1128 while (text < stop) {
1129 const SkGlyph& g = glyphCacheProc(cache, &text);
1130 if (widths) {
1131 SkFixed adjust = autokern.adjust(g);
1132
1133 if (count > 0) {
1134 *widths++ = SkFixedToScalar(prevWidth + adjust);
1135 }
reed@google.com44da42e2011-11-10 20:04:47 +00001136 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 }
1138 if (bounds) {
1139 set_bounds(g, bounds++);
1140 }
1141 ++count;
1142 }
1143 if (count > 0 && widths) {
1144 *widths = SkFixedToScalar(prevWidth);
1145 }
1146 }
1147 } else { // no devkern
1148 if (scale) {
1149 while (text < stop) {
1150 const SkGlyph& g = glyphCacheProc(cache, &text);
1151 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001152 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 scale);
1154 }
1155 if (bounds) {
1156 set_bounds(g, bounds++, scale);
1157 }
1158 ++count;
1159 }
1160 } else {
1161 while (text < stop) {
1162 const SkGlyph& g = glyphCacheProc(cache, &text);
1163 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001164 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 }
1166 if (bounds) {
1167 set_bounds(g, bounds++);
1168 }
1169 ++count;
1170 }
1171 }
1172 }
1173
1174 SkASSERT(text == stop);
1175 return count;
1176}
1177
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001178///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179
1180#include "SkDraw.h"
1181
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001182void SkPaint::getTextPath(const void* textData, size_t length,
1183 SkScalar x, SkScalar y, SkPath* path) const {
1184 SkASSERT(length == 0 || textData != NULL);
1185
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001187 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190
djsollen@google.com166e6532012-03-20 14:24:38 +00001191 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 SkMatrix matrix;
1193 SkScalar prevXPos = 0;
1194
1195 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1196 matrix.postTranslate(x, y);
1197 path->reset();
1198
1199 SkScalar xpos;
1200 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001201 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001203 if (iterPath) {
1204 path->addPath(*iterPath, matrix);
1205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 prevXPos = xpos;
1207 }
1208}
1209
reed@google.comca0062e2012-07-20 11:20:32 +00001210void SkPaint::getPosTextPath(const void* textData, size_t length,
1211 const SkPoint pos[], SkPath* path) const {
1212 SkASSERT(length == 0 || textData != NULL);
1213
1214 const char* text = (const char*)textData;
1215 if (text == NULL || length == 0 || path == NULL) {
1216 return;
1217 }
1218
1219 SkTextToPathIter iter(text, length, *this, false);
1220 SkMatrix matrix;
1221 SkPoint prevPos;
1222 prevPos.set(0, 0);
1223
1224 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1225 path->reset();
1226
1227 unsigned int i = 0;
1228 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001229 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001230 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001231 if (iterPath) {
1232 path->addPath(*iterPath, matrix);
1233 }
reed@google.comca0062e2012-07-20 11:20:32 +00001234 prevPos = pos[i];
1235 i++;
1236 }
1237}
1238
reed8893e5f2014-12-15 13:27:26 -08001239SkRect SkPaint::getFontBounds() const {
1240 SkMatrix m;
1241 m.setScale(fTextSize * fTextScaleX, fTextSize);
1242 m.postSkew(fTextSkewX, 0);
1243
1244 SkTypeface* typeface = this->getTypeface();
1245 if (NULL == typeface) {
1246 typeface = SkTypeface::GetDefaultTypeface();
1247 }
1248
1249 SkRect bounds;
1250 m.mapRect(&bounds, typeface->getBounds());
1251 return bounds;
1252}
1253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001255 SkWriteBuffer* buffer) {
1256 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257}
1258
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001259static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001262 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001263 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001264 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001265 }
1266
reed@google.com65dd8f82011-03-14 13:31:16 +00001267 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001268 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001269 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001270
1271 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
reed@android.com1cdcb512009-08-24 19:11:00 +00001274// if linear-text is on, then we force hinting to be off (since that's sort of
1275// the point of linear-text.
1276static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1277 SkPaint::Hinting h = paint.getHinting();
1278 if (paint.isLinearText()) {
1279 h = SkPaint::kNo_Hinting;
1280 }
1281 return h;
1282}
1283
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001284// return true if the paint is just a single color (i.e. not a shader). If its
1285// a shader, then we can't compute a const luminance for it :(
1286static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001287 SkColor c = paint.getColor();
1288
1289 SkShader* shader = paint.getShader();
1290 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001291 return false;
1292 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001293 if (paint.getColorFilter()) {
1294 c = paint.getColorFilter()->filterColor(c);
1295 }
1296 if (color) {
1297 *color = c;
1298 }
1299 return true;
1300}
1301
reed@google.comce6dbb62012-02-10 22:01:45 +00001302static SkColor computeLuminanceColor(const SkPaint& paint) {
1303 SkColor c;
1304 if (!justAColor(paint, &c)) {
1305 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1306 }
1307 return c;
1308}
reed@google.com813d38b2012-02-13 21:37:57 +00001309
reed@google.comdd43df92012-02-15 14:50:29 +00001310#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1311
reed@google.com4f79b9b2011-09-13 13:23:26 +00001312// Beyond this size, LCD doesn't appreciably improve quality, but it always
1313// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001314#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1315 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1316#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001317
reedb3da83a2014-10-01 12:06:12 -07001318const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1319
reed4942e752014-10-01 13:59:33 -07001320static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001321 if (checkPost2x2) {
1322 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1323 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001324 area *= rec.fTextSize * rec.fTextSize;
1325 return area > gMaxSize2ForLCDText;
1326 } else {
1327 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001328 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001329}
1330
reed@google.com72cf4922011-01-04 19:58:20 +00001331/*
1332 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1333 * that vary only slightly when we create our key into the font cache, since the font scaler
1334 * typically returns the same looking resuts for tiny changes in the matrix.
1335 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001336static SkScalar sk_relax(SkScalar x) {
1337 int n = sk_float_round2int(x * 1024);
1338 return n / 1024.0f;
1339}
1340
reed@android.com36a4c2a2009-07-22 19:52:11 +00001341void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001342 const SkDeviceProperties* deviceProperties,
1343 const SkMatrix* deviceMatrix,
1344 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001345 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001347 SkTypeface* typeface = paint.getTypeface();
1348 if (NULL == typeface) {
1349 typeface = SkTypeface::GetDefaultTypeface();
1350 }
bungemanec730b92014-08-18 07:57:35 -07001351 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 rec->fTextSize = paint.getTextSize();
1353 rec->fPreScaleX = paint.getTextScaleX();
1354 rec->fPreSkewX = paint.getTextSkewX();
1355
reedb3da83a2014-10-01 12:06:12 -07001356 bool checkPost2x2 = false;
1357
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001358 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001359 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1360 if (mask & SkMatrix::kScale_Mask) {
1361 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1362 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1363 checkPost2x2 = true;
1364 } else {
1365 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1366 }
1367 if (mask & SkMatrix::kAffine_Mask) {
1368 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1369 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1370 checkPost2x2 = true;
1371 } else {
1372 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1373 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001374 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1376 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1377 }
reed@google.com72cf4922011-01-04 19:58:20 +00001378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkPaint::Style style = paint.getStyle();
1380 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001381
reed@google.comffe49f52011-11-22 19:42:41 +00001382 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001383
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001384 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001385#ifdef SK_USE_FREETYPE_EMBOLDEN
1386 flags |= SkScalerContext::kEmbolden_Flag;
1387#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001388 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1389 kStdFakeBoldInterpKeys,
1390 kStdFakeBoldInterpValues,
1391 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001393
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001394 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 style = SkPaint::kStrokeAndFill_Style;
1396 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001397 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001399 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001400#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 }
1402
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001403 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001405 }
reed@google.com72cf4922011-01-04 19:58:20 +00001406
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001407 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 rec->fFrameWidth = strokeWidth;
1409 rec->fMiterLimit = paint.getStrokeMiter();
1410 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1411
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001412 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001414 }
1415 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 rec->fFrameWidth = 0;
1417 rec->fMiterLimit = 0;
1418 rec->fStrokeJoin = 0;
1419 }
1420
reed@google.com02b53312011-05-18 19:00:53 +00001421 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1422
reedd54d3fc2014-11-13 14:39:58 -08001423 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001424 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001425 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001426 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001427 } else {
reed4a8126e2014-09-22 07:29:03 -07001428 SkPixelGeometry geometry = deviceProperties
reedb2d77e42014-10-14 08:26:33 -07001429 ? deviceProperties->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001430 : SkSurfacePropsDefaultPixelGeometry();
1431 switch (geometry) {
1432 case kUnknown_SkPixelGeometry:
1433 // eeek, can't support LCD
1434 rec->fMaskFormat = SkMask::kA8_Format;
1435 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1436 break;
1437 case kRGB_H_SkPixelGeometry:
1438 // our default, do nothing.
1439 break;
1440 case kBGR_H_SkPixelGeometry:
1441 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1442 break;
1443 case kRGB_V_SkPixelGeometry:
1444 flags |= SkScalerContext::kLCD_Vertical_Flag;
1445 break;
1446 case kBGR_V_SkPixelGeometry:
1447 flags |= SkScalerContext::kLCD_Vertical_Flag;
1448 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1449 break;
reed@google.com02b53312011-05-18 19:00:53 +00001450 }
1451 }
1452 }
1453
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001454 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001455 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001456 }
1457 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001458 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001459 }
1460 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001461 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001462 }
reed@google.com830a23e2011-11-10 15:20:49 +00001463 if (paint.isVerticalText()) {
1464 flags |= SkScalerContext::kVertical_Flag;
1465 }
reed@google.com8351aab2012-01-18 17:06:35 +00001466 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1467 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1468 }
reed@google.com02b53312011-05-18 19:00:53 +00001469 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001470
reed@google.comffe49f52011-11-22 19:42:41 +00001471 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001472 rec->setHinting(computeHinting(paint));
1473
bungeman@google.com97efada2012-07-30 20:40:50 +00001474 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001475
1476 if (NULL == deviceProperties) {
1477 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1478 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1479 } else {
reedb2d77e42014-10-14 08:26:33 -07001480 rec->setDeviceGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001481
1482 //For now always set the paint gamma equal to the device gamma.
1483 //The math in SkMaskGamma can handle them being different,
1484 //but it requires superluminous masks when
1485 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
reedb2d77e42014-10-14 08:26:33 -07001486 rec->setPaintGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001487 }
1488
1489#ifdef SK_GAMMA_CONTRAST
1490 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001491#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001492 /**
1493 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1494 * With lower values small text appears washed out (though correctly so).
1495 * With higher values lcd fringing is worse and the smoothing effect of
1496 * partial coverage is diminished.
1497 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001498 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001499#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001500
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001501 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001502
reed@android.com36a4c2a2009-07-22 19:52:11 +00001503 /* Allow the fonthost to modify our rec before we use it as a key into the
1504 cache. This way if we're asking for something that they will ignore,
1505 they can modify our rec up front, so we don't create duplicate cache
1506 entries.
1507 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001508 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001509
reed@google.com10d2d4d2012-03-01 22:32:51 +00001510 // be sure to call PostMakeRec(rec) before you actually use it!
1511}
1512
1513/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001514 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1515 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1516 * to hold it until the returned pointer is refed or forgotten.
1517 */
1518SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1519
bungeman@google.comae30f562012-09-11 18:44:55 +00001520static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001521static SkMaskGamma* gMaskGamma = NULL;
1522static SkScalar gContrast = SK_ScalarMin;
1523static SkScalar gPaintGamma = SK_ScalarMin;
1524static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001525/**
1526 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1527 * the returned SkMaskGamma pointer is refed or forgotten.
1528 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001529static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001530 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001531 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1532 if (NULL == gLinearMaskGamma) {
1533 gLinearMaskGamma = SkNEW(SkMaskGamma);
1534 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001535 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001536 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001537 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1538 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001539 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001540 gContrast = contrast;
1541 gPaintGamma = paintGamma;
1542 gDeviceGamma = deviceGamma;
1543 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001544 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001545}
1546
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001547/*static*/ void SkPaint::Term() {
1548 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1549
bungeman@google.comae30f562012-09-11 18:44:55 +00001550 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001551 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001552 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001553 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001554 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1555 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1556 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001557}
1558
bungeman@google.com97efada2012-07-30 20:40:50 +00001559/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001560 * We ensure that the rec is self-consistent and efficient (where possible)
1561 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001562void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001563 /**
1564 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001565 * limits the number of unique entries, and the scaler will only look at
1566 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001567 */
reed@google.comdd43df92012-02-15 14:50:29 +00001568 switch (rec->fMaskFormat) {
reedd54d3fc2014-11-13 14:39:58 -08001569 case SkMask::kLCD16_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001570 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001571 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001572 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001573 break;
1574 }
1575 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001576 // filter down the luminance to a single component, since A8 can't
1577 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001578 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001579 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1580 SkColorGetG(color),
1581 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001582 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001583 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001584 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001585 break;
1586 }
1587 case SkMask::kBW_Format:
1588 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001589 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001590 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001591 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592}
1593
1594#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1595
reed@google.com17fb3872011-05-04 14:31:07 +00001596#ifdef SK_DEBUG
1597 #define TEST_DESC
1598#endif
1599
reed@google.comffe49f52011-11-22 19:42:41 +00001600/*
1601 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001602 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1603 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001604 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001605void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1606 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001607 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001608 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609 SkScalerContext::Rec rec;
1610
bungeman@google.com532470f2013-01-22 19:25:14 +00001611 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001612 if (ignoreGamma) {
jvanverth2d2a68c2014-06-10 06:42:56 -07001613 rec.ignorePreBlend();
djsollen@google.com57f49692011-02-23 20:46:31 +00001614 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615
1616 size_t descSize = sizeof(rec);
1617 int entryCount = 1;
1618 SkPathEffect* pe = this->getPathEffect();
1619 SkMaskFilter* mf = this->getMaskFilter();
1620 SkRasterizer* ra = this->getRasterizer();
1621
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001622 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623
1624 if (pe) {
1625 peBuffer.writeFlattenable(pe);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001626 descSize += peBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 entryCount += 1;
1628 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1629 // seems like we could support kLCD as well at this point...
1630 }
1631 if (mf) {
1632 mfBuffer.writeFlattenable(mf);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001633 descSize += mfBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 entryCount += 1;
1635 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001636 /* Pre-blend is not currently applied to filtered text.
1637 The primary filter is blur, for which contrast makes no sense,
1638 and for which the destination guess error is more visible.
1639 Also, all existing users of blur have calibrated for linear. */
1640 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 }
1642 if (ra) {
1643 raBuffer.writeFlattenable(ra);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001644 descSize += raBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 entryCount += 1;
1646 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1647 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001648
1649 ///////////////////////////////////////////////////////////////////////////
1650 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001651 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001652
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 descSize += SkDescriptor::ComputeOverhead(entryCount);
1654
1655 SkAutoDescriptor ad(descSize);
1656 SkDescriptor* desc = ad.getDesc();
1657
1658 desc->init();
1659 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1660
1661 if (pe) {
1662 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1663 }
1664 if (mf) {
1665 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1666 }
1667 if (ra) {
1668 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1669 }
1670
1671 SkASSERT(descSize == desc->getLength());
1672 desc->computeChecksum();
1673
reed@google.com17fb3872011-05-04 14:31:07 +00001674#ifdef TEST_DESC
1675 {
1676 // Check that we completely write the bytes in desc (our key), and that
1677 // there are no uninitialized bytes. If there were, then we would get
1678 // false-misses (or worse, false-hits) in our fontcache.
1679 //
1680 // We do this buy filling 2 others, one with 0s and the other with 1s
1681 // and create those, and then check that all 3 are identical.
1682 SkAutoDescriptor ad1(descSize);
1683 SkAutoDescriptor ad2(descSize);
1684 SkDescriptor* desc1 = ad1.getDesc();
1685 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001686
reed@google.com17fb3872011-05-04 14:31:07 +00001687 memset(desc1, 0x00, descSize);
1688 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001689
reed@google.com17fb3872011-05-04 14:31:07 +00001690 desc1->init();
1691 desc2->init();
1692 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1693 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001694
reed@google.com17fb3872011-05-04 14:31:07 +00001695 if (pe) {
1696 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1697 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1698 }
1699 if (mf) {
1700 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1701 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1702 }
1703 if (ra) {
1704 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1705 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1706 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001707
reed@google.com17fb3872011-05-04 14:31:07 +00001708 SkASSERT(descSize == desc1->getLength());
1709 SkASSERT(descSize == desc2->getLength());
1710 desc1->computeChecksum();
1711 desc2->computeChecksum();
1712 SkASSERT(!memcmp(desc, desc1, descSize));
1713 SkASSERT(!memcmp(desc, desc2, descSize));
1714 }
1715#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001716
reed@google.com90808e82013-03-19 14:44:54 +00001717 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718}
1719
bungeman@google.com532470f2013-01-22 19:25:14 +00001720SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
jvanverth2d2a68c2014-06-10 06:42:56 -07001721 const SkMatrix* deviceMatrix,
1722 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 SkGlyphCache* cache;
jvanverth2d2a68c2014-06-10 06:42:56 -07001724 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 return cache;
1726}
1727
bungeman@google.com97efada2012-07-30 20:40:50 +00001728/**
1729 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1730 */
1731//static
1732SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1733 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001734 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1735 rec.getPaintGamma(),
1736 rec.getDeviceGamma());
1737 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001738}
1739
jvanverth2d2a68c2014-06-10 06:42:56 -07001740size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1741 SkScalar deviceGamma, int* width, int* height) {
1742 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1743 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1744 paintGamma,
1745 deviceGamma);
1746
1747 maskGamma.getGammaTableDimensions(width, height);
1748 size_t size = (*width)*(*height)*sizeof(uint8_t);
1749
1750 return size;
1751}
1752
1753void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1754 void* data) {
1755 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1756 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1757 paintGamma,
1758 deviceGamma);
1759 int width, height;
1760 maskGamma.getGammaTableDimensions(&width, &height);
1761 size_t size = width*height*sizeof(uint8_t);
1762 const uint8_t* gammaTables = maskGamma.getGammaTables();
1763 memcpy(data, gammaTables, size);
1764}
1765
1766
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767///////////////////////////////////////////////////////////////////////////////
1768
1769#include "SkStream.h"
1770
reed@android.comaefd2bc2009-03-30 21:02:14 +00001771static uintptr_t asint(const void* p) {
1772 return reinterpret_cast<uintptr_t>(p);
1773}
1774
1775union Scalar32 {
1776 SkScalar fScalar;
1777 uint32_t f32;
1778};
1779
1780static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1781 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1782 Scalar32 tmp;
1783 tmp.fScalar = value;
1784 *ptr = tmp.f32;
1785 return ptr + 1;
1786}
1787
1788static SkScalar read_scalar(const uint32_t*& ptr) {
1789 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1790 Scalar32 tmp;
1791 tmp.f32 = *ptr++;
1792 return tmp.fScalar;
1793}
1794
1795static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1796 SkASSERT(a == (uint8_t)a);
1797 SkASSERT(b == (uint8_t)b);
1798 SkASSERT(c == (uint8_t)c);
1799 SkASSERT(d == (uint8_t)d);
1800 return (a << 24) | (b << 16) | (c << 8) | d;
1801}
1802
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001803#ifdef SK_DEBUG
1804 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1805 SkASSERT(bitCount > 0 && bitCount <= 32);
1806 uint32_t mask = ~0U;
1807 mask >>= (32 - bitCount);
1808 SkASSERT(0 == (value & ~mask));
1809 }
1810#else
1811 #define ASSERT_FITS_IN(value, bitcount)
1812#endif
1813
reed@android.comaefd2bc2009-03-30 21:02:14 +00001814enum FlatFlags {
mtklein88fd0fb2014-12-01 06:56:38 -08001815 kHasTypeface_FlatFlag = 0x1,
1816 kHasEffects_FlatFlag = 0x2,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001817
mtklein88fd0fb2014-12-01 06:56:38 -08001818 kFlatFlagMask = 0x3,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001819};
1820
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001821enum BitsPerField {
1822 kFlags_BPF = 16,
1823 kHint_BPF = 2,
1824 kAlign_BPF = 2,
1825 kFilter_BPF = 2,
1826 kFlatFlags_BPF = 3,
1827};
1828
1829static inline int BPF_Mask(int bits) {
1830 return (1 << bits) - 1;
1831}
1832
1833static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1834 unsigned filter, unsigned flatFlags) {
1835 ASSERT_FITS_IN(flags, kFlags_BPF);
1836 ASSERT_FITS_IN(hint, kHint_BPF);
1837 ASSERT_FITS_IN(align, kAlign_BPF);
1838 ASSERT_FITS_IN(filter, kFilter_BPF);
1839 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1840
1841 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1842 // add more bits in the future.
1843 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1844}
1845
1846static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1847 paint->setFlags(packed >> 16);
1848 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1849 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
1850 paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
1851 return (FlatFlags)(packed & kFlatFlagMask);
1852}
1853
reed@android.comaefd2bc2009-03-30 21:02:14 +00001854// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00001855static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00001856 1 * sizeof(SkColor) +
1857 1 * sizeof(uint16_t) +
1858 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001859
1860/* To save space/time, we analyze the paint, and write a truncated version of
1861 it if there are not tricky elements like shaders, etc.
1862 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001863void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001864 uint8_t flatFlags = 0;
1865 if (this->getTypeface()) {
1866 flatFlags |= kHasTypeface_FlatFlag;
1867 }
1868 if (asint(this->getPathEffect()) |
1869 asint(this->getShader()) |
1870 asint(this->getXfermode()) |
1871 asint(this->getMaskFilter()) |
1872 asint(this->getColorFilter()) |
1873 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001874 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001875 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001876 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001877 flatFlags |= kHasEffects_FlatFlag;
1878 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001879
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001880 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1881 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001882
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001883 ptr = write_scalar(ptr, this->getTextSize());
1884 ptr = write_scalar(ptr, this->getTextScaleX());
1885 ptr = write_scalar(ptr, this->getTextSkewX());
1886 ptr = write_scalar(ptr, this->getStrokeWidth());
1887 ptr = write_scalar(ptr, this->getStrokeMiter());
1888 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001889
1890 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
1891 this->getFilterLevel(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001892 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1893 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001894
1895 // now we're done with ptr and the (pre)reserved space. If we need to write
1896 // additional fields, use the buffer directly
1897 if (flatFlags & kHasTypeface_FlatFlag) {
1898 buffer.writeTypeface(this->getTypeface());
1899 }
1900 if (flatFlags & kHasEffects_FlatFlag) {
1901 buffer.writeFlattenable(this->getPathEffect());
1902 buffer.writeFlattenable(this->getShader());
1903 buffer.writeFlattenable(this->getXfermode());
1904 buffer.writeFlattenable(this->getMaskFilter());
1905 buffer.writeFlattenable(this->getColorFilter());
1906 buffer.writeFlattenable(this->getRasterizer());
1907 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001908 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00001909
1910 if (fAnnotation) {
1911 buffer.writeBool(true);
1912 fAnnotation->writeToBuffer(buffer);
1913 } else {
1914 buffer.writeBool(false);
1915 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917}
1918
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001919void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001920 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1921 const void* podData = buffer.skip(kPODPaintSize);
1922 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001923
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001924 // the order we read must match the order we wrote in flatten()
1925 this->setTextSize(read_scalar(pod));
1926 this->setTextScaleX(read_scalar(pod));
1927 this->setTextSkewX(read_scalar(pod));
1928 this->setStrokeWidth(read_scalar(pod));
1929 this->setStrokeMiter(read_scalar(pod));
1930 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00001931
mtklein88fd0fb2014-12-01 06:56:38 -08001932 unsigned flatFlags = unpack_paint_flags(this, *pod++);
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001933
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001934 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001935 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1936 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1937 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1938 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001939
1940 if (flatFlags & kHasTypeface_FlatFlag) {
1941 this->setTypeface(buffer.readTypeface());
1942 } else {
1943 this->setTypeface(NULL);
1944 }
1945
1946 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00001947 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
1948 SkSafeUnref(this->setShader(buffer.readShader()));
1949 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
1950 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
1951 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
1952 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
1953 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
1954 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00001955
reed@google.com0cd2ac62013-10-14 20:02:44 +00001956 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00001957 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00001958 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001959 } else {
1960 this->setPathEffect(NULL);
1961 this->setShader(NULL);
1962 this->setXfermode(NULL);
1963 this->setMaskFilter(NULL);
1964 this->setColorFilter(NULL);
1965 this->setRasterizer(NULL);
1966 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001967 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001968 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969}
1970
1971///////////////////////////////////////////////////////////////////////////////
1972
reed@google.com82065d62011-02-07 15:30:46 +00001973SkShader* SkPaint::setShader(SkShader* shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 SkRefCnt_SafeAssign(fShader, shader);
1975 return shader;
1976}
1977
reed@google.com82065d62011-02-07 15:30:46 +00001978SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 SkRefCnt_SafeAssign(fColorFilter, filter);
1980 return filter;
1981}
1982
reed@google.com82065d62011-02-07 15:30:46 +00001983SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984 SkRefCnt_SafeAssign(fXfermode, mode);
1985 return mode;
1986}
1987
reed@android.com0baf1932009-06-24 12:41:42 +00001988SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00001989 SkSafeUnref(fXfermode);
1990 fXfermode = SkXfermode::Create(mode);
1991 return fXfermode;
1992}
1993
reed@google.com82065d62011-02-07 15:30:46 +00001994SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 SkRefCnt_SafeAssign(fPathEffect, effect);
1996 return effect;
1997}
1998
reed@google.com82065d62011-02-07 15:30:46 +00001999SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000 SkRefCnt_SafeAssign(fMaskFilter, filter);
2001 return filter;
2002}
2003
reed@google.com82065d62011-02-07 15:30:46 +00002004///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005
reed@google.com4bbdeac2013-01-24 21:03:11 +00002006bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2007 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002008 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009
reed@google.comfd4be262012-05-25 01:04:12 +00002010 const SkPath* srcPtr = &src;
2011 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002012
reed@google.com4bbdeac2013-01-24 21:03:11 +00002013 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002014 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002015 }
2016
reed@google.comfd4be262012-05-25 01:04:12 +00002017 if (!rec.applyToPath(dst, *srcPtr)) {
2018 if (srcPtr == &tmpPath) {
2019 // If path's were copy-on-write, this trick would not be needed.
2020 // As it is, we want to save making a deep-copy from tmpPath -> dst
2021 // since we know we're just going to delete tmpPath when we return,
2022 // so the swap saves that copy.
2023 dst->swap(tmpPath);
2024 } else {
2025 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 }
2027 }
reed@google.comfd4be262012-05-25 01:04:12 +00002028 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029}
2030
reed@google.come4f10a72012-05-15 20:47:50 +00002031const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002032 SkRect* storage,
2033 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002035
reed@google.come4f10a72012-05-15 20:47:50 +00002036 const SkRect* src = &origSrc;
2037
reed@google.com9efd9a02012-01-30 15:41:43 +00002038 if (this->getLooper()) {
2039 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002040 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002041 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002043
reed@google.come4f10a72012-05-15 20:47:50 +00002044 SkRect tmpSrc;
2045 if (this->getPathEffect()) {
2046 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2047 src = &tmpSrc;
2048 }
2049
reed@google.coma584aed2012-05-16 14:06:02 +00002050 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002051 // since we're stroked, outset the rect by the radius (and join type)
2052 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2053 if (0 == radius) { // hairline
2054 radius = SK_Scalar1;
2055 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2056 SkScalar scale = this->getStrokeMiter();
2057 if (scale > SK_Scalar1) {
2058 radius = SkScalarMul(radius, scale);
2059 }
2060 }
reed@google.come4f10a72012-05-15 20:47:50 +00002061 storage->set(src->fLeft - radius, src->fTop - radius,
2062 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002063 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002064 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002065 }
2066
reed@google.com9efd9a02012-01-30 15:41:43 +00002067 if (this->getMaskFilter()) {
2068 this->getMaskFilter()->computeFastBounds(*storage, storage);
2069 }
2070
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002071 if (this->getImageFilter()) {
2072 this->getImageFilter()->computeFastBounds(*storage, storage);
2073 }
2074
reed@android.comd252db02009-04-01 18:31:44 +00002075 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076}
2077
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002078#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002079
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002080void SkPaint::toString(SkString* str) const {
2081 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2082
2083 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002084 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002085 SkDynamicMemoryWStream ostream;
2086 typeface->serialize(&ostream);
scroggoa1193e42015-01-21 12:09:53 -08002087 SkAutoTDelete<SkStreamAsset> istream(ostream.detachAsStream());
bungemand71b7572014-09-18 10:55:32 -07002088 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002089
2090 str->append("<dt>Font Family Name:</dt><dd>");
2091 str->append(descriptor.getFamilyName());
2092 str->append("</dd><dt>Font Full Name:</dt><dd>");
2093 str->append(descriptor.getFullName());
2094 str->append("</dd><dt>Font PS Name:</dt><dd>");
2095 str->append(descriptor.getPostscriptName());
2096 str->append("</dd><dt>Font File Name:</dt><dd>");
2097 str->append(descriptor.getFontFileName());
2098 str->append("</dd>");
2099 }
2100
2101 str->append("<dt>TextSize:</dt><dd>");
2102 str->appendScalar(this->getTextSize());
2103 str->append("</dd>");
2104
2105 str->append("<dt>TextScaleX:</dt><dd>");
2106 str->appendScalar(this->getTextScaleX());
2107 str->append("</dd>");
2108
2109 str->append("<dt>TextSkewX:</dt><dd>");
2110 str->appendScalar(this->getTextSkewX());
2111 str->append("</dd>");
2112
2113 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002114 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002115 str->append("<dt>PathEffect:</dt><dd>");
robertphillips42dbfa82015-01-26 06:08:52 -08002116 pathEffect->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002117 str->append("</dd>");
2118 }
2119
2120 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002121 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002122 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002123 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002124 str->append("</dd>");
2125 }
2126
2127 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002128 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002129 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002130 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002131 str->append("</dd>");
2132 }
2133
2134 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002135 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002136 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002137 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002138 str->append("</dd>");
2139 }
2140
2141 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002142 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002143 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002144 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002145 str->append("</dd>");
2146 }
2147
2148 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002149 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002150 str->append("<dt>Rasterizer:</dt><dd>");
2151 str->append("</dd>");
2152 }
2153
2154 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002155 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002156 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002157 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002158 str->append("</dd>");
2159 }
2160
2161 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002162 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002163 str->append("<dt>ImageFilter:</dt><dd>");
robertphillipsf3f5bad2014-12-19 13:49:15 -08002164 imageFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002165 str->append("</dd>");
2166 }
2167
2168 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002169 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002170 str->append("<dt>Annotation:</dt><dd>");
2171 str->append("</dd>");
2172 }
2173
2174 str->append("<dt>Color:</dt><dd>0x");
2175 SkColor color = this->getColor();
2176 str->appendHex(color);
2177 str->append("</dd>");
2178
2179 str->append("<dt>Stroke Width:</dt><dd>");
2180 str->appendScalar(this->getStrokeWidth());
2181 str->append("</dd>");
2182
2183 str->append("<dt>Stroke Miter:</dt><dd>");
2184 str->appendScalar(this->getStrokeMiter());
2185 str->append("</dd>");
2186
2187 str->append("<dt>Flags:</dt><dd>(");
2188 if (this->getFlags()) {
2189 bool needSeparator = false;
2190 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002191 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2192 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2193 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2194 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2195 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2196 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2197 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2198 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2199 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2200 "EmbeddedBitmapText", &needSeparator);
2201 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2202 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2203 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2204 "GenA8FromLCD", &needSeparator);
2205 } else {
2206 str->append("None");
2207 }
2208 str->append(")</dd>");
2209
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002210 str->append("<dt>FilterLevel:</dt><dd>");
2211 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2212 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2213 str->append("</dd>");
2214
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002215 str->append("<dt>TextAlign:</dt><dd>");
2216 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2217 str->append(gTextAlignStrings[this->getTextAlign()]);
2218 str->append("</dd>");
2219
2220 str->append("<dt>CapType:</dt><dd>");
2221 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2222 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2223 str->append("</dd>");
2224
2225 str->append("<dt>JoinType:</dt><dd>");
2226 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2227 str->append(gJoinStrings[this->getStrokeJoin()]);
2228 str->append("</dd>");
2229
2230 str->append("<dt>Style:</dt><dd>");
2231 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2232 str->append(gStyleStrings[this->getStyle()]);
2233 str->append("</dd>");
2234
2235 str->append("<dt>TextEncoding:</dt><dd>");
2236 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2237 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2238 str->append("</dd>");
2239
2240 str->append("<dt>Hinting:</dt><dd>");
2241 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2242 str->append(gHintingStrings[this->getHinting()]);
2243 str->append("</dd>");
2244
2245 str->append("</dd></dl></dl>");
2246}
2247#endif
2248
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002249///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002251static bool has_thick_frame(const SkPaint& paint) {
2252 return paint.getStrokeWidth() > 0 &&
2253 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254}
2255
2256SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2257 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002258 bool applyStrokeAndPathEffects)
2259 : fPaint(paint) {
reed9e96aa02014-10-03 12:44:37 -07002260 fGlyphCacheProc = paint.getMeasureCacheProc(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261
djsollen@google.com166e6532012-03-20 14:24:38 +00002262 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2264
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002265 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268
djsollen@google.com166e6532012-03-20 14:24:38 +00002269 // can't use our canonical size if we need to apply patheffects
2270 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2272 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002273 if (has_thick_frame(fPaint)) {
2274 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2275 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002276 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002278 }
reed@google.com72cf4922011-01-04 19:58:20 +00002279
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002280 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 fPaint.setStyle(SkPaint::kFill_Style);
2282 fPaint.setPathEffect(NULL);
2283 }
2284
jvanverth2d2a68c2014-06-10 06:42:56 -07002285 fCache = fPaint.detachCache(NULL, NULL, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286
2287 SkPaint::Style style = SkPaint::kFill_Style;
2288 SkPathEffect* pe = NULL;
2289
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002290 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002291 style = paint.getStyle(); // restore
2292 pe = paint.getPathEffect(); // restore
2293 }
2294 fPaint.setStyle(style);
2295 fPaint.setPathEffect(pe);
2296 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2297
2298 // now compute fXOffset if needed
2299
2300 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002301 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002303 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2304 &count, NULL), fScale);
2305 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 xOffset = -width;
2309 }
2310 fXPos = xOffset;
2311 fPrevAdvance = 0;
2312
2313 fText = text;
2314 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002315
reed@google.com44da42e2011-11-10 20:04:47 +00002316 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317}
2318
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002319SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 SkGlyphCache::AttachCache(fCache);
2321}
2322
reed@google.com7b4531f2012-08-07 15:53:00 +00002323bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2324 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2326
2327 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002328 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002330 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002331 if (path) {
2332 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002333 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002334 } else {
2335 if (path) {
2336 *path = NULL;
2337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002339 if (xpos) {
2340 *xpos = fXPos;
2341 }
2342 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002344 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002346
2347///////////////////////////////////////////////////////////////////////////////
2348
reedf539b8c2014-11-11 12:51:33 -08002349// return true if the filter exists, and may affect alpha
2350static bool affects_alpha(const SkColorFilter* cf) {
2351 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2352}
2353
2354// return true if the filter exists, and may affect alpha
2355static bool affects_alpha(const SkImageFilter* imf) {
2356 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2357 // ala colorfilters
2358 return imf != NULL;
2359}
2360
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002361bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002362 if (fLooper) {
2363 return false;
2364 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002365 SkXfermode::Mode mode;
2366 if (SkXfermode::AsMode(fXfermode, &mode)) {
2367 switch (mode) {
2368 case SkXfermode::kSrcOver_Mode:
2369 case SkXfermode::kSrcATop_Mode:
2370 case SkXfermode::kDstOut_Mode:
2371 case SkXfermode::kDstOver_Mode:
2372 case SkXfermode::kPlus_Mode:
reedf539b8c2014-11-11 12:51:33 -08002373 if (0 == this->getAlpha()) {
2374 return !affects_alpha(fColorFilter) && !affects_alpha(fImageFilter);
2375 }
2376 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002377 case SkXfermode::kDst_Mode:
2378 return true;
2379 default:
2380 break;
2381 }
2382 }
2383 return false;
2384}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002385
mtkleinfb1fe4f2014-10-07 09:26:10 -07002386uint32_t SkPaint::getHash() const {
2387 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2388 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
2389 SK_COMPILE_ASSERT(offsetof(SkPaint, fBitfields) == 10 * sizeof(void*) + 6 * sizeof(uint32_t),
2390 SkPaint_notPackedTightly);
2391 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(this),
2392 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
2393}