blob: 24281d7cff2ec6cd837d77f4353a1756c5000465 [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@google.com44da42e2011-11-10 20:04:47 +0000807static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000808 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 bounds->join(SkIntToScalar(g.fLeft) + sx,
810 SkIntToScalar(g.fTop),
811 SkIntToScalar(g.fLeft + g.fWidth) + sx,
812 SkIntToScalar(g.fTop + g.fHeight));
813}
814
reed@google.com44da42e2011-11-10 20:04:47 +0000815static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
816 SkScalar sy = Sk48Dot16ToScalar(dy);
817 bounds->join(SkIntToScalar(g.fLeft),
818 SkIntToScalar(g.fTop) + sy,
819 SkIntToScalar(g.fLeft + g.fWidth),
820 SkIntToScalar(g.fTop + g.fHeight) + sy);
821}
822
823typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
824
825// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
826static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
827 SkASSERT(0 == xyIndex || 1 == xyIndex);
828 return (&glyph.fAdvanceX)[xyIndex];
829}
830
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831SkScalar SkPaint::measure_text(SkGlyphCache* cache,
832 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000833 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000835 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000837 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000839 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 return 0;
841 }
842
reed9e96aa02014-10-03 12:44:37 -0700843 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844
reed@google.com44da42e2011-11-10 20:04:47 +0000845 int xyIndex;
846 JoinBoundsProc joinBoundsProc;
847 if (this->isVerticalText()) {
848 xyIndex = 1;
849 joinBoundsProc = join_bounds_y;
850 } else {
851 xyIndex = 0;
852 joinBoundsProc = join_bounds_x;
853 }
854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 int n = 1;
856 const char* stop = (const char*)text + byteLength;
857 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000858 // our accumulated fixed-point advances might overflow 16.16, so we use
859 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
860 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000861 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862
863 SkAutoKern autokern;
864
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000865 if (NULL == bounds) {
866 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 int rsb;
868 for (; text < stop; n++) {
869 rsb = g->fRsbDelta;
870 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000871 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000873 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000875 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 }
877 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000878 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000880 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 int rsb;
882 for (; text < stop; n++) {
883 rsb = g->fRsbDelta;
884 g = &glyphCacheProc(cache, &text);
885 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000886 joinBoundsProc(*g, bounds, x);
887 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000889 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 for (; text < stop; n++) {
891 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000892 joinBoundsProc(*g, bounds, x);
893 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 }
895 }
896 }
897 SkASSERT(text == stop);
898
899 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000900 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901}
902
reed99ae8812014-08-26 11:30:01 -0700903SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 const char* text = (const char*)textData;
905 SkASSERT(text != NULL || length == 0);
906
reed@google.comed43dff2013-06-04 16:56:27 +0000907 SkCanonicalizePaint canon(*this);
908 const SkPaint& paint = canon.getPaint();
909 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000910
reed99ae8812014-08-26 11:30:01 -0700911 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 SkGlyphCache* cache = autoCache.getCache();
913
914 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000915
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000916 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 int tempCount;
918
reed@google.comed43dff2013-06-04 16:56:27 +0000919 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000920 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000922 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
924 bounds->fTop = SkScalarMul(bounds->fTop, scale);
925 bounds->fRight = SkScalarMul(bounds->fRight, scale);
926 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
927 }
928 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000929 } else if (bounds) {
930 // ensure that even if we don't measure_text we still update the bounds
931 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
933 return width;
934}
935
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700937 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000938 if (0 == length || 0 >= maxWidth) {
939 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000941 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 return 0;
943 }
944
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000945 if (0 == fTextSize) {
946 if (measuredWidth) {
947 *measuredWidth = 0;
948 }
949 return length;
950 }
951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 SkASSERT(textD != NULL);
953 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -0700954 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955
reed@google.comed43dff2013-06-04 16:56:27 +0000956 SkCanonicalizePaint canon(*this);
957 const SkPaint& paint = canon.getPaint();
958 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959
reed@google.comed43dff2013-06-04 16:56:27 +0000960 // adjust max in case we changed the textSize in paint
961 if (scale) {
962 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 }
reed@google.com72cf4922011-01-04 19:58:20 +0000964
reed@google.comed43dff2013-06-04 16:56:27 +0000965 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 SkGlyphCache* cache = autoCache.getCache();
967
reed9e96aa02014-10-03 12:44:37 -0700968 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(false);
reed@google.comed43dff2013-06-04 16:56:27 +0000969 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +0000970 // use 64bits for our accumulator, to avoid overflowing 16.16
971 Sk48Dot16 max = SkScalarToFixed(maxWidth);
972 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973
974 SkAutoKern autokern;
975
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000976 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -0700978 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 const char* curr = text;
980 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000981 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000982 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 width -= x;
984 text = curr;
985 break;
986 }
987 rsb = g.fRsbDelta;
988 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000989 } else {
reed9e96aa02014-10-03 12:44:37 -0700990 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +0000992 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000993 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 width -= x;
995 text = curr;
996 break;
997 }
998 }
999 }
reed@google.com72cf4922011-01-04 19:58:20 +00001000
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001001 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001002 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001003 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001005 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 *measuredWidth = scalarWidth;
1007 }
reed@google.com72cf4922011-01-04 19:58:20 +00001008
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -07001010 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011}
1012
1013///////////////////////////////////////////////////////////////////////////////
1014
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001015static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001016 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 return false; // don't detach the cache
1018}
1019
reed@google.com90808e82013-03-19 14:44:54 +00001020static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1021 void* context) {
1022 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023}
1024
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001025SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001026 SkCanonicalizePaint canon(*this);
1027 const SkPaint& paint = canon.getPaint();
1028 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001029
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001030 SkMatrix zoomMatrix, *zoomPtr = NULL;
1031 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 zoomMatrix.setScale(zoom, zoom);
1033 zoomPtr = &zoomMatrix;
1034 }
1035
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001037 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001039 }
reed@google.com72cf4922011-01-04 19:58:20 +00001040
reed@google.comed43dff2013-06-04 16:56:27 +00001041 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001043 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1045 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1046 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1047 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1048 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001049 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1050 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1051 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1052 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001053 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1054 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 }
1056 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1057}
1058
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001059///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001061static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 bounds->set(g.fLeft * scale,
1063 g.fTop * scale,
1064 (g.fLeft + g.fWidth) * scale,
1065 (g.fTop + g.fHeight) * scale);
1066}
1067
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001068int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1069 SkScalar widths[], SkRect bounds[]) const {
1070 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001072 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073
bsalomon49f085d2014-09-05 13:34:00 -07001074 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001076 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079
reed@google.comed43dff2013-06-04 16:56:27 +00001080 SkCanonicalizePaint canon(*this);
1081 const SkPaint& paint = canon.getPaint();
1082 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083
reed@google.comed43dff2013-06-04 16:56:27 +00001084 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 SkGlyphCache* cache = autoCache.getCache();
1086 SkMeasureCacheProc glyphCacheProc;
reed9e96aa02014-10-03 12:44:37 -07001087 glyphCacheProc = paint.getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088
1089 const char* text = (const char*)textData;
1090 const char* stop = text + byteLength;
1091 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001092 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001094 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 // we adjust the widths returned here through auto-kerning
1096 SkAutoKern autokern;
1097 SkFixed prevWidth = 0;
1098
1099 if (scale) {
1100 while (text < stop) {
1101 const SkGlyph& g = glyphCacheProc(cache, &text);
1102 if (widths) {
1103 SkFixed adjust = autokern.adjust(g);
1104
1105 if (count > 0) {
1106 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1107 *widths++ = SkScalarMul(w, scale);
1108 }
reed@google.com44da42e2011-11-10 20:04:47 +00001109 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 }
1111 if (bounds) {
1112 set_bounds(g, bounds++, scale);
1113 }
1114 ++count;
1115 }
1116 if (count > 0 && widths) {
1117 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1118 }
1119 } else {
1120 while (text < stop) {
1121 const SkGlyph& g = glyphCacheProc(cache, &text);
1122 if (widths) {
1123 SkFixed adjust = autokern.adjust(g);
1124
1125 if (count > 0) {
1126 *widths++ = SkFixedToScalar(prevWidth + adjust);
1127 }
reed@google.com44da42e2011-11-10 20:04:47 +00001128 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 }
1130 if (bounds) {
1131 set_bounds(g, bounds++);
1132 }
1133 ++count;
1134 }
1135 if (count > 0 && widths) {
1136 *widths = SkFixedToScalar(prevWidth);
1137 }
1138 }
1139 } else { // no devkern
1140 if (scale) {
1141 while (text < stop) {
1142 const SkGlyph& g = glyphCacheProc(cache, &text);
1143 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001144 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 scale);
1146 }
1147 if (bounds) {
1148 set_bounds(g, bounds++, scale);
1149 }
1150 ++count;
1151 }
1152 } else {
1153 while (text < stop) {
1154 const SkGlyph& g = glyphCacheProc(cache, &text);
1155 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001156 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 }
1158 if (bounds) {
1159 set_bounds(g, bounds++);
1160 }
1161 ++count;
1162 }
1163 }
1164 }
1165
1166 SkASSERT(text == stop);
1167 return count;
1168}
1169
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001170///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
1172#include "SkDraw.h"
1173
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001174void SkPaint::getTextPath(const void* textData, size_t length,
1175 SkScalar x, SkScalar y, SkPath* path) const {
1176 SkASSERT(length == 0 || textData != NULL);
1177
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001179 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001181 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182
djsollen@google.com166e6532012-03-20 14:24:38 +00001183 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 SkMatrix matrix;
1185 SkScalar prevXPos = 0;
1186
1187 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1188 matrix.postTranslate(x, y);
1189 path->reset();
1190
1191 SkScalar xpos;
1192 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001193 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001195 if (iterPath) {
1196 path->addPath(*iterPath, matrix);
1197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 prevXPos = xpos;
1199 }
1200}
1201
reed@google.comca0062e2012-07-20 11:20:32 +00001202void SkPaint::getPosTextPath(const void* textData, size_t length,
1203 const SkPoint pos[], SkPath* path) const {
1204 SkASSERT(length == 0 || textData != NULL);
1205
1206 const char* text = (const char*)textData;
1207 if (text == NULL || length == 0 || path == NULL) {
1208 return;
1209 }
1210
1211 SkTextToPathIter iter(text, length, *this, false);
1212 SkMatrix matrix;
1213 SkPoint prevPos;
1214 prevPos.set(0, 0);
1215
1216 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1217 path->reset();
1218
1219 unsigned int i = 0;
1220 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001221 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001222 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001223 if (iterPath) {
1224 path->addPath(*iterPath, matrix);
1225 }
reed@google.comca0062e2012-07-20 11:20:32 +00001226 prevPos = pos[i];
1227 i++;
1228 }
1229}
1230
reed8893e5f2014-12-15 13:27:26 -08001231SkRect SkPaint::getFontBounds() const {
1232 SkMatrix m;
1233 m.setScale(fTextSize * fTextScaleX, fTextSize);
1234 m.postSkew(fTextSkewX, 0);
1235
1236 SkTypeface* typeface = this->getTypeface();
1237 if (NULL == typeface) {
1238 typeface = SkTypeface::GetDefaultTypeface();
1239 }
1240
1241 SkRect bounds;
1242 m.mapRect(&bounds, typeface->getBounds());
1243 return bounds;
1244}
1245
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001247 SkWriteBuffer* buffer) {
1248 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249}
1250
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001251static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001254 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001255 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001256 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001257 }
1258
reed@google.com65dd8f82011-03-14 13:31:16 +00001259 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001260 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001261 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001262
1263 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
reed@android.com1cdcb512009-08-24 19:11:00 +00001266// if linear-text is on, then we force hinting to be off (since that's sort of
1267// the point of linear-text.
1268static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1269 SkPaint::Hinting h = paint.getHinting();
1270 if (paint.isLinearText()) {
1271 h = SkPaint::kNo_Hinting;
1272 }
1273 return h;
1274}
1275
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001276// return true if the paint is just a single color (i.e. not a shader). If its
1277// a shader, then we can't compute a const luminance for it :(
1278static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001279 SkColor c = paint.getColor();
1280
1281 SkShader* shader = paint.getShader();
1282 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001283 return false;
1284 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001285 if (paint.getColorFilter()) {
1286 c = paint.getColorFilter()->filterColor(c);
1287 }
1288 if (color) {
1289 *color = c;
1290 }
1291 return true;
1292}
1293
reed@google.comce6dbb62012-02-10 22:01:45 +00001294static SkColor computeLuminanceColor(const SkPaint& paint) {
1295 SkColor c;
1296 if (!justAColor(paint, &c)) {
1297 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1298 }
1299 return c;
1300}
reed@google.com813d38b2012-02-13 21:37:57 +00001301
reed@google.comdd43df92012-02-15 14:50:29 +00001302#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1303
reed@google.com4f79b9b2011-09-13 13:23:26 +00001304// Beyond this size, LCD doesn't appreciably improve quality, but it always
1305// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001306#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1307 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1308#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001309
reedb3da83a2014-10-01 12:06:12 -07001310const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1311
reed4942e752014-10-01 13:59:33 -07001312static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001313 if (checkPost2x2) {
1314 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1315 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001316 area *= rec.fTextSize * rec.fTextSize;
1317 return area > gMaxSize2ForLCDText;
1318 } else {
1319 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001320 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001321}
1322
reed@google.com72cf4922011-01-04 19:58:20 +00001323/*
1324 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1325 * that vary only slightly when we create our key into the font cache, since the font scaler
1326 * typically returns the same looking resuts for tiny changes in the matrix.
1327 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001328static SkScalar sk_relax(SkScalar x) {
1329 int n = sk_float_round2int(x * 1024);
1330 return n / 1024.0f;
1331}
1332
reed@android.com36a4c2a2009-07-22 19:52:11 +00001333void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001334 const SkDeviceProperties* deviceProperties,
1335 const SkMatrix* deviceMatrix,
1336 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001337 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001339 SkTypeface* typeface = paint.getTypeface();
1340 if (NULL == typeface) {
1341 typeface = SkTypeface::GetDefaultTypeface();
1342 }
bungemanec730b92014-08-18 07:57:35 -07001343 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 rec->fTextSize = paint.getTextSize();
1345 rec->fPreScaleX = paint.getTextScaleX();
1346 rec->fPreSkewX = paint.getTextSkewX();
1347
reedb3da83a2014-10-01 12:06:12 -07001348 bool checkPost2x2 = false;
1349
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001350 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001351 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1352 if (mask & SkMatrix::kScale_Mask) {
1353 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1354 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1355 checkPost2x2 = true;
1356 } else {
1357 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1358 }
1359 if (mask & SkMatrix::kAffine_Mask) {
1360 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1361 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1362 checkPost2x2 = true;
1363 } else {
1364 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1365 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001366 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1368 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1369 }
reed@google.com72cf4922011-01-04 19:58:20 +00001370
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 SkPaint::Style style = paint.getStyle();
1372 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001373
reed@google.comffe49f52011-11-22 19:42:41 +00001374 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001375
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001376 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001377#ifdef SK_USE_FREETYPE_EMBOLDEN
1378 flags |= SkScalerContext::kEmbolden_Flag;
1379#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001380 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1381 kStdFakeBoldInterpKeys,
1382 kStdFakeBoldInterpValues,
1383 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001385
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001386 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 style = SkPaint::kStrokeAndFill_Style;
1388 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001389 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001391 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001392#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 }
1394
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001395 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001397 }
reed@google.com72cf4922011-01-04 19:58:20 +00001398
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001399 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 rec->fFrameWidth = strokeWidth;
1401 rec->fMiterLimit = paint.getStrokeMiter();
1402 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1403
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001404 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001406 }
1407 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 rec->fFrameWidth = 0;
1409 rec->fMiterLimit = 0;
1410 rec->fStrokeJoin = 0;
1411 }
1412
reed@google.com02b53312011-05-18 19:00:53 +00001413 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1414
reedd54d3fc2014-11-13 14:39:58 -08001415 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001416 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001417 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001418 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001419 } else {
reed4a8126e2014-09-22 07:29:03 -07001420 SkPixelGeometry geometry = deviceProperties
reedb2d77e42014-10-14 08:26:33 -07001421 ? deviceProperties->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001422 : SkSurfacePropsDefaultPixelGeometry();
1423 switch (geometry) {
1424 case kUnknown_SkPixelGeometry:
1425 // eeek, can't support LCD
1426 rec->fMaskFormat = SkMask::kA8_Format;
1427 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1428 break;
1429 case kRGB_H_SkPixelGeometry:
1430 // our default, do nothing.
1431 break;
1432 case kBGR_H_SkPixelGeometry:
1433 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1434 break;
1435 case kRGB_V_SkPixelGeometry:
1436 flags |= SkScalerContext::kLCD_Vertical_Flag;
1437 break;
1438 case kBGR_V_SkPixelGeometry:
1439 flags |= SkScalerContext::kLCD_Vertical_Flag;
1440 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1441 break;
reed@google.com02b53312011-05-18 19:00:53 +00001442 }
1443 }
1444 }
1445
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001446 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001447 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001448 }
1449 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001450 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001451 }
1452 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001453 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001454 }
reed@google.com830a23e2011-11-10 15:20:49 +00001455 if (paint.isVerticalText()) {
1456 flags |= SkScalerContext::kVertical_Flag;
1457 }
reed@google.com8351aab2012-01-18 17:06:35 +00001458 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1459 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1460 }
reed@google.com02b53312011-05-18 19:00:53 +00001461 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001462
reed@google.comffe49f52011-11-22 19:42:41 +00001463 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001464 rec->setHinting(computeHinting(paint));
1465
bungeman@google.com97efada2012-07-30 20:40:50 +00001466 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001467
1468 if (NULL == deviceProperties) {
1469 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1470 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1471 } else {
reedb2d77e42014-10-14 08:26:33 -07001472 rec->setDeviceGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001473
1474 //For now always set the paint gamma equal to the device gamma.
1475 //The math in SkMaskGamma can handle them being different,
1476 //but it requires superluminous masks when
1477 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
reedb2d77e42014-10-14 08:26:33 -07001478 rec->setPaintGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001479 }
1480
1481#ifdef SK_GAMMA_CONTRAST
1482 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001483#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001484 /**
1485 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1486 * With lower values small text appears washed out (though correctly so).
1487 * With higher values lcd fringing is worse and the smoothing effect of
1488 * partial coverage is diminished.
1489 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001490 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001491#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001492
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001493 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001494
reed@android.com36a4c2a2009-07-22 19:52:11 +00001495 /* Allow the fonthost to modify our rec before we use it as a key into the
1496 cache. This way if we're asking for something that they will ignore,
1497 they can modify our rec up front, so we don't create duplicate cache
1498 entries.
1499 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001500 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001501
reed@google.com10d2d4d2012-03-01 22:32:51 +00001502 // be sure to call PostMakeRec(rec) before you actually use it!
1503}
1504
1505/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001506 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1507 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1508 * to hold it until the returned pointer is refed or forgotten.
1509 */
1510SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1511
bungeman@google.comae30f562012-09-11 18:44:55 +00001512static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001513static SkMaskGamma* gMaskGamma = NULL;
1514static SkScalar gContrast = SK_ScalarMin;
1515static SkScalar gPaintGamma = SK_ScalarMin;
1516static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001517/**
1518 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1519 * the returned SkMaskGamma pointer is refed or forgotten.
1520 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001521static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001522 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001523 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1524 if (NULL == gLinearMaskGamma) {
1525 gLinearMaskGamma = SkNEW(SkMaskGamma);
1526 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001527 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001528 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001529 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1530 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001531 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001532 gContrast = contrast;
1533 gPaintGamma = paintGamma;
1534 gDeviceGamma = deviceGamma;
1535 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001536 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001537}
1538
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001539/*static*/ void SkPaint::Term() {
1540 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1541
bungeman@google.comae30f562012-09-11 18:44:55 +00001542 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001543 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001544 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001545 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001546 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1547 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1548 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001549}
1550
bungeman@google.com97efada2012-07-30 20:40:50 +00001551/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001552 * We ensure that the rec is self-consistent and efficient (where possible)
1553 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001554void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001555 /**
1556 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001557 * limits the number of unique entries, and the scaler will only look at
1558 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001559 */
reed@google.comdd43df92012-02-15 14:50:29 +00001560 switch (rec->fMaskFormat) {
reedd54d3fc2014-11-13 14:39:58 -08001561 case SkMask::kLCD16_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001562 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001563 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001564 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001565 break;
1566 }
1567 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001568 // filter down the luminance to a single component, since A8 can't
1569 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001570 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001571 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1572 SkColorGetG(color),
1573 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001574 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001575 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001576 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001577 break;
1578 }
1579 case SkMask::kBW_Format:
1580 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001581 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001582 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584}
1585
1586#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1587
reed@google.com17fb3872011-05-04 14:31:07 +00001588#ifdef SK_DEBUG
1589 #define TEST_DESC
1590#endif
1591
joshualittfd450792015-03-13 08:38:43 -07001592static void write_out_descriptor(SkDescriptor* desc, const SkScalerContext::Rec& rec,
1593 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1594 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1595 const SkRasterizer* ra, SkWriteBuffer* raBuffer,
1596 size_t descSize) {
1597 desc->init();
1598 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1599
1600 if (pe) {
1601 add_flattenable(desc, kPathEffect_SkDescriptorTag, peBuffer);
1602 }
1603 if (mf) {
1604 add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfBuffer);
1605 }
1606 if (ra) {
1607 add_flattenable(desc, kRasterizer_SkDescriptorTag, raBuffer);
1608 }
1609
1610 desc->computeChecksum();
1611}
1612
1613static size_t fill_out_rec(const SkPaint& paint, SkScalerContext::Rec* rec,
1614 const SkDeviceProperties* deviceProperties,
1615 const SkMatrix* deviceMatrix, bool ignoreGamma,
1616 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1617 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1618 const SkRasterizer* ra, SkWriteBuffer* raBuffer) {
1619 SkScalerContext::MakeRec(paint, deviceProperties, deviceMatrix, rec);
1620 if (ignoreGamma) {
1621 rec->ignorePreBlend();
1622 }
1623
1624 int entryCount = 1;
1625 size_t descSize = sizeof(*rec);
1626
1627 if (pe) {
1628 peBuffer->writeFlattenable(pe);
1629 descSize += peBuffer->bytesWritten();
1630 entryCount += 1;
1631 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1632 // seems like we could support kLCD as well at this point...
1633 }
1634 if (mf) {
1635 mfBuffer->writeFlattenable(mf);
1636 descSize += mfBuffer->bytesWritten();
1637 entryCount += 1;
1638 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1639 /* Pre-blend is not currently applied to filtered text.
1640 The primary filter is blur, for which contrast makes no sense,
1641 and for which the destination guess error is more visible.
1642 Also, all existing users of blur have calibrated for linear. */
1643 rec->ignorePreBlend();
1644 }
1645 if (ra) {
1646 raBuffer->writeFlattenable(ra);
1647 descSize += raBuffer->bytesWritten();
1648 entryCount += 1;
1649 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1650 }
1651
1652 ///////////////////////////////////////////////////////////////////////////
1653 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
1654 SkScalerContext::PostMakeRec(paint, rec);
1655
1656 descSize += SkDescriptor::ComputeOverhead(entryCount);
1657 return descSize;
1658}
1659
1660#ifdef TEST_DESC
1661static void test_desc(const SkScalerContext::Rec& rec,
1662 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
1663 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
1664 const SkRasterizer* ra, SkWriteBuffer* raBuffer,
1665 const SkDescriptor* desc, size_t descSize) {
1666 // Check that we completely write the bytes in desc (our key), and that
1667 // there are no uninitialized bytes. If there were, then we would get
1668 // false-misses (or worse, false-hits) in our fontcache.
1669 //
1670 // We do this buy filling 2 others, one with 0s and the other with 1s
1671 // and create those, and then check that all 3 are identical.
1672 SkAutoDescriptor ad1(descSize);
1673 SkAutoDescriptor ad2(descSize);
1674 SkDescriptor* desc1 = ad1.getDesc();
1675 SkDescriptor* desc2 = ad2.getDesc();
1676
1677 memset(desc1, 0x00, descSize);
1678 memset(desc2, 0xFF, descSize);
1679
1680 desc1->init();
1681 desc2->init();
1682 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1683 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1684
1685 if (pe) {
1686 add_flattenable(desc1, kPathEffect_SkDescriptorTag, peBuffer);
1687 add_flattenable(desc2, kPathEffect_SkDescriptorTag, peBuffer);
1688 }
1689 if (mf) {
1690 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, mfBuffer);
1691 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, mfBuffer);
1692 }
1693 if (ra) {
1694 add_flattenable(desc1, kRasterizer_SkDescriptorTag, raBuffer);
1695 add_flattenable(desc2, kRasterizer_SkDescriptorTag, raBuffer);
1696 }
1697
1698 SkASSERT(descSize == desc1->getLength());
1699 SkASSERT(descSize == desc2->getLength());
1700 desc1->computeChecksum();
1701 desc2->computeChecksum();
1702 SkASSERT(!memcmp(desc, desc1, descSize));
1703 SkASSERT(!memcmp(desc, desc2, descSize));
1704}
1705#endif
1706
1707/* see the note on ignoreGamma on descriptorProc */
1708const SkData* SkPaint::getScalerContextDescriptor(const SkDeviceProperties* deviceProperties,
1709 const SkMatrix* deviceMatrix,
1710 bool ignoreGamma) const {
1711 SkScalerContext::Rec rec;
1712
1713 SkPathEffect* pe = this->getPathEffect();
1714 SkMaskFilter* mf = this->getMaskFilter();
1715 SkRasterizer* ra = this->getRasterizer();
1716
1717 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
1718 size_t descSize = fill_out_rec(*this, &rec, deviceProperties, deviceMatrix, ignoreGamma,
1719 pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
1720
1721 SkASSERT(SkAlign4(descSize) == descSize);
1722 SkData* data = SkData::NewUninitialized(descSize);
1723 SkDescriptor* desc = reinterpret_cast<SkDescriptor*>(data->writable_data());
1724
1725 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
1726
1727 SkASSERT(descSize == desc->getLength());
1728
1729#ifdef TEST_DESC
1730 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
1731#endif
1732
1733 return data;
1734}
1735
reed@google.comffe49f52011-11-22 19:42:41 +00001736/*
1737 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001738 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1739 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001740 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001741void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1742 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001743 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001744 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745 SkScalerContext::Rec rec;
1746
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747 SkPathEffect* pe = this->getPathEffect();
1748 SkMaskFilter* mf = this->getMaskFilter();
1749 SkRasterizer* ra = this->getRasterizer();
1750
joshualittfd450792015-03-13 08:38:43 -07001751 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
1752 size_t descSize = fill_out_rec(*this, &rec, deviceProperties, deviceMatrix, ignoreGamma,
1753 pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754
1755 SkAutoDescriptor ad(descSize);
1756 SkDescriptor* desc = ad.getDesc();
1757
joshualittfd450792015-03-13 08:38:43 -07001758 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759
1760 SkASSERT(descSize == desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761
reed@google.com17fb3872011-05-04 14:31:07 +00001762#ifdef TEST_DESC
joshualittfd450792015-03-13 08:38:43 -07001763 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
reed@google.com17fb3872011-05-04 14:31:07 +00001764#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001765
reed@google.com90808e82013-03-19 14:44:54 +00001766 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767}
1768
bungeman@google.com532470f2013-01-22 19:25:14 +00001769SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
jvanverth2d2a68c2014-06-10 06:42:56 -07001770 const SkMatrix* deviceMatrix,
1771 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 SkGlyphCache* cache;
jvanverth2d2a68c2014-06-10 06:42:56 -07001773 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 return cache;
1775}
1776
bungeman@google.com97efada2012-07-30 20:40:50 +00001777/**
1778 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1779 */
1780//static
1781SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1782 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001783 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1784 rec.getPaintGamma(),
1785 rec.getDeviceGamma());
1786 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001787}
1788
jvanverth2d2a68c2014-06-10 06:42:56 -07001789size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1790 SkScalar deviceGamma, int* width, int* height) {
1791 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1792 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1793 paintGamma,
1794 deviceGamma);
1795
1796 maskGamma.getGammaTableDimensions(width, height);
1797 size_t size = (*width)*(*height)*sizeof(uint8_t);
1798
1799 return size;
1800}
1801
1802void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1803 void* data) {
1804 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1805 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1806 paintGamma,
1807 deviceGamma);
1808 int width, height;
1809 maskGamma.getGammaTableDimensions(&width, &height);
1810 size_t size = width*height*sizeof(uint8_t);
1811 const uint8_t* gammaTables = maskGamma.getGammaTables();
1812 memcpy(data, gammaTables, size);
1813}
1814
1815
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816///////////////////////////////////////////////////////////////////////////////
1817
1818#include "SkStream.h"
1819
reed@android.comaefd2bc2009-03-30 21:02:14 +00001820static uintptr_t asint(const void* p) {
1821 return reinterpret_cast<uintptr_t>(p);
1822}
1823
1824union Scalar32 {
1825 SkScalar fScalar;
1826 uint32_t f32;
1827};
1828
1829static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1830 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1831 Scalar32 tmp;
1832 tmp.fScalar = value;
1833 *ptr = tmp.f32;
1834 return ptr + 1;
1835}
1836
1837static SkScalar read_scalar(const uint32_t*& ptr) {
1838 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1839 Scalar32 tmp;
1840 tmp.f32 = *ptr++;
1841 return tmp.fScalar;
1842}
1843
1844static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1845 SkASSERT(a == (uint8_t)a);
1846 SkASSERT(b == (uint8_t)b);
1847 SkASSERT(c == (uint8_t)c);
1848 SkASSERT(d == (uint8_t)d);
1849 return (a << 24) | (b << 16) | (c << 8) | d;
1850}
1851
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001852#ifdef SK_DEBUG
1853 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1854 SkASSERT(bitCount > 0 && bitCount <= 32);
1855 uint32_t mask = ~0U;
1856 mask >>= (32 - bitCount);
1857 SkASSERT(0 == (value & ~mask));
1858 }
1859#else
1860 #define ASSERT_FITS_IN(value, bitcount)
1861#endif
1862
reed@android.comaefd2bc2009-03-30 21:02:14 +00001863enum FlatFlags {
mtklein88fd0fb2014-12-01 06:56:38 -08001864 kHasTypeface_FlatFlag = 0x1,
1865 kHasEffects_FlatFlag = 0x2,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001866
mtklein88fd0fb2014-12-01 06:56:38 -08001867 kFlatFlagMask = 0x3,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001868};
1869
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001870enum BitsPerField {
1871 kFlags_BPF = 16,
1872 kHint_BPF = 2,
1873 kAlign_BPF = 2,
1874 kFilter_BPF = 2,
1875 kFlatFlags_BPF = 3,
1876};
1877
1878static inline int BPF_Mask(int bits) {
1879 return (1 << bits) - 1;
1880}
1881
1882static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1883 unsigned filter, unsigned flatFlags) {
1884 ASSERT_FITS_IN(flags, kFlags_BPF);
1885 ASSERT_FITS_IN(hint, kHint_BPF);
1886 ASSERT_FITS_IN(align, kAlign_BPF);
1887 ASSERT_FITS_IN(filter, kFilter_BPF);
1888 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1889
1890 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1891 // add more bits in the future.
1892 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1893}
1894
1895static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1896 paint->setFlags(packed >> 16);
1897 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1898 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
reed93a12152015-03-16 10:08:34 -07001899 paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001900 return (FlatFlags)(packed & kFlatFlagMask);
1901}
1902
reed@android.comaefd2bc2009-03-30 21:02:14 +00001903// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00001904static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00001905 1 * sizeof(SkColor) +
1906 1 * sizeof(uint16_t) +
1907 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001908
1909/* To save space/time, we analyze the paint, and write a truncated version of
1910 it if there are not tricky elements like shaders, etc.
1911 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001912void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001913 uint8_t flatFlags = 0;
1914 if (this->getTypeface()) {
1915 flatFlags |= kHasTypeface_FlatFlag;
1916 }
1917 if (asint(this->getPathEffect()) |
1918 asint(this->getShader()) |
1919 asint(this->getXfermode()) |
1920 asint(this->getMaskFilter()) |
1921 asint(this->getColorFilter()) |
1922 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001923 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001924 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001925 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001926 flatFlags |= kHasEffects_FlatFlag;
1927 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001928
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001929 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1930 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001931
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001932 ptr = write_scalar(ptr, this->getTextSize());
1933 ptr = write_scalar(ptr, this->getTextScaleX());
1934 ptr = write_scalar(ptr, this->getTextSkewX());
1935 ptr = write_scalar(ptr, this->getStrokeWidth());
1936 ptr = write_scalar(ptr, this->getStrokeMiter());
1937 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001938
1939 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
reed93a12152015-03-16 10:08:34 -07001940 this->getFilterQuality(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001941 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1942 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001943
1944 // now we're done with ptr and the (pre)reserved space. If we need to write
1945 // additional fields, use the buffer directly
1946 if (flatFlags & kHasTypeface_FlatFlag) {
1947 buffer.writeTypeface(this->getTypeface());
1948 }
1949 if (flatFlags & kHasEffects_FlatFlag) {
1950 buffer.writeFlattenable(this->getPathEffect());
1951 buffer.writeFlattenable(this->getShader());
1952 buffer.writeFlattenable(this->getXfermode());
1953 buffer.writeFlattenable(this->getMaskFilter());
1954 buffer.writeFlattenable(this->getColorFilter());
1955 buffer.writeFlattenable(this->getRasterizer());
1956 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001957 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00001958
1959 if (fAnnotation) {
1960 buffer.writeBool(true);
1961 fAnnotation->writeToBuffer(buffer);
1962 } else {
1963 buffer.writeBool(false);
1964 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001965 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966}
1967
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001968void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001969 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1970 const void* podData = buffer.skip(kPODPaintSize);
1971 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001972
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001973 // the order we read must match the order we wrote in flatten()
1974 this->setTextSize(read_scalar(pod));
1975 this->setTextScaleX(read_scalar(pod));
1976 this->setTextSkewX(read_scalar(pod));
1977 this->setStrokeWidth(read_scalar(pod));
1978 this->setStrokeMiter(read_scalar(pod));
1979 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00001980
mtklein88fd0fb2014-12-01 06:56:38 -08001981 unsigned flatFlags = unpack_paint_flags(this, *pod++);
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001982
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001983 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001984 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1985 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1986 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1987 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001988
1989 if (flatFlags & kHasTypeface_FlatFlag) {
1990 this->setTypeface(buffer.readTypeface());
1991 } else {
1992 this->setTypeface(NULL);
1993 }
1994
1995 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00001996 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
1997 SkSafeUnref(this->setShader(buffer.readShader()));
1998 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
1999 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2000 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2001 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2002 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2003 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002004
reed@google.com0cd2ac62013-10-14 20:02:44 +00002005 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002006 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00002007 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002008 } else {
2009 this->setPathEffect(NULL);
2010 this->setShader(NULL);
2011 this->setXfermode(NULL);
2012 this->setMaskFilter(NULL);
2013 this->setColorFilter(NULL);
2014 this->setRasterizer(NULL);
2015 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002016 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002017 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002018}
2019
2020///////////////////////////////////////////////////////////////////////////////
2021
reed@google.com82065d62011-02-07 15:30:46 +00002022SkShader* SkPaint::setShader(SkShader* shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002023 SkRefCnt_SafeAssign(fShader, shader);
2024 return shader;
2025}
2026
reed@google.com82065d62011-02-07 15:30:46 +00002027SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 SkRefCnt_SafeAssign(fColorFilter, filter);
2029 return filter;
2030}
2031
reed@google.com82065d62011-02-07 15:30:46 +00002032SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 SkRefCnt_SafeAssign(fXfermode, mode);
2034 return mode;
2035}
2036
reed@android.com0baf1932009-06-24 12:41:42 +00002037SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002038 SkSafeUnref(fXfermode);
2039 fXfermode = SkXfermode::Create(mode);
2040 return fXfermode;
2041}
2042
reed@google.com82065d62011-02-07 15:30:46 +00002043SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 SkRefCnt_SafeAssign(fPathEffect, effect);
2045 return effect;
2046}
2047
reed@google.com82065d62011-02-07 15:30:46 +00002048SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002049 SkRefCnt_SafeAssign(fMaskFilter, filter);
2050 return filter;
2051}
2052
reed@google.com82065d62011-02-07 15:30:46 +00002053///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054
reed05d90442015-02-12 13:35:52 -08002055bool SkPaint::getFillPath(const SkPath& src, SkPath* dst, const SkRect* cullRect,
2056 SkScalar resScale) const {
2057 SkStrokeRec rec(*this, resScale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058
reed@google.comfd4be262012-05-25 01:04:12 +00002059 const SkPath* srcPtr = &src;
2060 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002061
reed@google.com4bbdeac2013-01-24 21:03:11 +00002062 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002063 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064 }
2065
reed@google.comfd4be262012-05-25 01:04:12 +00002066 if (!rec.applyToPath(dst, *srcPtr)) {
2067 if (srcPtr == &tmpPath) {
2068 // If path's were copy-on-write, this trick would not be needed.
2069 // As it is, we want to save making a deep-copy from tmpPath -> dst
2070 // since we know we're just going to delete tmpPath when we return,
2071 // so the swap saves that copy.
2072 dst->swap(tmpPath);
2073 } else {
2074 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 }
2076 }
reed@google.comfd4be262012-05-25 01:04:12 +00002077 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078}
2079
reed@google.come4f10a72012-05-15 20:47:50 +00002080const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002081 SkRect* storage,
2082 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002084
reed@google.come4f10a72012-05-15 20:47:50 +00002085 const SkRect* src = &origSrc;
2086
reed@google.com9efd9a02012-01-30 15:41:43 +00002087 if (this->getLooper()) {
2088 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002089 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002090 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002092
reed@google.come4f10a72012-05-15 20:47:50 +00002093 SkRect tmpSrc;
2094 if (this->getPathEffect()) {
2095 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2096 src = &tmpSrc;
2097 }
2098
reed@google.coma584aed2012-05-16 14:06:02 +00002099 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002100 // since we're stroked, outset the rect by the radius (and join type)
2101 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2102 if (0 == radius) { // hairline
2103 radius = SK_Scalar1;
2104 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2105 SkScalar scale = this->getStrokeMiter();
2106 if (scale > SK_Scalar1) {
2107 radius = SkScalarMul(radius, scale);
2108 }
2109 }
reed@google.come4f10a72012-05-15 20:47:50 +00002110 storage->set(src->fLeft - radius, src->fTop - radius,
2111 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002112 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002113 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002114 }
2115
reed@google.com9efd9a02012-01-30 15:41:43 +00002116 if (this->getMaskFilter()) {
2117 this->getMaskFilter()->computeFastBounds(*storage, storage);
2118 }
2119
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002120 if (this->getImageFilter()) {
2121 this->getImageFilter()->computeFastBounds(*storage, storage);
2122 }
2123
reed@android.comd252db02009-04-01 18:31:44 +00002124 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125}
2126
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002127#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002128
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002129void SkPaint::toString(SkString* str) const {
2130 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2131
2132 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002133 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002134 SkDynamicMemoryWStream ostream;
2135 typeface->serialize(&ostream);
scroggoa1193e42015-01-21 12:09:53 -08002136 SkAutoTDelete<SkStreamAsset> istream(ostream.detachAsStream());
bungemand71b7572014-09-18 10:55:32 -07002137 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002138
2139 str->append("<dt>Font Family Name:</dt><dd>");
2140 str->append(descriptor.getFamilyName());
2141 str->append("</dd><dt>Font Full Name:</dt><dd>");
2142 str->append(descriptor.getFullName());
2143 str->append("</dd><dt>Font PS Name:</dt><dd>");
2144 str->append(descriptor.getPostscriptName());
2145 str->append("</dd><dt>Font File Name:</dt><dd>");
2146 str->append(descriptor.getFontFileName());
2147 str->append("</dd>");
2148 }
2149
2150 str->append("<dt>TextSize:</dt><dd>");
2151 str->appendScalar(this->getTextSize());
2152 str->append("</dd>");
2153
2154 str->append("<dt>TextScaleX:</dt><dd>");
2155 str->appendScalar(this->getTextScaleX());
2156 str->append("</dd>");
2157
2158 str->append("<dt>TextSkewX:</dt><dd>");
2159 str->appendScalar(this->getTextSkewX());
2160 str->append("</dd>");
2161
2162 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002163 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002164 str->append("<dt>PathEffect:</dt><dd>");
robertphillips42dbfa82015-01-26 06:08:52 -08002165 pathEffect->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002166 str->append("</dd>");
2167 }
2168
2169 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002170 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002171 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002172 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002173 str->append("</dd>");
2174 }
2175
2176 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002177 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002178 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002179 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002180 str->append("</dd>");
2181 }
2182
2183 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002184 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002185 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002186 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002187 str->append("</dd>");
2188 }
2189
2190 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002191 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002192 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002193 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002194 str->append("</dd>");
2195 }
2196
2197 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002198 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002199 str->append("<dt>Rasterizer:</dt><dd>");
2200 str->append("</dd>");
2201 }
2202
2203 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002204 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002205 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002206 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002207 str->append("</dd>");
2208 }
2209
2210 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002211 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002212 str->append("<dt>ImageFilter:</dt><dd>");
robertphillipsf3f5bad2014-12-19 13:49:15 -08002213 imageFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002214 str->append("</dd>");
2215 }
2216
2217 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002218 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002219 str->append("<dt>Annotation:</dt><dd>");
2220 str->append("</dd>");
2221 }
2222
2223 str->append("<dt>Color:</dt><dd>0x");
2224 SkColor color = this->getColor();
2225 str->appendHex(color);
2226 str->append("</dd>");
2227
2228 str->append("<dt>Stroke Width:</dt><dd>");
2229 str->appendScalar(this->getStrokeWidth());
2230 str->append("</dd>");
2231
2232 str->append("<dt>Stroke Miter:</dt><dd>");
2233 str->appendScalar(this->getStrokeMiter());
2234 str->append("</dd>");
2235
2236 str->append("<dt>Flags:</dt><dd>(");
2237 if (this->getFlags()) {
2238 bool needSeparator = false;
2239 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002240 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2241 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2242 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2243 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2244 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2245 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2246 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2247 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2248 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2249 "EmbeddedBitmapText", &needSeparator);
2250 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2251 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2252 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2253 "GenA8FromLCD", &needSeparator);
2254 } else {
2255 str->append("None");
2256 }
2257 str->append(")</dd>");
2258
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002259 str->append("<dt>FilterLevel:</dt><dd>");
reed93a12152015-03-16 10:08:34 -07002260 static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High" };
2261 str->append(gFilterQualityStrings[this->getFilterQuality()]);
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002262 str->append("</dd>");
2263
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002264 str->append("<dt>TextAlign:</dt><dd>");
2265 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2266 str->append(gTextAlignStrings[this->getTextAlign()]);
2267 str->append("</dd>");
2268
2269 str->append("<dt>CapType:</dt><dd>");
2270 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2271 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2272 str->append("</dd>");
2273
2274 str->append("<dt>JoinType:</dt><dd>");
2275 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2276 str->append(gJoinStrings[this->getStrokeJoin()]);
2277 str->append("</dd>");
2278
2279 str->append("<dt>Style:</dt><dd>");
2280 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2281 str->append(gStyleStrings[this->getStyle()]);
2282 str->append("</dd>");
2283
2284 str->append("<dt>TextEncoding:</dt><dd>");
2285 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2286 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2287 str->append("</dd>");
2288
2289 str->append("<dt>Hinting:</dt><dd>");
2290 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2291 str->append(gHintingStrings[this->getHinting()]);
2292 str->append("</dd>");
2293
2294 str->append("</dd></dl></dl>");
2295}
2296#endif
2297
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002298///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002300static bool has_thick_frame(const SkPaint& paint) {
2301 return paint.getStrokeWidth() > 0 &&
2302 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303}
2304
2305SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2306 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002307 bool applyStrokeAndPathEffects)
2308 : fPaint(paint) {
reed9e96aa02014-10-03 12:44:37 -07002309 fGlyphCacheProc = paint.getMeasureCacheProc(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310
djsollen@google.com166e6532012-03-20 14:24:38 +00002311 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2313
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002314 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317
djsollen@google.com166e6532012-03-20 14:24:38 +00002318 // can't use our canonical size if we need to apply patheffects
2319 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2321 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002322 if (has_thick_frame(fPaint)) {
2323 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2324 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002325 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002327 }
reed@google.com72cf4922011-01-04 19:58:20 +00002328
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002329 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 fPaint.setStyle(SkPaint::kFill_Style);
2331 fPaint.setPathEffect(NULL);
2332 }
2333
jvanverth2d2a68c2014-06-10 06:42:56 -07002334 fCache = fPaint.detachCache(NULL, NULL, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002335
2336 SkPaint::Style style = SkPaint::kFill_Style;
2337 SkPathEffect* pe = NULL;
2338
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002339 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 style = paint.getStyle(); // restore
2341 pe = paint.getPathEffect(); // restore
2342 }
2343 fPaint.setStyle(style);
2344 fPaint.setPathEffect(pe);
2345 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2346
2347 // now compute fXOffset if needed
2348
2349 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002350 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002352 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2353 &count, NULL), fScale);
2354 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 xOffset = -width;
2358 }
2359 fXPos = xOffset;
2360 fPrevAdvance = 0;
2361
2362 fText = text;
2363 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002364
reed@google.com44da42e2011-11-10 20:04:47 +00002365 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366}
2367
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002368SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 SkGlyphCache::AttachCache(fCache);
2370}
2371
reed@google.com7b4531f2012-08-07 15:53:00 +00002372bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2373 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2375
2376 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002377 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002379 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002380 if (path) {
2381 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002382 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002383 } else {
2384 if (path) {
2385 *path = NULL;
2386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002388 if (xpos) {
2389 *xpos = fXPos;
2390 }
2391 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002393 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002395
2396///////////////////////////////////////////////////////////////////////////////
2397
reedf539b8c2014-11-11 12:51:33 -08002398// return true if the filter exists, and may affect alpha
2399static bool affects_alpha(const SkColorFilter* cf) {
2400 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2401}
2402
2403// return true if the filter exists, and may affect alpha
2404static bool affects_alpha(const SkImageFilter* imf) {
2405 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2406 // ala colorfilters
2407 return imf != NULL;
2408}
2409
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002410bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002411 if (fLooper) {
2412 return false;
2413 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002414 SkXfermode::Mode mode;
2415 if (SkXfermode::AsMode(fXfermode, &mode)) {
2416 switch (mode) {
2417 case SkXfermode::kSrcOver_Mode:
2418 case SkXfermode::kSrcATop_Mode:
2419 case SkXfermode::kDstOut_Mode:
2420 case SkXfermode::kDstOver_Mode:
2421 case SkXfermode::kPlus_Mode:
reedf539b8c2014-11-11 12:51:33 -08002422 if (0 == this->getAlpha()) {
2423 return !affects_alpha(fColorFilter) && !affects_alpha(fImageFilter);
2424 }
2425 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002426 case SkXfermode::kDst_Mode:
2427 return true;
2428 default:
2429 break;
2430 }
2431 }
2432 return false;
2433}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002434
mtkleinfb1fe4f2014-10-07 09:26:10 -07002435uint32_t SkPaint::getHash() const {
2436 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2437 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
2438 SK_COMPILE_ASSERT(offsetof(SkPaint, fBitfields) == 10 * sizeof(void*) + 6 * sizeof(uint32_t),
2439 SkPaint_notPackedTightly);
2440 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(this),
2441 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
2442}