blob: 44b2928c25bb7b4cbf1c318f5f7bfee898472946 [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"
robertphillips@google.com791f12e2013-02-14 13:53:53 +00009#include "SkAutoKern.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000011#include "SkData.h"
rmistryc4b84ae2014-06-23 06:59:15 -070012#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000013#include "SkFontDescriptor.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000014#include "SkGlyphCache.h"
reed@google.com15356a62011-11-03 19:29:08 +000015#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000017#include "SkMaskGamma.h"
mtklein1b249332015-07-07 12:21:21 -070018#include "SkMutex.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000019#include "SkReadBuffer.h"
20#include "SkWriteBuffer.h"
mtklein4e976072016-08-08 09:06:27 -070021#include "SkOpts.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000022#include "SkPaintDefaults.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkPathEffect.h"
24#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000025#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000027#include "SkShader.h"
28#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkStroke.h"
fmalitaeae6a912016-07-28 09:47:24 -070030#include "SkStrokeRec.h"
31#include "SkSurfacePriv.h"
32#include "SkTextBlob.h"
33#include "SkTextBlobRunIterator.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000034#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000035#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000036#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000037#include "SkTypeface.h"
38#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000039
reed9a878a02015-12-27 12:47:25 -080040static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) {
41 return cond ? bits | mask : bits & ~mask;
42}
43
reed@google.coma3237872011-07-05 19:20:48 +000044// define this to get a printf for out-of-range parameter in setters
45// e.g. setTextSize(-1)
46//#define SK_REPORT_API_RANGE_CHECK
47
reed@android.coma3122b92009-08-13 20:38:25 +000048SkPaint::SkPaint() {
reedf59eab22014-07-14 14:39:15 -070049 fTextSize = SkPaintDefaults_TextSize;
50 fTextScaleX = SK_Scalar1;
51 fTextSkewX = 0;
52 fColor = SK_ColorBLACK;
53 fWidth = 0;
54 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000055
56 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070057 fBitfieldsUInt = 0;
58 fBitfields.fFlags = SkPaintDefaults_Flags;
59 fBitfields.fCapType = kDefault_Cap;
60 fBitfields.fJoinType = kDefault_Join;
61 fBitfields.fTextAlign = kLeft_Align;
62 fBitfields.fStyle = kFill_Style;
63 fBitfields.fTextEncoding = kUTF8_TextEncoding;
64 fBitfields.fHinting = SkPaintDefaults_Hinting;
reed@android.com8a1c16f2008-12-17 15:59:43 +000065}
66
reeda5ab9ec2016-03-06 18:10:48 -080067SkPaint::SkPaint(const SkPaint& src)
68#define COPY(field) field(src.field)
69 : COPY(fTypeface)
70 , COPY(fPathEffect)
71 , COPY(fShader)
72 , COPY(fXfermode)
73 , COPY(fMaskFilter)
74 , COPY(fColorFilter)
75 , COPY(fRasterizer)
76 , COPY(fLooper)
77 , COPY(fImageFilter)
78 , COPY(fTextSize)
79 , COPY(fTextScaleX)
80 , COPY(fTextSkewX)
81 , COPY(fColor)
82 , COPY(fWidth)
83 , COPY(fMiterLimit)
84 , COPY(fBitfields)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000085#undef COPY
reeda5ab9ec2016-03-06 18:10:48 -080086{}
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
bungemanccce0e02016-02-07 14:37:23 -080088SkPaint::SkPaint(SkPaint&& src) {
89#define MOVE(field) field = std::move(src.field)
reeda5ab9ec2016-03-06 18:10:48 -080090 MOVE(fTypeface);
91 MOVE(fPathEffect);
92 MOVE(fShader);
93 MOVE(fXfermode);
94 MOVE(fMaskFilter);
95 MOVE(fColorFilter);
96 MOVE(fRasterizer);
97 MOVE(fLooper);
98 MOVE(fImageFilter);
bungemanccce0e02016-02-07 14:37:23 -080099 MOVE(fTextSize);
100 MOVE(fTextScaleX);
101 MOVE(fTextSkewX);
102 MOVE(fColor);
103 MOVE(fWidth);
104 MOVE(fMiterLimit);
105 MOVE(fBitfields);
bungemanccce0e02016-02-07 14:37:23 -0800106#undef MOVE
bungemanccce0e02016-02-07 14:37:23 -0800107}
108
reeda5ab9ec2016-03-06 18:10:48 -0800109SkPaint::~SkPaint() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110
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
reeda5ab9ec2016-03-06 18:10:48 -0800116#define ASSIGN(field) field = src.field
117 ASSIGN(fTypeface);
118 ASSIGN(fPathEffect);
119 ASSIGN(fShader);
120 ASSIGN(fXfermode);
121 ASSIGN(fMaskFilter);
122 ASSIGN(fColorFilter);
123 ASSIGN(fRasterizer);
124 ASSIGN(fLooper);
125 ASSIGN(fImageFilter);
126 ASSIGN(fTextSize);
127 ASSIGN(fTextScaleX);
128 ASSIGN(fTextSkewX);
129 ASSIGN(fColor);
130 ASSIGN(fWidth);
131 ASSIGN(fMiterLimit);
132 ASSIGN(fBitfields);
133#undef ASSIGN
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134
135 return *this;
136}
137
bungemanccce0e02016-02-07 14:37:23 -0800138SkPaint& SkPaint::operator=(SkPaint&& src) {
139 if (this == &src) {
140 return *this;
141 }
142
143#define MOVE(field) field = std::move(src.field)
reeda5ab9ec2016-03-06 18:10:48 -0800144 MOVE(fTypeface);
145 MOVE(fPathEffect);
146 MOVE(fShader);
147 MOVE(fXfermode);
148 MOVE(fMaskFilter);
149 MOVE(fColorFilter);
150 MOVE(fRasterizer);
151 MOVE(fLooper);
152 MOVE(fImageFilter);
bungemanccce0e02016-02-07 14:37:23 -0800153 MOVE(fTextSize);
154 MOVE(fTextScaleX);
155 MOVE(fTextSkewX);
156 MOVE(fColor);
157 MOVE(fWidth);
158 MOVE(fMiterLimit);
159 MOVE(fBitfields);
reeda5ab9ec2016-03-06 18:10:48 -0800160#undef MOVE
bungemanccce0e02016-02-07 14:37:23 -0800161
162 return *this;
bungemanccce0e02016-02-07 14:37:23 -0800163}
164
robertphillips@google.comb2657412013-08-07 22:36:29 +0000165bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000166#define EQUAL(field) (a.field == b.field)
mtklein610a0152014-09-25 11:57:53 -0700167 return EQUAL(fTypeface)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000168 && EQUAL(fPathEffect)
169 && EQUAL(fShader)
170 && EQUAL(fXfermode)
171 && EQUAL(fMaskFilter)
172 && EQUAL(fColorFilter)
173 && EQUAL(fRasterizer)
174 && EQUAL(fLooper)
175 && EQUAL(fImageFilter)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000176 && EQUAL(fTextSize)
177 && EQUAL(fTextScaleX)
178 && EQUAL(fTextSkewX)
179 && EQUAL(fColor)
180 && EQUAL(fWidth)
181 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700182 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000183 ;
184#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000185}
186
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000187void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkPaint init;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000190}
191
reedf803da12015-01-23 05:58:07 -0800192void SkPaint::setFilterQuality(SkFilterQuality quality) {
193 fBitfields.fFilterQuality = quality;
reed@google.comc9683152013-07-18 13:47:01 +0000194}
195
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000196void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700197 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198}
199
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000200void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700201 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202}
203
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000204void SkPaint::setAntiAlias(bool doAA) {
reed9a878a02015-12-27 12:47:25 -0800205 this->setFlags(set_clear_mask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206}
207
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000208void SkPaint::setDither(bool doDither) {
reed9a878a02015-12-27 12:47:25 -0800209 this->setFlags(set_clear_mask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210}
211
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000212void SkPaint::setSubpixelText(bool doSubpixel) {
reed9a878a02015-12-27 12:47:25 -0800213 this->setFlags(set_clear_mask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214}
215
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000216void SkPaint::setLCDRenderText(bool doLCDRender) {
reed9a878a02015-12-27 12:47:25 -0800217 this->setFlags(set_clear_mask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000218}
219
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000220void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reed9a878a02015-12-27 12:47:25 -0800221 this->setFlags(set_clear_mask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000222}
223
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000224void SkPaint::setAutohinted(bool useAutohinter) {
reed9a878a02015-12-27 12:47:25 -0800225 this->setFlags(set_clear_mask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000226}
227
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000228void SkPaint::setLinearText(bool doLinearText) {
reed9a878a02015-12-27 12:47:25 -0800229 this->setFlags(set_clear_mask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230}
231
reed@google.com830a23e2011-11-10 15:20:49 +0000232void SkPaint::setVerticalText(bool doVertical) {
reed9a878a02015-12-27 12:47:25 -0800233 this->setFlags(set_clear_mask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000234}
235
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000236void SkPaint::setUnderlineText(bool doUnderline) {
reed9a878a02015-12-27 12:47:25 -0800237 this->setFlags(set_clear_mask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238}
239
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000240void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed9a878a02015-12-27 12:47:25 -0800241 this->setFlags(set_clear_mask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242}
243
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000244void SkPaint::setFakeBoldText(bool doFakeBold) {
reed9a878a02015-12-27 12:47:25 -0800245 this->setFlags(set_clear_mask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246}
247
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000248void SkPaint::setDevKernText(bool doDevKern) {
reed9a878a02015-12-27 12:47:25 -0800249 this->setFlags(set_clear_mask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250}
251
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000252void SkPaint::setStyle(Style style) {
253 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700254 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000255 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000256#ifdef SK_REPORT_API_RANGE_CHECK
257 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
258#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 fColor = color;
264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000267 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
268 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269}
270
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000271void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000272 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273}
274
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000275void SkPaint::setStrokeWidth(SkScalar width) {
276 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000278 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000279#ifdef SK_REPORT_API_RANGE_CHECK
280 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
281#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283}
284
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000285void SkPaint::setStrokeMiter(SkScalar limit) {
286 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000288 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000289#ifdef SK_REPORT_API_RANGE_CHECK
290 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
291#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293}
294
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000295void SkPaint::setStrokeCap(Cap ct) {
296 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700297 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000298 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000299#ifdef SK_REPORT_API_RANGE_CHECK
300 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
301#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000302 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303}
304
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000305void SkPaint::setStrokeJoin(Join jt) {
306 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700307 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000308 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000309#ifdef SK_REPORT_API_RANGE_CHECK
310 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
311#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313}
314
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000315///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000317void SkPaint::setTextAlign(Align align) {
318 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700319 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000320 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000321#ifdef SK_REPORT_API_RANGE_CHECK
322 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
323#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325}
326
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000327void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000328 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000330 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000331#ifdef SK_REPORT_API_RANGE_CHECK
332 SkDebugf("SkPaint::setTextSize() called with negative value\n");
333#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335}
336
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000337void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 fTextScaleX = scaleX;
339}
340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fTextSkewX = skewX;
343}
344
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000345void SkPaint::setTextEncoding(TextEncoding encoding) {
346 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700347 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000349#ifdef SK_REPORT_API_RANGE_CHECK
350 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
351#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353}
354
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000355///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
reeda5ab9ec2016-03-06 18:10:48 -0800357#define MOVE_FIELD(Field) void SkPaint::set##Field(sk_sp<Sk##Field> f) { f##Field = std::move(f); }
358MOVE_FIELD(Typeface)
359MOVE_FIELD(Rasterizer)
360MOVE_FIELD(ImageFilter)
361MOVE_FIELD(Shader)
362MOVE_FIELD(ColorFilter)
363MOVE_FIELD(Xfermode)
364MOVE_FIELD(PathEffect)
365MOVE_FIELD(MaskFilter)
366#undef MOVE_FIELD
367void SkPaint::setLooper(sk_sp<SkDrawLooper> looper) { fLooper = std::move(looper); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368
reeda5ab9ec2016-03-06 18:10:48 -0800369#define SET_PTR(Field) \
370 Sk##Field* SkPaint::set##Field(Sk##Field* f) { \
371 this->f##Field.reset(SkSafeRef(f)); \
372 return f; \
373 }
bungeman13b9c952016-05-12 10:09:30 -0700374#ifdef SK_SUPPORT_LEGACY_TYPEFACE_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800375SET_PTR(Typeface)
bungeman13b9c952016-05-12 10:09:30 -0700376#endif
reed7b380d02016-03-21 13:25:16 -0700377#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800378SET_PTR(Rasterizer)
reed7b380d02016-03-21 13:25:16 -0700379#endif
reeda5ab9ec2016-03-06 18:10:48 -0800380SET_PTR(ImageFilter)
reedfe630452016-03-25 09:08:00 -0700381#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800382SET_PTR(Shader)
reedfe630452016-03-25 09:08:00 -0700383#endif
reedd053ce92016-03-22 10:17:23 -0700384#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800385SET_PTR(ColorFilter)
reedd053ce92016-03-22 10:17:23 -0700386#endif
reedcfb6bdf2016-03-29 11:32:50 -0700387#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800388SET_PTR(Xfermode)
reedcfb6bdf2016-03-29 11:32:50 -0700389#endif
reeda4393342016-03-18 11:22:57 -0700390#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR
reedf28ad892016-03-18 10:17:27 -0700391SET_PTR(PathEffect)
reeda4393342016-03-18 11:22:57 -0700392#endif
reedefdfd512016-04-04 10:02:58 -0700393#ifdef SK_SUPPORT_LEGACY_MASKFILTER_PTR
reeda5ab9ec2016-03-06 18:10:48 -0800394SET_PTR(MaskFilter)
reedefdfd512016-04-04 10:02:58 -0700395#endif
reeda5ab9ec2016-03-06 18:10:48 -0800396#undef SET_PTR
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
reed7b380d02016-03-21 13:25:16 -0700398#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000399SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reeda5ab9ec2016-03-06 18:10:48 -0800400 fLooper.reset(SkSafeRef(looper));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 return looper;
402}
reed7b380d02016-03-21 13:25:16 -0700403#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404
reeda5ab9ec2016-03-06 18:10:48 -0800405SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reedcfb6bdf2016-03-29 11:32:50 -0700406 fXfermode = SkXfermode::Make(mode);
407 return fXfermode.get(); // can/should we change this API to be void, like the other setters?
reed@google.com15356a62011-11-03 19:29:08 +0000408}
409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410///////////////////////////////////////////////////////////////////////////////
411
reed@google.comed43dff2013-06-04 16:56:27 +0000412static SkScalar mag2(SkScalar x, SkScalar y) {
413 return x * x + y * y;
414}
415
416static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
417 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
418 ||
419 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
420}
421
422bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
423 SkASSERT(!ctm.hasPerspective());
424 SkASSERT(!textM.hasPerspective());
425
426 SkMatrix matrix;
427 matrix.setConcat(ctm, textM);
428 return tooBig(matrix, MaxCacheSize2());
429}
430
reed@google.comed43dff2013-06-04 16:56:27 +0000431
432///////////////////////////////////////////////////////////////////////////////
433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434#include "SkGlyphCache.h"
435#include "SkUtils.h"
436
reeda9322c22016-04-12 06:47:05 -0700437static void DetachDescProc(SkTypeface* typeface, const SkScalerContextEffects& effects,
438 const SkDescriptor* desc, void* context) {
439 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, effects, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000440}
441
reeda9322c22016-04-12 06:47:05 -0700442int SkPaint::textToGlyphs(const void* textData, size_t byteLength, uint16_t glyphs[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 if (byteLength == 0) {
444 return 0;
445 }
reed@google.com72cf4922011-01-04 19:58:20 +0000446
halcanary96fcdcc2015-08-27 07:41:13 -0700447 SkASSERT(textData != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448
halcanary96fcdcc2015-08-27 07:41:13 -0700449 if (nullptr == glyphs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 switch (this->getTextEncoding()) {
451 case kUTF8_TextEncoding:
452 return SkUTF8_CountUnichars((const char*)textData, byteLength);
453 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000454 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000455 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000456 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000458 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000460 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 }
462 return 0;
463 }
reed@google.com72cf4922011-01-04 19:58:20 +0000464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 // handle this encoding before the setup for the glyphcache
468 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
469 // we want to ignore the low bit of byteLength
470 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000471 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 }
reed@google.com72cf4922011-01-04 19:58:20 +0000473
halcanary96fcdcc2015-08-27 07:41:13 -0700474 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 SkGlyphCache* cache = autoCache.getCache();
476
477 const char* text = (const char*)textData;
478 const char* stop = text + byteLength;
479 uint16_t* gptr = glyphs;
480
481 switch (this->getTextEncoding()) {
482 case SkPaint::kUTF8_TextEncoding:
483 while (text < stop) {
484 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
485 }
486 break;
487 case SkPaint::kUTF16_TextEncoding: {
488 const uint16_t* text16 = (const uint16_t*)text;
489 const uint16_t* stop16 = (const uint16_t*)stop;
490 while (text16 < stop16) {
491 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
492 }
493 break;
494 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000495 case kUTF32_TextEncoding: {
496 const int32_t* text32 = (const int32_t*)text;
497 const int32_t* stop32 = (const int32_t*)stop;
498 while (text32 < stop32) {
499 *gptr++ = cache->unicharToGlyph(*text32++);
500 }
501 break;
502 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000504 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000506 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507}
508
reed@android.coma5dcaf62010-02-05 17:12:32 +0000509bool SkPaint::containsText(const void* textData, size_t byteLength) const {
510 if (0 == byteLength) {
511 return true;
512 }
reed@google.com72cf4922011-01-04 19:58:20 +0000513
halcanary96fcdcc2015-08-27 07:41:13 -0700514 SkASSERT(textData != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000515
reed@android.coma5dcaf62010-02-05 17:12:32 +0000516 // handle this encoding before the setup for the glyphcache
517 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
518 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
519 size_t count = byteLength >> 1;
520 for (size_t i = 0; i < count; i++) {
521 if (0 == glyphID[i]) {
522 return false;
523 }
524 }
525 return true;
526 }
reed@google.com72cf4922011-01-04 19:58:20 +0000527
halcanary96fcdcc2015-08-27 07:41:13 -0700528 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000529 SkGlyphCache* cache = autoCache.getCache();
530
531 switch (this->getTextEncoding()) {
532 case SkPaint::kUTF8_TextEncoding: {
533 const char* text = static_cast<const char*>(textData);
534 const char* stop = text + byteLength;
535 while (text < stop) {
536 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
537 return false;
538 }
539 }
540 break;
541 }
542 case SkPaint::kUTF16_TextEncoding: {
543 const uint16_t* text = static_cast<const uint16_t*>(textData);
544 const uint16_t* stop = text + (byteLength >> 1);
545 while (text < stop) {
546 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
547 return false;
548 }
549 }
550 break;
551 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000552 case SkPaint::kUTF32_TextEncoding: {
553 const int32_t* text = static_cast<const int32_t*>(textData);
554 const int32_t* stop = text + (byteLength >> 2);
555 while (text < stop) {
556 if (0 == cache->unicharToGlyph(*text++)) {
557 return false;
558 }
559 }
560 break;
561 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000562 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000563 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000564 return false;
565 }
566 return true;
567}
568
robertphillipsd24955a2015-06-26 12:17:59 -0700569void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, SkUnichar textData[]) const {
reed@android.com9d3a9852010-01-08 14:07:42 +0000570 if (count <= 0) {
571 return;
572 }
573
halcanary96fcdcc2015-08-27 07:41:13 -0700574 SkASSERT(glyphs != nullptr);
575 SkASSERT(textData != nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000576
robertphillipsd24955a2015-06-26 12:17:59 -0700577 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
halcanary96fcdcc2015-08-27 07:41:13 -0700578 SkAutoGlyphCache autoCache(*this, &props, nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000579 SkGlyphCache* cache = autoCache.getCache();
580
581 for (int index = 0; index < count; index++) {
582 textData[index] = cache->glyphToUnichar(glyphs[index]);
583 }
584}
585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586///////////////////////////////////////////////////////////////////////////////
587
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000588static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
589 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700590 SkASSERT(cache != nullptr);
591 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
594}
595
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000596static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
597 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700598 SkASSERT(cache != nullptr);
599 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
602}
603
reed@google.com68bc6f72012-03-14 19:41:55 +0000604static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
605 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700606 SkASSERT(cache != nullptr);
607 SkASSERT(text != nullptr);
reed@google.com68bc6f72012-03-14 19:41:55 +0000608
609 const int32_t* ptr = *(const int32_t**)text;
610 SkUnichar uni = *ptr++;
611 *text = (const char*)ptr;
612 return cache->getUnicharMetrics(uni);
613}
614
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000615static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
616 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700617 SkASSERT(cache != nullptr);
618 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 const uint16_t* ptr = *(const uint16_t**)text;
621 unsigned glyphID = *ptr;
622 ptr += 1;
623 *text = (const char*)ptr;
624 return cache->getGlyphIDMetrics(glyphID);
625}
626
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000627static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
628 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700629 SkASSERT(cache != nullptr);
630 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000631
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
633}
634
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000635static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
636 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700637 SkASSERT(cache != nullptr);
638 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
641}
642
reed@google.com68bc6f72012-03-14 19:41:55 +0000643static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
644 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700645 SkASSERT(cache != nullptr);
646 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000647
reed@google.com68bc6f72012-03-14 19:41:55 +0000648 const int32_t* ptr = *(const int32_t**)text;
649 SkUnichar uni = *ptr++;
650 *text = (const char*)ptr;
651 return cache->getUnicharAdvance(uni);
652}
653
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000654static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
655 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700656 SkASSERT(cache != nullptr);
657 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 const uint16_t* ptr = *(const uint16_t**)text;
660 unsigned glyphID = *ptr;
661 ptr += 1;
662 *text = (const char*)ptr;
663 return cache->getGlyphIDAdvance(glyphID);
664}
665
robertphillipse34f17d2016-07-19 07:59:22 -0700666SkPaint::GlyphCacheProc SkPaint::GetGlyphCacheProc(TextEncoding encoding,
667 bool isDevKern,
668 bool needFullMetrics) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800669 static const GlyphCacheProc gGlyphCacheProcs[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 sk_getMetrics_utf8_next,
671 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000672 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000674
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 sk_getAdvance_utf8_next,
676 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000677 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 sk_getAdvance_glyph_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 };
reed@google.com72cf4922011-01-04 19:58:20 +0000680
robertphillipse34f17d2016-07-19 07:59:22 -0700681 unsigned index = encoding;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682
robertphillipse34f17d2016-07-19 07:59:22 -0700683 if (!needFullMetrics && !isDevKern) {
reed9e96aa02014-10-03 12:44:37 -0700684 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000685 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686
benjaminwagnerd936f632016-02-23 10:44:31 -0800687 SkASSERT(index < SK_ARRAY_COUNT(gGlyphCacheProcs));
688 return gGlyphCacheProcs[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689}
690
691///////////////////////////////////////////////////////////////////////////////
692
reed@google.comed43dff2013-06-04 16:56:27 +0000693#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
694SkPaint::kDevKernText_Flag | \
695SkPaint::kLinearText_Flag | \
696SkPaint::kLCDRenderText_Flag | \
697SkPaint::kEmbeddedBitmapText_Flag | \
698SkPaint::kAutoHinting_Flag | \
699SkPaint::kGenA8FromLCD_Flag )
700
701SkScalar SkPaint::setupForAsPaths() {
702 uint32_t flags = this->getFlags();
703 // clear the flags we don't care about
704 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
705 // set the flags we do care about
706 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000707
reed@google.comed43dff2013-06-04 16:56:27 +0000708 this->setFlags(flags);
709 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000710
reed@google.comed43dff2013-06-04 16:56:27 +0000711 SkScalar textSize = fTextSize;
712 this->setTextSize(kCanonicalTextSizeForPaths);
713 return textSize / kCanonicalTextSizeForPaths;
714}
715
716class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717public:
reed@google.comed43dff2013-06-04 16:56:27 +0000718 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700719 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000720 SkPaint* p = fLazy.set(paint);
721 fScale = p->setupForAsPaths();
722 fPaint = p;
723 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000725
reed@google.comed43dff2013-06-04 16:56:27 +0000726 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000727
reed@google.comed43dff2013-06-04 16:56:27 +0000728 /**
729 * Returns 0 if the paint was unmodified, or the scale factor need to
730 * the original textSize
731 */
732 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734private:
reed@google.comed43dff2013-06-04 16:56:27 +0000735 const SkPaint* fPaint;
736 SkScalar fScale;
737 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738};
739
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000740static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 bounds->set(SkIntToScalar(g.fLeft),
742 SkIntToScalar(g.fTop),
743 SkIntToScalar(g.fLeft + g.fWidth),
744 SkIntToScalar(g.fTop + g.fHeight));
745}
746
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700747static void join_bounds_x(const SkGlyph& g, SkRect* bounds, SkScalar dx) {
748 bounds->join(SkIntToScalar(g.fLeft) + dx,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 SkIntToScalar(g.fTop),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700750 SkIntToScalar(g.fLeft + g.fWidth) + dx,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 SkIntToScalar(g.fTop + g.fHeight));
752}
753
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700754static void join_bounds_y(const SkGlyph& g, SkRect* bounds, SkScalar dy) {
reed@google.com44da42e2011-11-10 20:04:47 +0000755 bounds->join(SkIntToScalar(g.fLeft),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700756 SkIntToScalar(g.fTop) + dy,
reed@google.com44da42e2011-11-10 20:04:47 +0000757 SkIntToScalar(g.fLeft + g.fWidth),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700758 SkIntToScalar(g.fTop + g.fHeight) + dy);
reed@google.com44da42e2011-11-10 20:04:47 +0000759}
760
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700761typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, SkScalar);
reed@google.com44da42e2011-11-10 20:04:47 +0000762
763// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700764static SkScalar advance(const SkGlyph& glyph, int xyIndex) {
reed@google.com44da42e2011-11-10 20:04:47 +0000765 SkASSERT(0 == xyIndex || 1 == xyIndex);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700766 return SkFloatToScalar((&glyph.fAdvanceX)[xyIndex]);
reed@google.com44da42e2011-11-10 20:04:47 +0000767}
768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769SkScalar SkPaint::measure_text(SkGlyphCache* cache,
770 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000771 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000773 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000775 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000777 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 return 0;
779 }
780
robertphillipse34f17d2016-07-19 07:59:22 -0700781 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(this->getTextEncoding(),
782 this->isDevKernText(),
783 nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784
reed@google.com44da42e2011-11-10 20:04:47 +0000785 int xyIndex;
786 JoinBoundsProc joinBoundsProc;
787 if (this->isVerticalText()) {
788 xyIndex = 1;
789 joinBoundsProc = join_bounds_y;
790 } else {
791 xyIndex = 0;
792 joinBoundsProc = join_bounds_x;
793 }
794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 int n = 1;
796 const char* stop = (const char*)text + byteLength;
797 const SkGlyph* g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700798 SkScalar x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799
halcanary96fcdcc2015-08-27 07:41:13 -0700800 if (nullptr == bounds) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000801 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 for (; text < stop; n++) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700803 const int rsb = g->fRsbDelta;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700805 x += SkAutoKern_Adjust(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000807 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000809 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 }
811 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000812 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000814 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 for (; text < stop; n++) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700816 const int rsb = g->fRsbDelta;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700818 x += SkAutoKern_Adjust(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000819 joinBoundsProc(*g, bounds, x);
820 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000822 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 for (; text < stop; n++) {
824 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000825 joinBoundsProc(*g, bounds, x);
826 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 }
828 }
829 }
830 SkASSERT(text == stop);
831
832 *count = n;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700833 return x;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834}
835
reed99ae8812014-08-26 11:30:01 -0700836SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -0700838 SkASSERT(text != nullptr || length == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839
reed@google.comed43dff2013-06-04 16:56:27 +0000840 SkCanonicalizePaint canon(*this);
841 const SkPaint& paint = canon.getPaint();
842 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000843
halcanary96fcdcc2015-08-27 07:41:13 -0700844 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 SkGlyphCache* cache = autoCache.getCache();
846
847 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000848
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000849 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 int tempCount;
851
reed@google.comed43dff2013-06-04 16:56:27 +0000852 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000853 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000855 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
857 bounds->fTop = SkScalarMul(bounds->fTop, scale);
858 bounds->fRight = SkScalarMul(bounds->fRight, scale);
859 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
860 }
861 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000862 } else if (bounds) {
863 // ensure that even if we don't measure_text we still update the bounds
864 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 }
866 return width;
867}
868
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700870 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000871 if (0 == length || 0 >= maxWidth) {
872 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000874 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return 0;
876 }
877
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000878 if (0 == fTextSize) {
879 if (measuredWidth) {
880 *measuredWidth = 0;
881 }
882 return length;
883 }
884
halcanary96fcdcc2015-08-27 07:41:13 -0700885 SkASSERT(textD != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -0700887 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888
reed@google.comed43dff2013-06-04 16:56:27 +0000889 SkCanonicalizePaint canon(*this);
890 const SkPaint& paint = canon.getPaint();
891 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892
reed@google.comed43dff2013-06-04 16:56:27 +0000893 // adjust max in case we changed the textSize in paint
894 if (scale) {
895 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 }
reed@google.com72cf4922011-01-04 19:58:20 +0000897
halcanary96fcdcc2015-08-27 07:41:13 -0700898 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 SkGlyphCache* cache = autoCache.getCache();
900
robertphillipse34f17d2016-07-19 07:59:22 -0700901 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
902 paint.isDevKernText(),
903 false);
reed@google.comed43dff2013-06-04 16:56:27 +0000904 const int xyIndex = paint.isVerticalText() ? 1 : 0;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700905 SkScalar width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000907 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -0700909 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 const char* curr = text;
911 const SkGlyph& g = glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700912 SkScalar x = SkAutoKern_Adjust(rsb, g.fLsbDelta) + advance(g, xyIndex);
913 if ((width += x) > maxWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 width -= x;
915 text = curr;
916 break;
917 }
918 rsb = g.fRsbDelta;
919 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000920 } else {
reed9e96aa02014-10-03 12:44:37 -0700921 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 const char* curr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700923 SkScalar x = advance(glyphCacheProc(cache, &text), xyIndex);
924 if ((width += x) > maxWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 width -= x;
926 text = curr;
927 break;
928 }
929 }
930 }
reed@google.com72cf4922011-01-04 19:58:20 +0000931
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000932 if (measuredWidth) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000933 if (scale) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700934 width *= scale;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000935 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700936 *measuredWidth = width;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 }
reed@google.com72cf4922011-01-04 19:58:20 +0000938
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -0700940 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941}
942
943///////////////////////////////////////////////////////////////////////////////
944
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000945static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +0000946 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 return false; // don't detach the cache
948}
949
reeda9322c22016-04-12 06:47:05 -0700950static void FontMetricsDescProc(SkTypeface* typeface, const SkScalerContextEffects& effects,
951 const SkDescriptor* desc, void* context) {
952 SkGlyphCache::VisitCache(typeface, effects, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953}
954
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000955SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +0000956 SkCanonicalizePaint canon(*this);
957 const SkPaint& paint = canon.getPaint();
958 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000959
halcanary96fcdcc2015-08-27 07:41:13 -0700960 SkMatrix zoomMatrix, *zoomPtr = nullptr;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000961 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 zoomMatrix.setScale(zoom, zoom);
963 zoomPtr = &zoomMatrix;
964 }
965
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 FontMetrics storage;
halcanary96fcdcc2015-08-27 07:41:13 -0700967 if (nullptr == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000969 }
reed@google.com72cf4922011-01-04 19:58:20 +0000970
brianosmana1e8f8d2016-04-08 06:47:54 -0700971 paint.descriptorProc(nullptr, kNone_ScalerContextFlags, zoomPtr, FontMetricsDescProc, metrics);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000973 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 metrics->fTop = SkScalarMul(metrics->fTop, scale);
975 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
976 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
977 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
978 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +0000979 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
980 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
981 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
982 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000983 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
984 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 }
986 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
987}
988
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000989///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000991static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 bounds->set(g.fLeft * scale,
993 g.fTop * scale,
994 (g.fLeft + g.fWidth) * scale,
995 (g.fTop + g.fHeight) * scale);
996}
997
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000998int SkPaint::getTextWidths(const void* textData, size_t byteLength,
999 SkScalar widths[], SkRect bounds[]) const {
1000 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001002 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003
bsalomon49f085d2014-09-05 13:34:00 -07001004 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
halcanary96fcdcc2015-08-27 07:41:13 -07001006 if (nullptr == widths && nullptr == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001008 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009
reed@google.comed43dff2013-06-04 16:56:27 +00001010 SkCanonicalizePaint canon(*this);
1011 const SkPaint& paint = canon.getPaint();
1012 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013
halcanary96fcdcc2015-08-27 07:41:13 -07001014 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 SkGlyphCache* cache = autoCache.getCache();
robertphillipse34f17d2016-07-19 07:59:22 -07001016 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
1017 paint.isDevKernText(),
1018 nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019
1020 const char* text = (const char*)textData;
1021 const char* stop = text + byteLength;
1022 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001023 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001025 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 // we adjust the widths returned here through auto-kerning
1027 SkAutoKern autokern;
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001028 SkScalar prevWidth = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029
1030 if (scale) {
1031 while (text < stop) {
1032 const SkGlyph& g = glyphCacheProc(cache, &text);
1033 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001034 SkScalar adjust = autokern.adjust(g);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
1036 if (count > 0) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001037 *widths++ = SkScalarMul(prevWidth + adjust, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
reed@google.com44da42e2011-11-10 20:04:47 +00001039 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 }
1041 if (bounds) {
1042 set_bounds(g, bounds++, scale);
1043 }
1044 ++count;
1045 }
1046 if (count > 0 && widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001047 *widths = SkScalarMul(prevWidth, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 }
1049 } else {
1050 while (text < stop) {
1051 const SkGlyph& g = glyphCacheProc(cache, &text);
1052 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001053 SkScalar adjust = autokern.adjust(g);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054
1055 if (count > 0) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001056 *widths++ = prevWidth + adjust;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 }
reed@google.com44da42e2011-11-10 20:04:47 +00001058 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 }
1060 if (bounds) {
1061 set_bounds(g, bounds++);
1062 }
1063 ++count;
1064 }
1065 if (count > 0 && widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001066 *widths = prevWidth;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 }
1068 }
1069 } else { // no devkern
1070 if (scale) {
1071 while (text < stop) {
1072 const SkGlyph& g = glyphCacheProc(cache, &text);
1073 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001074 *widths++ = SkScalarMul(advance(g, xyIndex),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 scale);
1076 }
1077 if (bounds) {
1078 set_bounds(g, bounds++, scale);
1079 }
1080 ++count;
1081 }
1082 } else {
1083 while (text < stop) {
1084 const SkGlyph& g = glyphCacheProc(cache, &text);
1085 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001086 *widths++ = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 }
1088 if (bounds) {
1089 set_bounds(g, bounds++);
1090 }
1091 ++count;
1092 }
1093 }
1094 }
1095
1096 SkASSERT(text == stop);
1097 return count;
1098}
1099
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001100///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101
1102#include "SkDraw.h"
1103
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001104void SkPaint::getTextPath(const void* textData, size_t length,
1105 SkScalar x, SkScalar y, SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001106 SkASSERT(length == 0 || textData != nullptr);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001107
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001109 if (text == nullptr || length == 0 || path == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112
djsollen@google.com166e6532012-03-20 14:24:38 +00001113 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 SkMatrix matrix;
1115 SkScalar prevXPos = 0;
1116
1117 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1118 matrix.postTranslate(x, y);
1119 path->reset();
1120
1121 SkScalar xpos;
1122 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001123 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001125 if (iterPath) {
1126 path->addPath(*iterPath, matrix);
1127 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 prevXPos = xpos;
1129 }
1130}
1131
reed@google.comca0062e2012-07-20 11:20:32 +00001132void SkPaint::getPosTextPath(const void* textData, size_t length,
1133 const SkPoint pos[], SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001134 SkASSERT(length == 0 || textData != nullptr);
reed@google.comca0062e2012-07-20 11:20:32 +00001135
1136 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001137 if (text == nullptr || length == 0 || path == nullptr) {
reed@google.comca0062e2012-07-20 11:20:32 +00001138 return;
1139 }
1140
1141 SkTextToPathIter iter(text, length, *this, false);
1142 SkMatrix matrix;
1143 SkPoint prevPos;
1144 prevPos.set(0, 0);
1145
1146 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1147 path->reset();
1148
1149 unsigned int i = 0;
1150 const SkPath* iterPath;
halcanary96fcdcc2015-08-27 07:41:13 -07001151 while (iter.next(&iterPath, nullptr)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001152 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001153 if (iterPath) {
1154 path->addPath(*iterPath, matrix);
1155 }
reed@google.comca0062e2012-07-20 11:20:32 +00001156 prevPos = pos[i];
1157 i++;
1158 }
1159}
1160
fmalitaeae6a912016-07-28 09:47:24 -07001161template <SkTextInterceptsIter::TextType TextType, typename Func>
1162int GetTextIntercepts(const SkPaint& paint, const void* text, size_t length,
1163 const SkScalar bounds[2], SkScalar* array, Func posMaker) {
1164 SkASSERT(length == 0 || text != nullptr);
caryclark0449bcf2016-02-09 13:25:45 -08001165 if (!length) {
1166 return 0;
1167 }
1168
fmalitaeae6a912016-07-28 09:47:24 -07001169 const SkPoint pos0 = posMaker(0);
1170 SkTextInterceptsIter iter(static_cast<const char*>(text), length, paint, bounds,
1171 pos0.x(), pos0.y(), TextType);
1172
caryclark0449bcf2016-02-09 13:25:45 -08001173 int i = 0;
1174 int count = 0;
1175 while (iter.next(array, &count)) {
fmalitaeae6a912016-07-28 09:47:24 -07001176 if (TextType == SkTextInterceptsIter::TextType::kPosText) {
1177 const SkPoint pos = posMaker(++i);
1178 iter.setPosition(pos.x(), pos.y());
1179 }
caryclark0449bcf2016-02-09 13:25:45 -08001180 }
fmalitaeae6a912016-07-28 09:47:24 -07001181
1182 return count;
1183}
1184
1185int SkPaint::getTextIntercepts(const void* textData, size_t length,
1186 SkScalar x, SkScalar y, const SkScalar bounds[2],
1187 SkScalar* array) const {
1188
1189 return GetTextIntercepts<SkTextInterceptsIter::TextType::kText>(
1190 *this, textData, length, bounds, array, [&x, &y] (int) -> SkPoint {
1191 return SkPoint::Make(x, y);
1192 });
1193}
1194
1195int SkPaint::getPosTextIntercepts(const void* textData, size_t length, const SkPoint pos[],
1196 const SkScalar bounds[2], SkScalar* array) const {
1197
1198 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
1199 *this, textData, length, bounds, array, [&pos] (int i) -> SkPoint {
1200 return pos[i];
1201 });
1202}
1203
1204int SkPaint::getPosTextHIntercepts(const void* textData, size_t length, const SkScalar xpos[],
1205 SkScalar constY, const SkScalar bounds[2],
1206 SkScalar* array) const {
1207
1208 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
1209 *this, textData, length, bounds, array, [&xpos, &constY] (int i) -> SkPoint {
1210 return SkPoint::Make(xpos[i], constY);
1211 });
1212}
1213
1214int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2],
1215 SkScalar* intervals) const {
1216 int count = 0;
1217 SkPaint runPaint(*this);
1218
1219 SkTextBlobRunIterator it(blob);
1220 while (!it.done()) {
1221 it.applyFontToPaint(&runPaint);
1222 const size_t runByteCount = it.glyphCount() * sizeof(SkGlyphID);
1223 SkScalar* runIntervals = intervals ? intervals + count : nullptr;
1224
1225 switch (it.positioning()) {
1226 case SkTextBlob::kDefault_Positioning:
1227 count += runPaint.getTextIntercepts(it.glyphs(), runByteCount, it.offset().x(),
1228 it.offset().y(), bounds, runIntervals);
1229 break;
1230 case SkTextBlob::kHorizontal_Positioning:
1231 count += runPaint.getPosTextHIntercepts(it.glyphs(), runByteCount, it.pos(),
1232 it.offset().y(), bounds, runIntervals);
1233 break;
1234 case SkTextBlob::kFull_Positioning:
1235 count += runPaint.getPosTextIntercepts(it.glyphs(), runByteCount,
1236 reinterpret_cast<const SkPoint*>(it.pos()),
1237 bounds, runIntervals);
1238 break;
1239 }
1240
1241 it.next();
1242 }
1243
caryclark0449bcf2016-02-09 13:25:45 -08001244 return count;
1245}
1246
reed8893e5f2014-12-15 13:27:26 -08001247SkRect SkPaint::getFontBounds() const {
1248 SkMatrix m;
1249 m.setScale(fTextSize * fTextScaleX, fTextSize);
1250 m.postSkew(fTextSkewX, 0);
1251
1252 SkTypeface* typeface = this->getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001253 if (nullptr == typeface) {
reed8893e5f2014-12-15 13:27:26 -08001254 typeface = SkTypeface::GetDefaultTypeface();
1255 }
1256
1257 SkRect bounds;
1258 m.mapRect(&bounds, typeface->getBounds());
1259 return bounds;
1260}
1261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262static void add_flattenable(SkDescriptor* desc, uint32_t tag,
brianosmanfad98562016-05-04 11:06:28 -07001263 SkBinaryWriteBuffer* buffer) {
halcanary96fcdcc2015-08-27 07:41:13 -07001264 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), nullptr));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265}
1266
joshualitt20dac882015-07-24 13:16:24 -07001267static SkMask::Format compute_mask_format(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001270 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001271 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001272 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001273 }
1274
reed@google.com65dd8f82011-03-14 13:31:16 +00001275 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001276 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001277 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001278
1279 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280}
1281
reed@android.com1cdcb512009-08-24 19:11:00 +00001282// if linear-text is on, then we force hinting to be off (since that's sort of
1283// the point of linear-text.
1284static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1285 SkPaint::Hinting h = paint.getHinting();
1286 if (paint.isLinearText()) {
1287 h = SkPaint::kNo_Hinting;
1288 }
1289 return h;
1290}
1291
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001292// return true if the paint is just a single color (i.e. not a shader). If its
1293// a shader, then we can't compute a const luminance for it :(
1294static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001295 SkColor c = paint.getColor();
1296
1297 SkShader* shader = paint.getShader();
1298 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001299 return false;
1300 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001301 if (paint.getColorFilter()) {
1302 c = paint.getColorFilter()->filterColor(c);
1303 }
1304 if (color) {
1305 *color = c;
1306 }
1307 return true;
1308}
1309
joshualitt9e36c1a2015-04-14 12:17:27 -07001310SkColor SkPaint::computeLuminanceColor() const {
reed@google.comce6dbb62012-02-10 22:01:45 +00001311 SkColor c;
joshualitt9e36c1a2015-04-14 12:17:27 -07001312 if (!justAColor(*this, &c)) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001313 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1314 }
1315 return c;
1316}
reed@google.com813d38b2012-02-13 21:37:57 +00001317
reed@google.comdd43df92012-02-15 14:50:29 +00001318#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1319
reed@google.com4f79b9b2011-09-13 13:23:26 +00001320// Beyond this size, LCD doesn't appreciably improve quality, but it always
1321// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001322#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1323 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1324#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001325
reedb3da83a2014-10-01 12:06:12 -07001326const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1327
reed4942e752014-10-01 13:59:33 -07001328static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001329 if (checkPost2x2) {
1330 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1331 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001332 area *= rec.fTextSize * rec.fTextSize;
1333 return area > gMaxSize2ForLCDText;
1334 } else {
1335 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001336 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001337}
1338
reed@google.com72cf4922011-01-04 19:58:20 +00001339/*
1340 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1341 * that vary only slightly when we create our key into the font cache, since the font scaler
1342 * typically returns the same looking resuts for tiny changes in the matrix.
1343 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001344static SkScalar sk_relax(SkScalar x) {
bungeman27876bc2016-02-29 11:22:55 -08001345 SkScalar n = SkScalarRoundToScalar(x * 1024);
reed@android.comf2b98d62010-12-20 18:26:13 +00001346 return n / 1024.0f;
1347}
1348
reed@android.com36a4c2a2009-07-22 19:52:11 +00001349void SkScalerContext::MakeRec(const SkPaint& paint,
robertphillipsfcf78292015-06-19 11:49:52 -07001350 const SkSurfaceProps* surfaceProps,
bungeman@google.com532470f2013-01-22 19:25:14 +00001351 const SkMatrix* deviceMatrix,
1352 Rec* rec) {
halcanary96fcdcc2015-08-27 07:41:13 -07001353 SkASSERT(deviceMatrix == nullptr || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001355 SkTypeface* typeface = paint.getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001356 if (nullptr == typeface) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001357 typeface = SkTypeface::GetDefaultTypeface();
1358 }
bungemanec730b92014-08-18 07:57:35 -07001359 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 rec->fTextSize = paint.getTextSize();
1361 rec->fPreScaleX = paint.getTextScaleX();
1362 rec->fPreSkewX = paint.getTextSkewX();
1363
reedb3da83a2014-10-01 12:06:12 -07001364 bool checkPost2x2 = false;
1365
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001366 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001367 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1368 if (mask & SkMatrix::kScale_Mask) {
1369 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1370 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1371 checkPost2x2 = true;
1372 } else {
1373 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1374 }
1375 if (mask & SkMatrix::kAffine_Mask) {
1376 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1377 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1378 checkPost2x2 = true;
1379 } else {
1380 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1381 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001382 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1384 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1385 }
reed@google.com72cf4922011-01-04 19:58:20 +00001386
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 SkPaint::Style style = paint.getStyle();
1388 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001389
reed@google.comffe49f52011-11-22 19:42:41 +00001390 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001391
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001392 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001393#ifdef SK_USE_FREETYPE_EMBOLDEN
1394 flags |= SkScalerContext::kEmbolden_Flag;
1395#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001396 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1397 kStdFakeBoldInterpKeys,
1398 kStdFakeBoldInterpValues,
1399 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001401
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001402 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 style = SkPaint::kStrokeAndFill_Style;
1404 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001405 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001407 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001408#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 }
1410
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001411 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001413 }
reed@google.com72cf4922011-01-04 19:58:20 +00001414
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001415 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 rec->fFrameWidth = strokeWidth;
1417 rec->fMiterLimit = paint.getStrokeMiter();
1418 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
caryclarkd7ea92f2016-03-16 07:34:02 -07001419 rec->fStrokeCap = SkToU8(paint.getStrokeCap());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001421 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001423 }
1424 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 rec->fFrameWidth = 0;
1426 rec->fMiterLimit = 0;
1427 rec->fStrokeJoin = 0;
caryclarkd7ea92f2016-03-16 07:34:02 -07001428 rec->fStrokeCap = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 }
1430
joshualitt20dac882015-07-24 13:16:24 -07001431 rec->fMaskFormat = SkToU8(compute_mask_format(paint));
reed@google.com02b53312011-05-18 19:00:53 +00001432
reedd54d3fc2014-11-13 14:39:58 -08001433 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001434 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001435 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001436 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001437 } else {
robertphillipsfcf78292015-06-19 11:49:52 -07001438 SkPixelGeometry geometry = surfaceProps
1439 ? surfaceProps->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001440 : SkSurfacePropsDefaultPixelGeometry();
1441 switch (geometry) {
1442 case kUnknown_SkPixelGeometry:
1443 // eeek, can't support LCD
1444 rec->fMaskFormat = SkMask::kA8_Format;
1445 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1446 break;
1447 case kRGB_H_SkPixelGeometry:
1448 // our default, do nothing.
1449 break;
1450 case kBGR_H_SkPixelGeometry:
1451 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1452 break;
1453 case kRGB_V_SkPixelGeometry:
1454 flags |= SkScalerContext::kLCD_Vertical_Flag;
1455 break;
1456 case kBGR_V_SkPixelGeometry:
1457 flags |= SkScalerContext::kLCD_Vertical_Flag;
1458 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1459 break;
reed@google.com02b53312011-05-18 19:00:53 +00001460 }
1461 }
1462 }
1463
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001464 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001465 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001466 }
1467 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001468 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001469 }
1470 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001471 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001472 }
reed@google.com830a23e2011-11-10 15:20:49 +00001473 if (paint.isVerticalText()) {
1474 flags |= SkScalerContext::kVertical_Flag;
1475 }
reed@google.com8351aab2012-01-18 17:06:35 +00001476 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1477 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1478 }
reed@google.com02b53312011-05-18 19:00:53 +00001479 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001480
reed@google.comffe49f52011-11-22 19:42:41 +00001481 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001482 rec->setHinting(computeHinting(paint));
1483
joshualitt9e36c1a2015-04-14 12:17:27 -07001484 rec->setLuminanceColor(paint.computeLuminanceColor());
bungeman@google.com532470f2013-01-22 19:25:14 +00001485
robertphillips9fc82752015-06-19 04:46:45 -07001486 //For now always set the paint gamma equal to the device gamma.
1487 //The math in SkMaskGamma can handle them being different,
1488 //but it requires superluminous masks when
1489 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1490 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1491 rec->setPaintGamma(SK_GAMMA_EXPONENT);
bungeman@google.com532470f2013-01-22 19:25:14 +00001492
1493#ifdef SK_GAMMA_CONTRAST
1494 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001495#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001496 /**
1497 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1498 * With lower values small text appears washed out (though correctly so).
1499 * With higher values lcd fringing is worse and the smoothing effect of
1500 * partial coverage is diminished.
1501 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001502 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001503#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001504
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001505 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001506
reed@android.com36a4c2a2009-07-22 19:52:11 +00001507 /* Allow the fonthost to modify our rec before we use it as a key into the
1508 cache. This way if we're asking for something that they will ignore,
1509 they can modify our rec up front, so we don't create duplicate cache
1510 entries.
1511 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001512 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001513
reed@google.com10d2d4d2012-03-01 22:32:51 +00001514 // be sure to call PostMakeRec(rec) before you actually use it!
1515}
1516
1517/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001518 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1519 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1520 * to hold it until the returned pointer is refed or forgotten.
1521 */
reed086eea92016-05-04 17:12:46 -07001522SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
bungeman@google.com97efada2012-07-30 20:40:50 +00001523
halcanary96fcdcc2015-08-27 07:41:13 -07001524static SkMaskGamma* gLinearMaskGamma = nullptr;
1525static SkMaskGamma* gMaskGamma = nullptr;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001526static SkScalar gContrast = SK_ScalarMin;
1527static SkScalar gPaintGamma = SK_ScalarMin;
1528static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001529/**
1530 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1531 * the returned SkMaskGamma pointer is refed or forgotten.
1532 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001533static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001534 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001535 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
halcanary96fcdcc2015-08-27 07:41:13 -07001536 if (nullptr == gLinearMaskGamma) {
halcanary385fe4d2015-08-26 13:07:48 -07001537 gLinearMaskGamma = new SkMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001538 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001539 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001540 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001541 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1542 SkSafeUnref(gMaskGamma);
halcanary385fe4d2015-08-26 13:07:48 -07001543 gMaskGamma = new SkMaskGamma(contrast, paintGamma, deviceGamma);
bungeman@google.com97efada2012-07-30 20:40:50 +00001544 gContrast = contrast;
1545 gPaintGamma = paintGamma;
1546 gDeviceGamma = deviceGamma;
1547 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001548 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001549}
1550
1551/**
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:
brianosmana1e8f8d2016-04-08 06:47:54 -07001580 // No need to differentiate gamma or apply contrast 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,
brianosmanfad98562016-05-04 11:06:28 -07001593 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1594 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1595 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer,
joshualittfd450792015-03-13 08:38:43 -07001596 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,
robertphillipsfcf78292015-06-19 11:49:52 -07001614 const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001615 bool fakeGamma, bool boostContrast,
bungemanf6d1e602016-02-22 13:20:28 -08001616 const SkMatrix* deviceMatrix,
brianosmanfad98562016-05-04 11:06:28 -07001617 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1618 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1619 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer) {
robertphillipsfcf78292015-06-19 11:49:52 -07001620 SkScalerContext::MakeRec(paint, surfaceProps, deviceMatrix, rec);
bungemanf6d1e602016-02-22 13:20:28 -08001621 if (!fakeGamma) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001622 rec->ignoreGamma();
1623 }
1624 if (!boostContrast) {
1625 rec->setContrast(0);
joshualittfd450792015-03-13 08:38:43 -07001626 }
1627
1628 int entryCount = 1;
1629 size_t descSize = sizeof(*rec);
1630
1631 if (pe) {
reed80f5ea02016-04-27 13:49:32 -07001632 pe->flatten(*peBuffer);
joshualittfd450792015-03-13 08:38:43 -07001633 descSize += peBuffer->bytesWritten();
1634 entryCount += 1;
1635 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1636 // seems like we could support kLCD as well at this point...
1637 }
1638 if (mf) {
reed80f5ea02016-04-27 13:49:32 -07001639 mf->flatten(*mfBuffer);
joshualittfd450792015-03-13 08:38:43 -07001640 descSize += mfBuffer->bytesWritten();
1641 entryCount += 1;
1642 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1643 /* Pre-blend is not currently applied to filtered text.
1644 The primary filter is blur, for which contrast makes no sense,
1645 and for which the destination guess error is more visible.
1646 Also, all existing users of blur have calibrated for linear. */
1647 rec->ignorePreBlend();
1648 }
1649 if (ra) {
reed80f5ea02016-04-27 13:49:32 -07001650 ra->flatten(*raBuffer);
joshualittfd450792015-03-13 08:38:43 -07001651 descSize += raBuffer->bytesWritten();
1652 entryCount += 1;
1653 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1654 }
1655
1656 ///////////////////////////////////////////////////////////////////////////
1657 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
1658 SkScalerContext::PostMakeRec(paint, rec);
1659
1660 descSize += SkDescriptor::ComputeOverhead(entryCount);
1661 return descSize;
1662}
1663
1664#ifdef TEST_DESC
1665static void test_desc(const SkScalerContext::Rec& rec,
brianosmanfad98562016-05-04 11:06:28 -07001666 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1667 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1668 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer,
joshualittfd450792015-03-13 08:38:43 -07001669 const SkDescriptor* desc, size_t descSize) {
1670 // Check that we completely write the bytes in desc (our key), and that
1671 // there are no uninitialized bytes. If there were, then we would get
1672 // false-misses (or worse, false-hits) in our fontcache.
1673 //
1674 // We do this buy filling 2 others, one with 0s and the other with 1s
1675 // and create those, and then check that all 3 are identical.
1676 SkAutoDescriptor ad1(descSize);
1677 SkAutoDescriptor ad2(descSize);
1678 SkDescriptor* desc1 = ad1.getDesc();
1679 SkDescriptor* desc2 = ad2.getDesc();
1680
1681 memset(desc1, 0x00, descSize);
1682 memset(desc2, 0xFF, descSize);
1683
1684 desc1->init();
1685 desc2->init();
1686 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1687 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1688
1689 if (pe) {
1690 add_flattenable(desc1, kPathEffect_SkDescriptorTag, peBuffer);
1691 add_flattenable(desc2, kPathEffect_SkDescriptorTag, peBuffer);
1692 }
1693 if (mf) {
1694 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, mfBuffer);
1695 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, mfBuffer);
1696 }
1697 if (ra) {
1698 add_flattenable(desc1, kRasterizer_SkDescriptorTag, raBuffer);
1699 add_flattenable(desc2, kRasterizer_SkDescriptorTag, raBuffer);
1700 }
1701
1702 SkASSERT(descSize == desc1->getLength());
1703 SkASSERT(descSize == desc2->getLength());
1704 desc1->computeChecksum();
1705 desc2->computeChecksum();
1706 SkASSERT(!memcmp(desc, desc1, descSize));
1707 SkASSERT(!memcmp(desc, desc2, descSize));
1708}
1709#endif
1710
1711/* see the note on ignoreGamma on descriptorProc */
reeda9322c22016-04-12 06:47:05 -07001712void SkPaint::getScalerContextDescriptor(SkScalerContextEffects* effects,
1713 SkAutoDescriptor* ad,
robertphillipsfcf78292015-06-19 11:49:52 -07001714 const SkSurfaceProps& surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001715 uint32_t scalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -08001716 const SkMatrix* deviceMatrix) const {
joshualittfd450792015-03-13 08:38:43 -07001717 SkScalerContext::Rec rec;
1718
1719 SkPathEffect* pe = this->getPathEffect();
1720 SkMaskFilter* mf = this->getMaskFilter();
1721 SkRasterizer* ra = this->getRasterizer();
1722
brianosmanfad98562016-05-04 11:06:28 -07001723 SkBinaryWriteBuffer peBuffer, mfBuffer, raBuffer;
brianosmana1e8f8d2016-04-08 06:47:54 -07001724 size_t descSize = fill_out_rec(*this, &rec, &surfaceProps,
1725 SkToBool(scalerContextFlags & kFakeGamma_ScalerContextFlag),
1726 SkToBool(scalerContextFlags & kBoostContrast_ScalerContextFlag),
1727 deviceMatrix, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
joshualittfd450792015-03-13 08:38:43 -07001728
joshualitt2b6acb42015-04-01 11:30:27 -07001729 ad->reset(descSize);
1730 SkDescriptor* desc = ad->getDesc();
joshualittfd450792015-03-13 08:38:43 -07001731
1732 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
1733
1734 SkASSERT(descSize == desc->getLength());
1735
1736#ifdef TEST_DESC
1737 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
1738#endif
reeda9322c22016-04-12 06:47:05 -07001739
1740 effects->fPathEffect = pe;
1741 effects->fMaskFilter = mf;
1742 effects->fRasterizer = ra;
joshualittfd450792015-03-13 08:38:43 -07001743}
1744
reed@google.comffe49f52011-11-22 19:42:41 +00001745/*
1746 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001747 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1748 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001749 */
robertphillipsfcf78292015-06-19 11:49:52 -07001750void SkPaint::descriptorProc(const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001751 uint32_t scalerContextFlags,
bungeman@google.com532470f2013-01-22 19:25:14 +00001752 const SkMatrix* deviceMatrix,
reeda9322c22016-04-12 06:47:05 -07001753 void (*proc)(SkTypeface*, const SkScalerContextEffects&,
1754 const SkDescriptor*, void*),
bungemanf6d1e602016-02-22 13:20:28 -08001755 void* context) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756 SkScalerContext::Rec rec;
1757
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 SkPathEffect* pe = this->getPathEffect();
1759 SkMaskFilter* mf = this->getMaskFilter();
1760 SkRasterizer* ra = this->getRasterizer();
1761
brianosmanfad98562016-05-04 11:06:28 -07001762 SkBinaryWriteBuffer peBuffer, mfBuffer, raBuffer;
brianosmana1e8f8d2016-04-08 06:47:54 -07001763 size_t descSize = fill_out_rec(*this, &rec, surfaceProps,
1764 SkToBool(scalerContextFlags & kFakeGamma_ScalerContextFlag),
1765 SkToBool(scalerContextFlags & kBoostContrast_ScalerContextFlag),
1766 deviceMatrix, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767
1768 SkAutoDescriptor ad(descSize);
1769 SkDescriptor* desc = ad.getDesc();
1770
joshualittfd450792015-03-13 08:38:43 -07001771 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772
1773 SkASSERT(descSize == desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774
reed@google.com17fb3872011-05-04 14:31:07 +00001775#ifdef TEST_DESC
joshualittfd450792015-03-13 08:38:43 -07001776 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
reed@google.com17fb3872011-05-04 14:31:07 +00001777#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001778
reeda9322c22016-04-12 06:47:05 -07001779 proc(fTypeface.get(), { pe, mf, ra }, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780}
1781
robertphillipsfcf78292015-06-19 11:49:52 -07001782SkGlyphCache* SkPaint::detachCache(const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001783 uint32_t scalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -08001784 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 SkGlyphCache* cache;
brianosmana1e8f8d2016-04-08 06:47:54 -07001786 this->descriptorProc(surfaceProps, scalerContextFlags, deviceMatrix, DetachDescProc, &cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 return cache;
1788}
1789
bungeman@google.com97efada2012-07-30 20:40:50 +00001790/**
1791 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1792 */
1793//static
1794SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1795 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001796 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1797 rec.getPaintGamma(),
1798 rec.getDeviceGamma());
1799 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001800}
1801
jvanverth2d2a68c2014-06-10 06:42:56 -07001802size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1803 SkScalar deviceGamma, int* width, int* height) {
1804 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1805 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1806 paintGamma,
1807 deviceGamma);
1808
1809 maskGamma.getGammaTableDimensions(width, height);
1810 size_t size = (*width)*(*height)*sizeof(uint8_t);
1811
1812 return size;
1813}
1814
1815void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1816 void* data) {
1817 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1818 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1819 paintGamma,
1820 deviceGamma);
1821 int width, height;
1822 maskGamma.getGammaTableDimensions(&width, &height);
1823 size_t size = width*height*sizeof(uint8_t);
1824 const uint8_t* gammaTables = maskGamma.getGammaTables();
1825 memcpy(data, gammaTables, size);
1826}
1827
1828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829///////////////////////////////////////////////////////////////////////////////
1830
1831#include "SkStream.h"
1832
reed@android.comaefd2bc2009-03-30 21:02:14 +00001833static uintptr_t asint(const void* p) {
1834 return reinterpret_cast<uintptr_t>(p);
1835}
1836
reed@android.comaefd2bc2009-03-30 21:02:14 +00001837static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1838 SkASSERT(a == (uint8_t)a);
1839 SkASSERT(b == (uint8_t)b);
1840 SkASSERT(c == (uint8_t)c);
1841 SkASSERT(d == (uint8_t)d);
1842 return (a << 24) | (b << 16) | (c << 8) | d;
1843}
1844
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001845#ifdef SK_DEBUG
1846 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1847 SkASSERT(bitCount > 0 && bitCount <= 32);
1848 uint32_t mask = ~0U;
1849 mask >>= (32 - bitCount);
1850 SkASSERT(0 == (value & ~mask));
1851 }
1852#else
1853 #define ASSERT_FITS_IN(value, bitcount)
1854#endif
1855
reed@android.comaefd2bc2009-03-30 21:02:14 +00001856enum FlatFlags {
mtklein88fd0fb2014-12-01 06:56:38 -08001857 kHasTypeface_FlatFlag = 0x1,
1858 kHasEffects_FlatFlag = 0x2,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001859
mtklein88fd0fb2014-12-01 06:56:38 -08001860 kFlatFlagMask = 0x3,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001861};
1862
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001863enum BitsPerField {
1864 kFlags_BPF = 16,
1865 kHint_BPF = 2,
1866 kAlign_BPF = 2,
1867 kFilter_BPF = 2,
1868 kFlatFlags_BPF = 3,
1869};
1870
1871static inline int BPF_Mask(int bits) {
1872 return (1 << bits) - 1;
1873}
1874
1875static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1876 unsigned filter, unsigned flatFlags) {
1877 ASSERT_FITS_IN(flags, kFlags_BPF);
1878 ASSERT_FITS_IN(hint, kHint_BPF);
1879 ASSERT_FITS_IN(align, kAlign_BPF);
1880 ASSERT_FITS_IN(filter, kFilter_BPF);
1881 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1882
1883 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1884 // add more bits in the future.
1885 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1886}
1887
1888static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1889 paint->setFlags(packed >> 16);
1890 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1891 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
reed93a12152015-03-16 10:08:34 -07001892 paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001893 return (FlatFlags)(packed & kFlatFlagMask);
1894}
1895
reed@android.comaefd2bc2009-03-30 21:02:14 +00001896/* To save space/time, we analyze the paint, and write a truncated version of
1897 it if there are not tricky elements like shaders, etc.
1898 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001899void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001900 uint8_t flatFlags = 0;
1901 if (this->getTypeface()) {
1902 flatFlags |= kHasTypeface_FlatFlag;
1903 }
1904 if (asint(this->getPathEffect()) |
1905 asint(this->getShader()) |
1906 asint(this->getXfermode()) |
1907 asint(this->getMaskFilter()) |
1908 asint(this->getColorFilter()) |
1909 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001910 asint(this->getLooper()) |
1911 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001912 flatFlags |= kHasEffects_FlatFlag;
1913 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001914
mtklein1b5dd882016-04-29 08:46:41 -07001915 buffer.writeScalar(this->getTextSize());
1916 buffer.writeScalar(this->getTextScaleX());
1917 buffer.writeScalar(this->getTextSkewX());
1918 buffer.writeScalar(this->getStrokeWidth());
1919 buffer.writeScalar(this->getStrokeMiter());
1920 buffer.writeColor(this->getColor());
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001921
mtklein1b5dd882016-04-29 08:46:41 -07001922 buffer.writeUInt(pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
1923 this->getFilterQuality(), flatFlags));
1924 buffer.writeUInt(pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1925 this->getStyle(), this->getTextEncoding()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001926
1927 // now we're done with ptr and the (pre)reserved space. If we need to write
1928 // additional fields, use the buffer directly
1929 if (flatFlags & kHasTypeface_FlatFlag) {
1930 buffer.writeTypeface(this->getTypeface());
1931 }
1932 if (flatFlags & kHasEffects_FlatFlag) {
1933 buffer.writeFlattenable(this->getPathEffect());
1934 buffer.writeFlattenable(this->getShader());
1935 buffer.writeFlattenable(this->getXfermode());
1936 buffer.writeFlattenable(this->getMaskFilter());
1937 buffer.writeFlattenable(this->getColorFilter());
1938 buffer.writeFlattenable(this->getRasterizer());
1939 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001940 buffer.writeFlattenable(this->getImageFilter());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001941 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942}
1943
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001944void SkPaint::unflatten(SkReadBuffer& buffer) {
mtklein1b5dd882016-04-29 08:46:41 -07001945 this->setTextSize(buffer.readScalar());
1946 this->setTextScaleX(buffer.readScalar());
1947 this->setTextSkewX(buffer.readScalar());
1948 this->setStrokeWidth(buffer.readScalar());
1949 this->setStrokeMiter(buffer.readScalar());
1950 this->setColor(buffer.readColor());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001951
mtklein1b5dd882016-04-29 08:46:41 -07001952 unsigned flatFlags = unpack_paint_flags(this, buffer.readUInt());
bungeman@google.com24babf42011-11-07 16:33:40 +00001953
mtklein1b5dd882016-04-29 08:46:41 -07001954 uint32_t tmp = buffer.readUInt();
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001955 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1956 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1957 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1958 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001959
1960 if (flatFlags & kHasTypeface_FlatFlag) {
bungeman13b9c952016-05-12 10:09:30 -07001961 this->setTypeface(sk_ref_sp(buffer.readTypeface()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001962 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001963 this->setTypeface(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001964 }
1965
1966 if (flatFlags & kHasEffects_FlatFlag) {
reeda4393342016-03-18 11:22:57 -07001967 this->setPathEffect(buffer.readPathEffect());
reed8a21c9f2016-03-08 18:50:00 -08001968 this->setShader(buffer.readShader());
reedcfb6bdf2016-03-29 11:32:50 -07001969 this->setXfermode(buffer.readXfermode());
reed60c9b582016-04-03 09:11:13 -07001970 this->setMaskFilter(buffer.readMaskFilter());
reedd053ce92016-03-22 10:17:23 -07001971 this->setColorFilter(buffer.readColorFilter());
reed7b380d02016-03-21 13:25:16 -07001972 this->setRasterizer(buffer.readRasterizer());
1973 this->setLooper(buffer.readDrawLooper());
reed60c9b582016-04-03 09:11:13 -07001974 this->setImageFilter(buffer.readImageFilter());
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00001975
reedf70b5312016-03-04 16:36:20 -08001976 if (buffer.isVersionLT(SkReadBuffer::kAnnotationsMovedToCanvas_Version)) {
1977 // We used to store annotations here (string+skdata) if this bool was true
1978 if (buffer.readBool()) {
1979 // Annotations have moved to drawAnnotation, so we just drop this one on the floor.
1980 SkString key;
1981 buffer.readString(&key);
reedfde05112016-03-11 13:02:28 -08001982 (void)buffer.readByteArrayAsData();
reedf70b5312016-03-04 16:36:20 -08001983 }
reed@google.com0cd2ac62013-10-14 20:02:44 +00001984 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001985 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001986 this->setPathEffect(nullptr);
1987 this->setShader(nullptr);
1988 this->setXfermode(nullptr);
1989 this->setMaskFilter(nullptr);
1990 this->setColorFilter(nullptr);
1991 this->setRasterizer(nullptr);
1992 this->setLooper(nullptr);
1993 this->setImageFilter(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001994 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995}
1996
1997///////////////////////////////////////////////////////////////////////////////
1998
reed05d90442015-02-12 13:35:52 -08001999bool SkPaint::getFillPath(const SkPath& src, SkPath* dst, const SkRect* cullRect,
2000 SkScalar resScale) const {
2001 SkStrokeRec rec(*this, resScale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002
reed@google.comfd4be262012-05-25 01:04:12 +00002003 const SkPath* srcPtr = &src;
2004 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002005
reed@google.com4bbdeac2013-01-24 21:03:11 +00002006 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002007 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002008 }
2009
reed@google.comfd4be262012-05-25 01:04:12 +00002010 if (!rec.applyToPath(dst, *srcPtr)) {
2011 if (srcPtr == &tmpPath) {
2012 // If path's were copy-on-write, this trick would not be needed.
2013 // As it is, we want to save making a deep-copy from tmpPath -> dst
2014 // since we know we're just going to delete tmpPath when we return,
2015 // so the swap saves that copy.
2016 dst->swap(tmpPath);
2017 } else {
2018 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019 }
2020 }
reed@google.comfd4be262012-05-25 01:04:12 +00002021 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022}
2023
senorblanco0abdf762015-08-20 11:10:41 -07002024bool SkPaint::canComputeFastBounds() const {
2025 if (this->getLooper()) {
2026 return this->getLooper()->canComputeFastBounds(*this);
2027 }
2028 if (this->getImageFilter() && !this->getImageFilter()->canComputeFastBounds()) {
2029 return false;
2030 }
2031 return !this->getRasterizer();
2032}
2033
reed@google.come4f10a72012-05-15 20:47:50 +00002034const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002035 SkRect* storage,
2036 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002038
reed@google.come4f10a72012-05-15 20:47:50 +00002039 const SkRect* src = &origSrc;
2040
reed@google.com9efd9a02012-01-30 15:41:43 +00002041 if (this->getLooper()) {
2042 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002043 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002044 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002046
reed@google.come4f10a72012-05-15 20:47:50 +00002047 SkRect tmpSrc;
2048 if (this->getPathEffect()) {
2049 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2050 src = &tmpSrc;
2051 }
2052
bsalomon56686482016-04-29 07:07:03 -07002053 SkScalar radius = SkStrokeRec::GetInflationRadius(*this, style);
2054 *storage = src->makeOutset(radius, radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002055
reed@google.com9efd9a02012-01-30 15:41:43 +00002056 if (this->getMaskFilter()) {
2057 this->getMaskFilter()->computeFastBounds(*storage, storage);
2058 }
2059
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002060 if (this->getImageFilter()) {
senorblancoe5e79842016-03-21 14:51:59 -07002061 *storage = this->getImageFilter()->computeFastBounds(*storage);
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002062 }
2063
reed@android.comd252db02009-04-01 18:31:44 +00002064 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065}
2066
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002067#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002068
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002069void SkPaint::toString(SkString* str) const {
2070 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2071
2072 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002073 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002074 SkDynamicMemoryWStream ostream;
2075 typeface->serialize(&ostream);
scroggoa1193e42015-01-21 12:09:53 -08002076 SkAutoTDelete<SkStreamAsset> istream(ostream.detachAsStream());
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002077
robertphillips3552ba12016-02-25 10:58:49 -08002078 SkFontDescriptor descriptor;
2079 if (!SkFontDescriptor::Deserialize(istream, &descriptor)) {
2080 str->append("<dt>FontDescriptor deserialization failed</dt>");
2081 } else {
2082 str->append("<dt>Font Family Name:</dt><dd>");
2083 str->append(descriptor.getFamilyName());
2084 str->append("</dd><dt>Font Full Name:</dt><dd>");
2085 str->append(descriptor.getFullName());
2086 str->append("</dd><dt>Font PS Name:</dt><dd>");
2087 str->append(descriptor.getPostscriptName());
2088 str->append("</dd>");
2089 }
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002090 }
2091
2092 str->append("<dt>TextSize:</dt><dd>");
2093 str->appendScalar(this->getTextSize());
2094 str->append("</dd>");
2095
2096 str->append("<dt>TextScaleX:</dt><dd>");
2097 str->appendScalar(this->getTextScaleX());
2098 str->append("</dd>");
2099
2100 str->append("<dt>TextSkewX:</dt><dd>");
2101 str->appendScalar(this->getTextSkewX());
2102 str->append("</dd>");
2103
2104 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002105 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002106 str->append("<dt>PathEffect:</dt><dd>");
robertphillips42dbfa82015-01-26 06:08:52 -08002107 pathEffect->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002108 str->append("</dd>");
2109 }
2110
2111 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002112 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002113 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002114 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002115 str->append("</dd>");
2116 }
2117
2118 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002119 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002120 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002121 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002122 str->append("</dd>");
2123 }
2124
2125 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002126 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002127 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002128 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002129 str->append("</dd>");
2130 }
2131
2132 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002133 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002134 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002135 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002136 str->append("</dd>");
2137 }
2138
2139 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002140 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002141 str->append("<dt>Rasterizer:</dt><dd>");
2142 str->append("</dd>");
2143 }
2144
2145 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002146 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002147 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002148 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002149 str->append("</dd>");
2150 }
2151
2152 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002153 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002154 str->append("<dt>ImageFilter:</dt><dd>");
robertphillipsf3f5bad2014-12-19 13:49:15 -08002155 imageFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002156 str->append("</dd>");
2157 }
2158
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002159 str->append("<dt>Color:</dt><dd>0x");
2160 SkColor color = this->getColor();
2161 str->appendHex(color);
2162 str->append("</dd>");
2163
2164 str->append("<dt>Stroke Width:</dt><dd>");
2165 str->appendScalar(this->getStrokeWidth());
2166 str->append("</dd>");
2167
2168 str->append("<dt>Stroke Miter:</dt><dd>");
2169 str->appendScalar(this->getStrokeMiter());
2170 str->append("</dd>");
2171
2172 str->append("<dt>Flags:</dt><dd>(");
2173 if (this->getFlags()) {
2174 bool needSeparator = false;
2175 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002176 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2177 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2178 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2179 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2180 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2181 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2182 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2183 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2184 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2185 "EmbeddedBitmapText", &needSeparator);
2186 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2187 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2188 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2189 "GenA8FromLCD", &needSeparator);
2190 } else {
2191 str->append("None");
2192 }
2193 str->append(")</dd>");
2194
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002195 str->append("<dt>FilterLevel:</dt><dd>");
reed93a12152015-03-16 10:08:34 -07002196 static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High" };
2197 str->append(gFilterQualityStrings[this->getFilterQuality()]);
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002198 str->append("</dd>");
2199
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002200 str->append("<dt>TextAlign:</dt><dd>");
2201 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2202 str->append(gTextAlignStrings[this->getTextAlign()]);
2203 str->append("</dd>");
2204
2205 str->append("<dt>CapType:</dt><dd>");
2206 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2207 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2208 str->append("</dd>");
2209
2210 str->append("<dt>JoinType:</dt><dd>");
2211 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2212 str->append(gJoinStrings[this->getStrokeJoin()]);
2213 str->append("</dd>");
2214
2215 str->append("<dt>Style:</dt><dd>");
2216 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2217 str->append(gStyleStrings[this->getStyle()]);
2218 str->append("</dd>");
2219
2220 str->append("<dt>TextEncoding:</dt><dd>");
2221 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2222 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2223 str->append("</dd>");
2224
2225 str->append("<dt>Hinting:</dt><dd>");
2226 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2227 str->append(gHintingStrings[this->getHinting()]);
2228 str->append("</dd>");
2229
2230 str->append("</dd></dl></dl>");
2231}
2232#endif
2233
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002234///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002236static bool has_thick_frame(const SkPaint& paint) {
2237 return paint.getStrokeWidth() > 0 &&
2238 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239}
2240
caryclark0449bcf2016-02-09 13:25:45 -08002241SkTextBaseIter::SkTextBaseIter(const char text[], size_t length,
robertphillipsfcf78292015-06-19 11:49:52 -07002242 const SkPaint& paint,
2243 bool applyStrokeAndPathEffects)
2244 : fPaint(paint) {
robertphillipse34f17d2016-07-19 07:59:22 -07002245 fGlyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
2246 paint.isDevKernText(),
2247 true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248
djsollen@google.com166e6532012-03-20 14:24:38 +00002249 fPaint.setLinearText(true);
halcanary96fcdcc2015-08-27 07:41:13 -07002250 fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251
halcanary96fcdcc2015-08-27 07:41:13 -07002252 if (fPaint.getPathEffect() == nullptr && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255
djsollen@google.com166e6532012-03-20 14:24:38 +00002256 // can't use our canonical size if we need to apply patheffects
halcanary96fcdcc2015-08-27 07:41:13 -07002257 if (fPaint.getPathEffect() == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2259 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002260 if (has_thick_frame(fPaint)) {
reed80ea19c2015-05-12 10:37:34 -07002261 fPaint.setStrokeWidth(fPaint.getStrokeWidth() / fScale);
djsollen@google.com166e6532012-03-20 14:24:38 +00002262 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002263 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002265 }
reed@google.com72cf4922011-01-04 19:58:20 +00002266
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002267 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 fPaint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07002269 fPaint.setPathEffect(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 }
2271
brianosmana1e8f8d2016-04-08 06:47:54 -07002272 // SRGBTODO: Is this correct?
2273 fCache = fPaint.detachCache(nullptr, SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags,
2274 nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275
2276 SkPaint::Style style = SkPaint::kFill_Style;
reeda4393342016-03-18 11:22:57 -07002277 sk_sp<SkPathEffect> pe;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002279 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280 style = paint.getStyle(); // restore
reeda4393342016-03-18 11:22:57 -07002281 pe = sk_ref_sp(paint.getPathEffect()); // restore
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 }
2283 fPaint.setStyle(style);
2284 fPaint.setPathEffect(pe);
reedefdfd512016-04-04 10:02:58 -07002285 fPaint.setMaskFilter(sk_ref_sp(paint.getMaskFilter())); // restore
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286
2287 // now compute fXOffset if needed
2288
2289 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002290 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002291 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002292 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
halcanary96fcdcc2015-08-27 07:41:13 -07002293 &count, nullptr), fScale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002294 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297 xOffset = -width;
2298 }
2299 fXPos = xOffset;
2300 fPrevAdvance = 0;
2301
2302 fText = text;
2303 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002304
reed@google.com44da42e2011-11-10 20:04:47 +00002305 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306}
2307
caryclark0449bcf2016-02-09 13:25:45 -08002308SkTextBaseIter::~SkTextBaseIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 SkGlyphCache::AttachCache(fCache);
2310}
2311
reed@google.com7b4531f2012-08-07 15:53:00 +00002312bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2313 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2315
benjaminwagner6b3eacb2016-03-24 19:07:58 -07002316 fXPos += SkScalarMul(fPrevAdvance + fAutoKern.adjust(glyph), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002317 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002319 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002320 if (path) {
2321 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002322 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002323 } else {
2324 if (path) {
halcanary96fcdcc2015-08-27 07:41:13 -07002325 *path = nullptr;
reed@google.com7b4531f2012-08-07 15:53:00 +00002326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002328 if (xpos) {
2329 *xpos = fXPos;
2330 }
2331 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002332 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002333 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002335
caryclark0449bcf2016-02-09 13:25:45 -08002336bool SkTextInterceptsIter::next(SkScalar* array, int* count) {
2337 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
benjaminwagner6b3eacb2016-03-24 19:07:58 -07002338 fXPos += SkScalarMul(fPrevAdvance + fAutoKern.adjust(glyph), fScale);
caryclark0449bcf2016-02-09 13:25:45 -08002339 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
2340 if (fCache->findPath(glyph)) {
2341 fCache->findIntercepts(fBounds, fScale, fXPos, SkToBool(fXYIndex),
2342 const_cast<SkGlyph*>(&glyph), array, count);
2343 }
2344 return fText < fStop;
2345}
2346
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002347///////////////////////////////////////////////////////////////////////////////
2348
reedf539b8c2014-11-11 12:51:33 -08002349// return true if the filter exists, and may affect alpha
2350static bool affects_alpha(const SkColorFilter* cf) {
2351 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2352}
2353
2354// return true if the filter exists, and may affect alpha
2355static bool affects_alpha(const SkImageFilter* imf) {
2356 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2357 // ala colorfilters
halcanary96fcdcc2015-08-27 07:41:13 -07002358 return imf != nullptr;
reedf539b8c2014-11-11 12:51:33 -08002359}
2360
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002361bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002362 if (fLooper) {
2363 return false;
2364 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002365 SkXfermode::Mode mode;
reeda5ab9ec2016-03-06 18:10:48 -08002366 if (SkXfermode::AsMode(fXfermode.get(), &mode)) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002367 switch (mode) {
2368 case SkXfermode::kSrcOver_Mode:
2369 case SkXfermode::kSrcATop_Mode:
2370 case SkXfermode::kDstOut_Mode:
2371 case SkXfermode::kDstOver_Mode:
2372 case SkXfermode::kPlus_Mode:
reedf539b8c2014-11-11 12:51:33 -08002373 if (0 == this->getAlpha()) {
reeda5ab9ec2016-03-06 18:10:48 -08002374 return !affects_alpha(fColorFilter.get()) && !affects_alpha(fImageFilter.get());
reedf539b8c2014-11-11 12:51:33 -08002375 }
2376 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002377 case SkXfermode::kDst_Mode:
2378 return true;
2379 default:
2380 break;
2381 }
2382 }
2383 return false;
2384}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002385
mtkleinfb1fe4f2014-10-07 09:26:10 -07002386uint32_t SkPaint::getHash() const {
2387 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2388 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
reedf70b5312016-03-04 16:36:20 -08002389 static_assert(offsetof(SkPaint, fBitfields) == 9 * sizeof(void*) + 6 * sizeof(uint32_t),
bungeman99fe8222015-08-20 07:57:51 -07002390 "SkPaint_notPackedTightly");
mtklein4e976072016-08-08 09:06:27 -07002391 return SkOpts::hash(reinterpret_cast<const uint32_t*>(this),
2392 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
mtkleinfb1fe4f2014-10-07 09:26:10 -07002393}