blob: ea33d5b6c9185d2f1275372f1c2ad712aca60365 [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"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000038
reed9a878a02015-12-27 12:47:25 -080039static inline uint32_t set_clear_mask(uint32_t bits, bool cond, uint32_t mask) {
40 return cond ? bits | mask : bits & ~mask;
41}
42
reed@google.coma3237872011-07-05 19:20:48 +000043// define this to get a printf for out-of-range parameter in setters
44// e.g. setTextSize(-1)
45//#define SK_REPORT_API_RANGE_CHECK
46
reed@android.coma3122b92009-08-13 20:38:25 +000047SkPaint::SkPaint() {
reedf59eab22014-07-14 14:39:15 -070048 fTextSize = SkPaintDefaults_TextSize;
49 fTextScaleX = SK_Scalar1;
50 fTextSkewX = 0;
51 fColor = SK_ColorBLACK;
52 fWidth = 0;
53 fMiterLimit = SkPaintDefaults_MiterLimit;
reed374772b2016-10-05 17:33:02 -070054 fBlendMode = (unsigned)SkBlendMode::kSrcOver;
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)
reeda5ab9ec2016-03-06 18:10:48 -080072 , COPY(fMaskFilter)
73 , COPY(fColorFilter)
74 , COPY(fRasterizer)
reed46f2d0a2016-09-11 05:40:31 -070075 , COPY(fDrawLooper)
reeda5ab9ec2016-03-06 18:10:48 -080076 , COPY(fImageFilter)
77 , COPY(fTextSize)
78 , COPY(fTextScaleX)
79 , COPY(fTextSkewX)
80 , COPY(fColor)
81 , COPY(fWidth)
82 , COPY(fMiterLimit)
reed374772b2016-10-05 17:33:02 -070083 , COPY(fBlendMode)
reeda5ab9ec2016-03-06 18:10:48 -080084 , 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);
reeda5ab9ec2016-03-06 18:10:48 -080093 MOVE(fMaskFilter);
94 MOVE(fColorFilter);
95 MOVE(fRasterizer);
reed46f2d0a2016-09-11 05:40:31 -070096 MOVE(fDrawLooper);
reeda5ab9ec2016-03-06 18:10:48 -080097 MOVE(fImageFilter);
bungemanccce0e02016-02-07 14:37:23 -080098 MOVE(fTextSize);
99 MOVE(fTextScaleX);
100 MOVE(fTextSkewX);
101 MOVE(fColor);
102 MOVE(fWidth);
103 MOVE(fMiterLimit);
reed374772b2016-10-05 17:33:02 -0700104 MOVE(fBlendMode);
bungemanccce0e02016-02-07 14:37:23 -0800105 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);
reeda5ab9ec2016-03-06 18:10:48 -0800120 ASSIGN(fMaskFilter);
121 ASSIGN(fColorFilter);
122 ASSIGN(fRasterizer);
reed46f2d0a2016-09-11 05:40:31 -0700123 ASSIGN(fDrawLooper);
reeda5ab9ec2016-03-06 18:10:48 -0800124 ASSIGN(fImageFilter);
125 ASSIGN(fTextSize);
126 ASSIGN(fTextScaleX);
127 ASSIGN(fTextSkewX);
128 ASSIGN(fColor);
129 ASSIGN(fWidth);
130 ASSIGN(fMiterLimit);
reed374772b2016-10-05 17:33:02 -0700131 ASSIGN(fBlendMode);
reeda5ab9ec2016-03-06 18:10:48 -0800132 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);
reeda5ab9ec2016-03-06 18:10:48 -0800147 MOVE(fMaskFilter);
148 MOVE(fColorFilter);
149 MOVE(fRasterizer);
reed46f2d0a2016-09-11 05:40:31 -0700150 MOVE(fDrawLooper);
reeda5ab9ec2016-03-06 18:10:48 -0800151 MOVE(fImageFilter);
bungemanccce0e02016-02-07 14:37:23 -0800152 MOVE(fTextSize);
153 MOVE(fTextScaleX);
154 MOVE(fTextSkewX);
155 MOVE(fColor);
156 MOVE(fWidth);
157 MOVE(fMiterLimit);
reed374772b2016-10-05 17:33:02 -0700158 MOVE(fBlendMode);
bungemanccce0e02016-02-07 14:37:23 -0800159 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)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000170 && EQUAL(fMaskFilter)
171 && EQUAL(fColorFilter)
172 && EQUAL(fRasterizer)
reed46f2d0a2016-09-11 05:40:31 -0700173 && EQUAL(fDrawLooper)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000174 && EQUAL(fImageFilter)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000175 && EQUAL(fTextSize)
176 && EQUAL(fTextScaleX)
177 && EQUAL(fTextSkewX)
178 && EQUAL(fColor)
179 && EQUAL(fWidth)
180 && EQUAL(fMiterLimit)
reed374772b2016-10-05 17:33:02 -0700181 && EQUAL(fBlendMode)
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
Mike Reed693fdbd2017-01-12 10:13:40 -0500187#define DEFINE_REF_FOO(type) sk_sp<Sk##type> SkPaint::ref##type() const { return f##type; }
188DEFINE_REF_FOO(ColorFilter)
189DEFINE_REF_FOO(DrawLooper)
190DEFINE_REF_FOO(ImageFilter)
191DEFINE_REF_FOO(MaskFilter)
192DEFINE_REF_FOO(PathEffect)
193DEFINE_REF_FOO(Rasterizer)
194DEFINE_REF_FOO(Shader)
195DEFINE_REF_FOO(Typeface)
196#undef DEFINE_REF_FOO
197
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000198void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 SkPaint init;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000201}
202
reedf803da12015-01-23 05:58:07 -0800203void SkPaint::setFilterQuality(SkFilterQuality quality) {
204 fBitfields.fFilterQuality = quality;
reed@google.comc9683152013-07-18 13:47:01 +0000205}
206
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000207void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700208 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209}
210
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000211void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700212 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213}
214
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000215void SkPaint::setAntiAlias(bool doAA) {
reed9a878a02015-12-27 12:47:25 -0800216 this->setFlags(set_clear_mask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217}
218
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000219void SkPaint::setDither(bool doDither) {
reed9a878a02015-12-27 12:47:25 -0800220 this->setFlags(set_clear_mask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221}
222
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000223void SkPaint::setSubpixelText(bool doSubpixel) {
reed9a878a02015-12-27 12:47:25 -0800224 this->setFlags(set_clear_mask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225}
226
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000227void SkPaint::setLCDRenderText(bool doLCDRender) {
reed9a878a02015-12-27 12:47:25 -0800228 this->setFlags(set_clear_mask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000229}
230
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000231void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reed9a878a02015-12-27 12:47:25 -0800232 this->setFlags(set_clear_mask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000233}
234
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000235void SkPaint::setAutohinted(bool useAutohinter) {
reed9a878a02015-12-27 12:47:25 -0800236 this->setFlags(set_clear_mask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000237}
238
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000239void SkPaint::setLinearText(bool doLinearText) {
reed9a878a02015-12-27 12:47:25 -0800240 this->setFlags(set_clear_mask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241}
242
reed@google.com830a23e2011-11-10 15:20:49 +0000243void SkPaint::setVerticalText(bool doVertical) {
reed9a878a02015-12-27 12:47:25 -0800244 this->setFlags(set_clear_mask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000245}
246
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000247void SkPaint::setUnderlineText(bool doUnderline) {
reed9a878a02015-12-27 12:47:25 -0800248 this->setFlags(set_clear_mask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249}
250
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000251void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed9a878a02015-12-27 12:47:25 -0800252 this->setFlags(set_clear_mask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253}
254
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000255void SkPaint::setFakeBoldText(bool doFakeBold) {
reed9a878a02015-12-27 12:47:25 -0800256 this->setFlags(set_clear_mask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257}
258
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000259void SkPaint::setDevKernText(bool doDevKern) {
reed9a878a02015-12-27 12:47:25 -0800260 this->setFlags(set_clear_mask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261}
262
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000263void SkPaint::setStyle(Style style) {
264 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700265 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000266 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000267#ifdef SK_REPORT_API_RANGE_CHECK
268 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
269#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271}
272
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000273void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 fColor = color;
275}
276
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000277void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000278 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
279 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000282void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000283 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284}
285
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286void SkPaint::setStrokeWidth(SkScalar width) {
287 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000289 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000290#ifdef SK_REPORT_API_RANGE_CHECK
291 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
292#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000293 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294}
295
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000296void SkPaint::setStrokeMiter(SkScalar limit) {
297 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000299 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000300#ifdef SK_REPORT_API_RANGE_CHECK
301 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
302#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304}
305
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000306void SkPaint::setStrokeCap(Cap ct) {
307 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700308 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000309 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000310#ifdef SK_REPORT_API_RANGE_CHECK
311 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
312#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314}
315
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000316void SkPaint::setStrokeJoin(Join jt) {
317 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700318 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000319 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000320#ifdef SK_REPORT_API_RANGE_CHECK
321 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
322#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324}
325
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000326///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000328void SkPaint::setTextAlign(Align align) {
329 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700330 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000332#ifdef SK_REPORT_API_RANGE_CHECK
333 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
334#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000335 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336}
337
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000338void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000339 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000342#ifdef SK_REPORT_API_RANGE_CHECK
343 SkDebugf("SkPaint::setTextSize() called with negative value\n");
344#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000345 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346}
347
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 fTextScaleX = scaleX;
350}
351
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000352void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 fTextSkewX = skewX;
354}
355
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000356void SkPaint::setTextEncoding(TextEncoding encoding) {
357 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700358 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000359 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000360#ifdef SK_REPORT_API_RANGE_CHECK
361 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
362#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364}
365
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000366///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367
reeda5ab9ec2016-03-06 18:10:48 -0800368#define MOVE_FIELD(Field) void SkPaint::set##Field(sk_sp<Sk##Field> f) { f##Field = std::move(f); }
369MOVE_FIELD(Typeface)
370MOVE_FIELD(Rasterizer)
371MOVE_FIELD(ImageFilter)
372MOVE_FIELD(Shader)
373MOVE_FIELD(ColorFilter)
reeda5ab9ec2016-03-06 18:10:48 -0800374MOVE_FIELD(PathEffect)
375MOVE_FIELD(MaskFilter)
reed46f2d0a2016-09-11 05:40:31 -0700376MOVE_FIELD(DrawLooper)
reeda5ab9ec2016-03-06 18:10:48 -0800377#undef MOVE_FIELD
reed46f2d0a2016-09-11 05:40:31 -0700378void SkPaint::setLooper(sk_sp<SkDrawLooper> looper) { fDrawLooper = std::move(looper); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380///////////////////////////////////////////////////////////////////////////////
381
reed@google.comed43dff2013-06-04 16:56:27 +0000382static SkScalar mag2(SkScalar x, SkScalar y) {
383 return x * x + y * y;
384}
385
386static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
387 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
388 ||
389 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
390}
391
392bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
393 SkASSERT(!ctm.hasPerspective());
394 SkASSERT(!textM.hasPerspective());
395
396 SkMatrix matrix;
397 matrix.setConcat(ctm, textM);
398 return tooBig(matrix, MaxCacheSize2());
399}
400
reed@google.comed43dff2013-06-04 16:56:27 +0000401
402///////////////////////////////////////////////////////////////////////////////
403
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404#include "SkGlyphCache.h"
405#include "SkUtils.h"
406
reeda9322c22016-04-12 06:47:05 -0700407static void DetachDescProc(SkTypeface* typeface, const SkScalerContextEffects& effects,
408 const SkDescriptor* desc, void* context) {
409 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, effects, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000410}
411
reeda9322c22016-04-12 06:47:05 -0700412int SkPaint::textToGlyphs(const void* textData, size_t byteLength, uint16_t glyphs[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 if (byteLength == 0) {
414 return 0;
415 }
reed@google.com72cf4922011-01-04 19:58:20 +0000416
halcanary96fcdcc2015-08-27 07:41:13 -0700417 SkASSERT(textData != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
halcanary96fcdcc2015-08-27 07:41:13 -0700419 if (nullptr == glyphs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 switch (this->getTextEncoding()) {
421 case kUTF8_TextEncoding:
422 return SkUTF8_CountUnichars((const char*)textData, byteLength);
423 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000424 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000425 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000426 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000428 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000430 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 }
432 return 0;
433 }
reed@google.com72cf4922011-01-04 19:58:20 +0000434
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 // handle this encoding before the setup for the glyphcache
438 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
439 // we want to ignore the low bit of byteLength
440 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000441 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 }
reed@google.com72cf4922011-01-04 19:58:20 +0000443
halcanary96fcdcc2015-08-27 07:41:13 -0700444 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 SkGlyphCache* cache = autoCache.getCache();
446
447 const char* text = (const char*)textData;
448 const char* stop = text + byteLength;
449 uint16_t* gptr = glyphs;
450
451 switch (this->getTextEncoding()) {
452 case SkPaint::kUTF8_TextEncoding:
453 while (text < stop) {
Hal Canaryd1c8e562017-01-11 15:53:25 -0500454 SkUnichar u = SkUTF8_NextUnicharWithError(&text, stop);
455 if (u < 0) {
456 return 0; // bad UTF-8 sequence
457 }
458 *gptr++ = cache->unicharToGlyph(u);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 }
460 break;
461 case SkPaint::kUTF16_TextEncoding: {
462 const uint16_t* text16 = (const uint16_t*)text;
463 const uint16_t* stop16 = (const uint16_t*)stop;
464 while (text16 < stop16) {
465 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
466 }
467 break;
468 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000469 case kUTF32_TextEncoding: {
470 const int32_t* text32 = (const int32_t*)text;
471 const int32_t* stop32 = (const int32_t*)stop;
472 while (text32 < stop32) {
473 *gptr++ = cache->unicharToGlyph(*text32++);
474 }
475 break;
476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000478 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000480 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481}
482
reed@android.coma5dcaf62010-02-05 17:12:32 +0000483bool SkPaint::containsText(const void* textData, size_t byteLength) const {
484 if (0 == byteLength) {
485 return true;
486 }
reed@google.com72cf4922011-01-04 19:58:20 +0000487
halcanary96fcdcc2015-08-27 07:41:13 -0700488 SkASSERT(textData != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000489
reed@android.coma5dcaf62010-02-05 17:12:32 +0000490 // handle this encoding before the setup for the glyphcache
491 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
492 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
493 size_t count = byteLength >> 1;
494 for (size_t i = 0; i < count; i++) {
495 if (0 == glyphID[i]) {
496 return false;
497 }
498 }
499 return true;
500 }
reed@google.com72cf4922011-01-04 19:58:20 +0000501
halcanary96fcdcc2015-08-27 07:41:13 -0700502 SkAutoGlyphCache autoCache(*this, nullptr, nullptr);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000503 SkGlyphCache* cache = autoCache.getCache();
504
505 switch (this->getTextEncoding()) {
506 case SkPaint::kUTF8_TextEncoding: {
507 const char* text = static_cast<const char*>(textData);
508 const char* stop = text + byteLength;
509 while (text < stop) {
510 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
511 return false;
512 }
513 }
514 break;
515 }
516 case SkPaint::kUTF16_TextEncoding: {
517 const uint16_t* text = static_cast<const uint16_t*>(textData);
518 const uint16_t* stop = text + (byteLength >> 1);
519 while (text < stop) {
520 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
521 return false;
522 }
523 }
524 break;
525 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000526 case SkPaint::kUTF32_TextEncoding: {
527 const int32_t* text = static_cast<const int32_t*>(textData);
528 const int32_t* stop = text + (byteLength >> 2);
529 while (text < stop) {
530 if (0 == cache->unicharToGlyph(*text++)) {
531 return false;
532 }
533 }
534 break;
535 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000536 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000537 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000538 return false;
539 }
540 return true;
541}
542
robertphillipsd24955a2015-06-26 12:17:59 -0700543void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, SkUnichar textData[]) const {
reed@android.com9d3a9852010-01-08 14:07:42 +0000544 if (count <= 0) {
545 return;
546 }
547
halcanary96fcdcc2015-08-27 07:41:13 -0700548 SkASSERT(glyphs != nullptr);
549 SkASSERT(textData != nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000550
robertphillipsd24955a2015-06-26 12:17:59 -0700551 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
halcanary96fcdcc2015-08-27 07:41:13 -0700552 SkAutoGlyphCache autoCache(*this, &props, nullptr);
reed@android.com9d3a9852010-01-08 14:07:42 +0000553 SkGlyphCache* cache = autoCache.getCache();
554
555 for (int index = 0; index < count; index++) {
556 textData[index] = cache->glyphToUnichar(glyphs[index]);
557 }
558}
559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560///////////////////////////////////////////////////////////////////////////////
561
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000562static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
563 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700564 SkASSERT(cache != nullptr);
565 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000566
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
568}
569
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000570static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
571 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700572 SkASSERT(cache != nullptr);
573 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
576}
577
reed@google.com68bc6f72012-03-14 19:41:55 +0000578static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
579 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700580 SkASSERT(cache != nullptr);
581 SkASSERT(text != nullptr);
reed@google.com68bc6f72012-03-14 19:41:55 +0000582
583 const int32_t* ptr = *(const int32_t**)text;
584 SkUnichar uni = *ptr++;
585 *text = (const char*)ptr;
586 return cache->getUnicharMetrics(uni);
587}
588
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000589static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
590 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700591 SkASSERT(cache != nullptr);
592 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 const uint16_t* ptr = *(const uint16_t**)text;
595 unsigned glyphID = *ptr;
596 ptr += 1;
597 *text = (const char*)ptr;
598 return cache->getGlyphIDMetrics(glyphID);
599}
600
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000601static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
602 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700603 SkASSERT(cache != nullptr);
604 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
607}
608
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000609static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
610 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700611 SkASSERT(cache != nullptr);
612 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
615}
616
reed@google.com68bc6f72012-03-14 19:41:55 +0000617static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
618 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700619 SkASSERT(cache != nullptr);
620 SkASSERT(text != nullptr);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000621
reed@google.com68bc6f72012-03-14 19:41:55 +0000622 const int32_t* ptr = *(const int32_t**)text;
623 SkUnichar uni = *ptr++;
624 *text = (const char*)ptr;
625 return cache->getUnicharAdvance(uni);
626}
627
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000628static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
629 const char** text) {
halcanary96fcdcc2015-08-27 07:41:13 -0700630 SkASSERT(cache != nullptr);
631 SkASSERT(text != nullptr);
reed@google.com72cf4922011-01-04 19:58:20 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 const uint16_t* ptr = *(const uint16_t**)text;
634 unsigned glyphID = *ptr;
635 ptr += 1;
636 *text = (const char*)ptr;
637 return cache->getGlyphIDAdvance(glyphID);
638}
639
robertphillipse34f17d2016-07-19 07:59:22 -0700640SkPaint::GlyphCacheProc SkPaint::GetGlyphCacheProc(TextEncoding encoding,
641 bool isDevKern,
642 bool needFullMetrics) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800643 static const GlyphCacheProc gGlyphCacheProcs[] = {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 sk_getMetrics_utf8_next,
645 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000646 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000648
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 sk_getAdvance_utf8_next,
650 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000651 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 sk_getAdvance_glyph_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 };
reed@google.com72cf4922011-01-04 19:58:20 +0000654
robertphillipse34f17d2016-07-19 07:59:22 -0700655 unsigned index = encoding;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656
robertphillipse34f17d2016-07-19 07:59:22 -0700657 if (!needFullMetrics && !isDevKern) {
reed9e96aa02014-10-03 12:44:37 -0700658 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000659 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660
benjaminwagnerd936f632016-02-23 10:44:31 -0800661 SkASSERT(index < SK_ARRAY_COUNT(gGlyphCacheProcs));
662 return gGlyphCacheProcs[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663}
664
665///////////////////////////////////////////////////////////////////////////////
666
reed@google.comed43dff2013-06-04 16:56:27 +0000667#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
668SkPaint::kDevKernText_Flag | \
669SkPaint::kLinearText_Flag | \
670SkPaint::kLCDRenderText_Flag | \
671SkPaint::kEmbeddedBitmapText_Flag | \
672SkPaint::kAutoHinting_Flag | \
673SkPaint::kGenA8FromLCD_Flag )
674
675SkScalar SkPaint::setupForAsPaths() {
676 uint32_t flags = this->getFlags();
677 // clear the flags we don't care about
678 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
679 // set the flags we do care about
680 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000681
reed@google.comed43dff2013-06-04 16:56:27 +0000682 this->setFlags(flags);
683 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000684
reed@google.comed43dff2013-06-04 16:56:27 +0000685 SkScalar textSize = fTextSize;
686 this->setTextSize(kCanonicalTextSizeForPaths);
687 return textSize / kCanonicalTextSizeForPaths;
688}
689
690class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691public:
reed@google.comed43dff2013-06-04 16:56:27 +0000692 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700693 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000694 SkPaint* p = fLazy.set(paint);
695 fScale = p->setupForAsPaths();
696 fPaint = p;
697 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000699
reed@google.comed43dff2013-06-04 16:56:27 +0000700 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000701
reed@google.comed43dff2013-06-04 16:56:27 +0000702 /**
703 * Returns 0 if the paint was unmodified, or the scale factor need to
704 * the original textSize
705 */
706 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000707
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708private:
reed@google.comed43dff2013-06-04 16:56:27 +0000709 const SkPaint* fPaint;
710 SkScalar fScale;
711 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712};
713
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000714static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 bounds->set(SkIntToScalar(g.fLeft),
716 SkIntToScalar(g.fTop),
717 SkIntToScalar(g.fLeft + g.fWidth),
718 SkIntToScalar(g.fTop + g.fHeight));
719}
720
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700721static void join_bounds_x(const SkGlyph& g, SkRect* bounds, SkScalar dx) {
722 bounds->join(SkIntToScalar(g.fLeft) + dx,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 SkIntToScalar(g.fTop),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700724 SkIntToScalar(g.fLeft + g.fWidth) + dx,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 SkIntToScalar(g.fTop + g.fHeight));
726}
727
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700728static void join_bounds_y(const SkGlyph& g, SkRect* bounds, SkScalar dy) {
reed@google.com44da42e2011-11-10 20:04:47 +0000729 bounds->join(SkIntToScalar(g.fLeft),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700730 SkIntToScalar(g.fTop) + dy,
reed@google.com44da42e2011-11-10 20:04:47 +0000731 SkIntToScalar(g.fLeft + g.fWidth),
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700732 SkIntToScalar(g.fTop + g.fHeight) + dy);
reed@google.com44da42e2011-11-10 20:04:47 +0000733}
734
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700735typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, SkScalar);
reed@google.com44da42e2011-11-10 20:04:47 +0000736
737// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700738static SkScalar advance(const SkGlyph& glyph, int xyIndex) {
reed@google.com44da42e2011-11-10 20:04:47 +0000739 SkASSERT(0 == xyIndex || 1 == xyIndex);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700740 return SkFloatToScalar((&glyph.fAdvanceX)[xyIndex]);
reed@google.com44da42e2011-11-10 20:04:47 +0000741}
742
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743SkScalar SkPaint::measure_text(SkGlyphCache* cache,
744 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000745 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000747 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000749 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000751 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 return 0;
753 }
754
robertphillipse34f17d2016-07-19 07:59:22 -0700755 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(this->getTextEncoding(),
756 this->isDevKernText(),
757 nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758
reed@google.com44da42e2011-11-10 20:04:47 +0000759 int xyIndex;
760 JoinBoundsProc joinBoundsProc;
761 if (this->isVerticalText()) {
762 xyIndex = 1;
763 joinBoundsProc = join_bounds_y;
764 } else {
765 xyIndex = 0;
766 joinBoundsProc = join_bounds_x;
767 }
768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 int n = 1;
770 const char* stop = (const char*)text + byteLength;
771 const SkGlyph* g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700772 SkScalar x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773
halcanary96fcdcc2015-08-27 07:41:13 -0700774 if (nullptr == bounds) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000775 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 for (; text < stop; n++) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700777 const int rsb = g->fRsbDelta;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700779 x += SkAutoKern_Adjust(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000781 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000783 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 }
785 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000786 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000788 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 for (; text < stop; n++) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700790 const int rsb = g->fRsbDelta;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 g = &glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700792 x += SkAutoKern_Adjust(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000793 joinBoundsProc(*g, bounds, x);
794 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000796 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 for (; text < stop; n++) {
798 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000799 joinBoundsProc(*g, bounds, x);
800 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 }
802 }
803 }
804 SkASSERT(text == stop);
805
806 *count = n;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700807 return x;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808}
809
reed99ae8812014-08-26 11:30:01 -0700810SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -0700812 SkASSERT(text != nullptr || length == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813
reed@google.comed43dff2013-06-04 16:56:27 +0000814 SkCanonicalizePaint canon(*this);
815 const SkPaint& paint = canon.getPaint();
816 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000817
halcanary96fcdcc2015-08-27 07:41:13 -0700818 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 SkGlyphCache* cache = autoCache.getCache();
820
821 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000822
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000823 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 int tempCount;
825
reed@google.comed43dff2013-06-04 16:56:27 +0000826 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000827 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000829 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
831 bounds->fTop = SkScalarMul(bounds->fTop, scale);
832 bounds->fRight = SkScalarMul(bounds->fRight, scale);
833 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
834 }
835 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000836 } else if (bounds) {
837 // ensure that even if we don't measure_text we still update the bounds
838 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 }
840 return width;
841}
842
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700844 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000845 if (0 == length || 0 >= maxWidth) {
846 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000848 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return 0;
850 }
851
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000852 if (0 == fTextSize) {
853 if (measuredWidth) {
854 *measuredWidth = 0;
855 }
856 return length;
857 }
858
halcanary96fcdcc2015-08-27 07:41:13 -0700859 SkASSERT(textD != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -0700861 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862
reed@google.comed43dff2013-06-04 16:56:27 +0000863 SkCanonicalizePaint canon(*this);
864 const SkPaint& paint = canon.getPaint();
865 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866
reed@google.comed43dff2013-06-04 16:56:27 +0000867 // adjust max in case we changed the textSize in paint
868 if (scale) {
869 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 }
reed@google.com72cf4922011-01-04 19:58:20 +0000871
halcanary96fcdcc2015-08-27 07:41:13 -0700872 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 SkGlyphCache* cache = autoCache.getCache();
874
robertphillipse34f17d2016-07-19 07:59:22 -0700875 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
876 paint.isDevKernText(),
877 false);
reed@google.comed43dff2013-06-04 16:56:27 +0000878 const int xyIndex = paint.isVerticalText() ? 1 : 0;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700879 SkScalar width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000881 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -0700883 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 const char* curr = text;
885 const SkGlyph& g = glyphCacheProc(cache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700886 SkScalar x = SkAutoKern_Adjust(rsb, g.fLsbDelta) + advance(g, xyIndex);
887 if ((width += x) > maxWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 width -= x;
889 text = curr;
890 break;
891 }
892 rsb = g.fRsbDelta;
893 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000894 } else {
reed9e96aa02014-10-03 12:44:37 -0700895 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 const char* curr = text;
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700897 SkScalar x = advance(glyphCacheProc(cache, &text), xyIndex);
898 if ((width += x) > maxWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 width -= x;
900 text = curr;
901 break;
902 }
903 }
904 }
reed@google.com72cf4922011-01-04 19:58:20 +0000905
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000906 if (measuredWidth) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000907 if (scale) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700908 width *= scale;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000909 }
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700910 *measuredWidth = width;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 }
reed@google.com72cf4922011-01-04 19:58:20 +0000912
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -0700914 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915}
916
917///////////////////////////////////////////////////////////////////////////////
918
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000919static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +0000920 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 return false; // don't detach the cache
922}
923
reeda9322c22016-04-12 06:47:05 -0700924static void FontMetricsDescProc(SkTypeface* typeface, const SkScalerContextEffects& effects,
925 const SkDescriptor* desc, void* context) {
926 SkGlyphCache::VisitCache(typeface, effects, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927}
928
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000929SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +0000930 SkCanonicalizePaint canon(*this);
931 const SkPaint& paint = canon.getPaint();
932 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000933
halcanary96fcdcc2015-08-27 07:41:13 -0700934 SkMatrix zoomMatrix, *zoomPtr = nullptr;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000935 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 zoomMatrix.setScale(zoom, zoom);
937 zoomPtr = &zoomMatrix;
938 }
939
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 FontMetrics storage;
halcanary96fcdcc2015-08-27 07:41:13 -0700941 if (nullptr == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000943 }
reed@google.com72cf4922011-01-04 19:58:20 +0000944
brianosmana1e8f8d2016-04-08 06:47:54 -0700945 paint.descriptorProc(nullptr, kNone_ScalerContextFlags, zoomPtr, FontMetricsDescProc, metrics);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000947 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 metrics->fTop = SkScalarMul(metrics->fTop, scale);
949 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
950 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
951 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
952 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +0000953 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
954 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
955 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
956 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000957 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
958 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 }
960 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
961}
962
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000963///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000965static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 bounds->set(g.fLeft * scale,
967 g.fTop * scale,
968 (g.fLeft + g.fWidth) * scale,
969 (g.fTop + g.fHeight) * scale);
970}
971
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000972int SkPaint::getTextWidths(const void* textData, size_t byteLength,
973 SkScalar widths[], SkRect bounds[]) const {
974 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000976 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977
bsalomon49f085d2014-09-05 13:34:00 -0700978 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979
halcanary96fcdcc2015-08-27 07:41:13 -0700980 if (nullptr == widths && nullptr == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000982 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
reed@google.comed43dff2013-06-04 16:56:27 +0000984 SkCanonicalizePaint canon(*this);
985 const SkPaint& paint = canon.getPaint();
986 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987
halcanary96fcdcc2015-08-27 07:41:13 -0700988 SkAutoGlyphCache autoCache(paint, nullptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 SkGlyphCache* cache = autoCache.getCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700990 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
991 paint.isDevKernText(),
992 nullptr != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
994 const char* text = (const char*)textData;
995 const char* stop = text + byteLength;
996 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +0000997 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000999 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 // we adjust the widths returned here through auto-kerning
1001 SkAutoKern autokern;
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001002 SkScalar prevWidth = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003
1004 if (scale) {
1005 while (text < stop) {
1006 const SkGlyph& g = glyphCacheProc(cache, &text);
1007 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001008 SkScalar adjust = autokern.adjust(g);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009
1010 if (count > 0) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001011 *widths++ = SkScalarMul(prevWidth + adjust, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
reed@google.com44da42e2011-11-10 20:04:47 +00001013 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 }
1015 if (bounds) {
1016 set_bounds(g, bounds++, scale);
1017 }
1018 ++count;
1019 }
1020 if (count > 0 && widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001021 *widths = SkScalarMul(prevWidth, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 }
1023 } else {
1024 while (text < stop) {
1025 const SkGlyph& g = glyphCacheProc(cache, &text);
1026 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001027 SkScalar adjust = autokern.adjust(g);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028
1029 if (count > 0) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001030 *widths++ = prevWidth + adjust;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 }
reed@google.com44da42e2011-11-10 20:04:47 +00001032 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 }
1034 if (bounds) {
1035 set_bounds(g, bounds++);
1036 }
1037 ++count;
1038 }
1039 if (count > 0 && widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001040 *widths = prevWidth;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 }
1042 }
1043 } else { // no devkern
1044 if (scale) {
1045 while (text < stop) {
1046 const SkGlyph& g = glyphCacheProc(cache, &text);
1047 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001048 *widths++ = SkScalarMul(advance(g, xyIndex),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 scale);
1050 }
1051 if (bounds) {
1052 set_bounds(g, bounds++, scale);
1053 }
1054 ++count;
1055 }
1056 } else {
1057 while (text < stop) {
1058 const SkGlyph& g = glyphCacheProc(cache, &text);
1059 if (widths) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001060 *widths++ = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 }
1062 if (bounds) {
1063 set_bounds(g, bounds++);
1064 }
1065 ++count;
1066 }
1067 }
1068 }
1069
1070 SkASSERT(text == stop);
1071 return count;
1072}
1073
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001074///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
1076#include "SkDraw.h"
1077
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001078void SkPaint::getTextPath(const void* textData, size_t length,
1079 SkScalar x, SkScalar y, SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001080 SkASSERT(length == 0 || textData != nullptr);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001081
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001083 if (text == nullptr || length == 0 || path == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001085 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086
djsollen@google.com166e6532012-03-20 14:24:38 +00001087 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 SkMatrix matrix;
1089 SkScalar prevXPos = 0;
1090
1091 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1092 matrix.postTranslate(x, y);
1093 path->reset();
1094
1095 SkScalar xpos;
1096 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001097 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001099 if (iterPath) {
1100 path->addPath(*iterPath, matrix);
1101 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 prevXPos = xpos;
1103 }
1104}
1105
reed@google.comca0062e2012-07-20 11:20:32 +00001106void SkPaint::getPosTextPath(const void* textData, size_t length,
1107 const SkPoint pos[], SkPath* path) const {
halcanary96fcdcc2015-08-27 07:41:13 -07001108 SkASSERT(length == 0 || textData != nullptr);
reed@google.comca0062e2012-07-20 11:20:32 +00001109
1110 const char* text = (const char*)textData;
halcanary96fcdcc2015-08-27 07:41:13 -07001111 if (text == nullptr || length == 0 || path == nullptr) {
reed@google.comca0062e2012-07-20 11:20:32 +00001112 return;
1113 }
1114
1115 SkTextToPathIter iter(text, length, *this, false);
1116 SkMatrix matrix;
1117 SkPoint prevPos;
1118 prevPos.set(0, 0);
1119
1120 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1121 path->reset();
1122
1123 unsigned int i = 0;
1124 const SkPath* iterPath;
halcanary96fcdcc2015-08-27 07:41:13 -07001125 while (iter.next(&iterPath, nullptr)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001126 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001127 if (iterPath) {
1128 path->addPath(*iterPath, matrix);
1129 }
reed@google.comca0062e2012-07-20 11:20:32 +00001130 prevPos = pos[i];
1131 i++;
1132 }
1133}
1134
fmalitaeae6a912016-07-28 09:47:24 -07001135template <SkTextInterceptsIter::TextType TextType, typename Func>
1136int GetTextIntercepts(const SkPaint& paint, const void* text, size_t length,
1137 const SkScalar bounds[2], SkScalar* array, Func posMaker) {
1138 SkASSERT(length == 0 || text != nullptr);
caryclark0449bcf2016-02-09 13:25:45 -08001139 if (!length) {
1140 return 0;
1141 }
1142
fmalitaeae6a912016-07-28 09:47:24 -07001143 const SkPoint pos0 = posMaker(0);
1144 SkTextInterceptsIter iter(static_cast<const char*>(text), length, paint, bounds,
1145 pos0.x(), pos0.y(), TextType);
1146
caryclark0449bcf2016-02-09 13:25:45 -08001147 int i = 0;
1148 int count = 0;
1149 while (iter.next(array, &count)) {
fmalitaeae6a912016-07-28 09:47:24 -07001150 if (TextType == SkTextInterceptsIter::TextType::kPosText) {
1151 const SkPoint pos = posMaker(++i);
1152 iter.setPosition(pos.x(), pos.y());
1153 }
caryclark0449bcf2016-02-09 13:25:45 -08001154 }
fmalitaeae6a912016-07-28 09:47:24 -07001155
1156 return count;
1157}
1158
1159int SkPaint::getTextIntercepts(const void* textData, size_t length,
1160 SkScalar x, SkScalar y, const SkScalar bounds[2],
1161 SkScalar* array) const {
1162
1163 return GetTextIntercepts<SkTextInterceptsIter::TextType::kText>(
1164 *this, textData, length, bounds, array, [&x, &y] (int) -> SkPoint {
1165 return SkPoint::Make(x, y);
1166 });
1167}
1168
1169int SkPaint::getPosTextIntercepts(const void* textData, size_t length, const SkPoint pos[],
1170 const SkScalar bounds[2], SkScalar* array) const {
1171
1172 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
1173 *this, textData, length, bounds, array, [&pos] (int i) -> SkPoint {
1174 return pos[i];
1175 });
1176}
1177
1178int SkPaint::getPosTextHIntercepts(const void* textData, size_t length, const SkScalar xpos[],
1179 SkScalar constY, const SkScalar bounds[2],
1180 SkScalar* array) const {
1181
1182 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
1183 *this, textData, length, bounds, array, [&xpos, &constY] (int i) -> SkPoint {
1184 return SkPoint::Make(xpos[i], constY);
1185 });
1186}
1187
1188int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2],
1189 SkScalar* intervals) const {
1190 int count = 0;
1191 SkPaint runPaint(*this);
1192
1193 SkTextBlobRunIterator it(blob);
1194 while (!it.done()) {
1195 it.applyFontToPaint(&runPaint);
1196 const size_t runByteCount = it.glyphCount() * sizeof(SkGlyphID);
1197 SkScalar* runIntervals = intervals ? intervals + count : nullptr;
1198
1199 switch (it.positioning()) {
1200 case SkTextBlob::kDefault_Positioning:
1201 count += runPaint.getTextIntercepts(it.glyphs(), runByteCount, it.offset().x(),
1202 it.offset().y(), bounds, runIntervals);
1203 break;
1204 case SkTextBlob::kHorizontal_Positioning:
1205 count += runPaint.getPosTextHIntercepts(it.glyphs(), runByteCount, it.pos(),
1206 it.offset().y(), bounds, runIntervals);
1207 break;
1208 case SkTextBlob::kFull_Positioning:
1209 count += runPaint.getPosTextIntercepts(it.glyphs(), runByteCount,
1210 reinterpret_cast<const SkPoint*>(it.pos()),
1211 bounds, runIntervals);
1212 break;
1213 }
1214
1215 it.next();
1216 }
1217
caryclark0449bcf2016-02-09 13:25:45 -08001218 return count;
1219}
1220
reed8893e5f2014-12-15 13:27:26 -08001221SkRect SkPaint::getFontBounds() const {
1222 SkMatrix m;
1223 m.setScale(fTextSize * fTextScaleX, fTextSize);
1224 m.postSkew(fTextSkewX, 0);
1225
1226 SkTypeface* typeface = this->getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001227 if (nullptr == typeface) {
reed8893e5f2014-12-15 13:27:26 -08001228 typeface = SkTypeface::GetDefaultTypeface();
1229 }
1230
1231 SkRect bounds;
1232 m.mapRect(&bounds, typeface->getBounds());
1233 return bounds;
1234}
1235
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236static void add_flattenable(SkDescriptor* desc, uint32_t tag,
brianosmanfad98562016-05-04 11:06:28 -07001237 SkBinaryWriteBuffer* buffer) {
halcanary96fcdcc2015-08-27 07:41:13 -07001238 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), nullptr));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239}
1240
joshualitt20dac882015-07-24 13:16:24 -07001241static SkMask::Format compute_mask_format(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001244 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001245 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001246 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001247 }
1248
reed@google.com65dd8f82011-03-14 13:31:16 +00001249 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001250 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001251 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001252
1253 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254}
1255
reed@android.com1cdcb512009-08-24 19:11:00 +00001256// if linear-text is on, then we force hinting to be off (since that's sort of
1257// the point of linear-text.
1258static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1259 SkPaint::Hinting h = paint.getHinting();
1260 if (paint.isLinearText()) {
1261 h = SkPaint::kNo_Hinting;
1262 }
1263 return h;
1264}
1265
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001266// return true if the paint is just a single color (i.e. not a shader). If its
1267// a shader, then we can't compute a const luminance for it :(
1268static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001269 SkColor c = paint.getColor();
1270
1271 SkShader* shader = paint.getShader();
1272 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001273 return false;
1274 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001275 if (paint.getColorFilter()) {
1276 c = paint.getColorFilter()->filterColor(c);
1277 }
1278 if (color) {
1279 *color = c;
1280 }
1281 return true;
1282}
1283
joshualitt9e36c1a2015-04-14 12:17:27 -07001284SkColor SkPaint::computeLuminanceColor() const {
reed@google.comce6dbb62012-02-10 22:01:45 +00001285 SkColor c;
joshualitt9e36c1a2015-04-14 12:17:27 -07001286 if (!justAColor(*this, &c)) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001287 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1288 }
1289 return c;
1290}
reed@google.com813d38b2012-02-13 21:37:57 +00001291
reed@google.comdd43df92012-02-15 14:50:29 +00001292#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1293
reed@google.com4f79b9b2011-09-13 13:23:26 +00001294// Beyond this size, LCD doesn't appreciably improve quality, but it always
1295// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001296#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1297 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1298#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001299
reedb3da83a2014-10-01 12:06:12 -07001300const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1301
reed4942e752014-10-01 13:59:33 -07001302static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001303 if (checkPost2x2) {
1304 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1305 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001306 area *= rec.fTextSize * rec.fTextSize;
1307 return area > gMaxSize2ForLCDText;
1308 } else {
1309 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001310 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001311}
1312
reed@google.com72cf4922011-01-04 19:58:20 +00001313/*
1314 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1315 * that vary only slightly when we create our key into the font cache, since the font scaler
1316 * typically returns the same looking resuts for tiny changes in the matrix.
1317 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001318static SkScalar sk_relax(SkScalar x) {
bungeman27876bc2016-02-29 11:22:55 -08001319 SkScalar n = SkScalarRoundToScalar(x * 1024);
reed@android.comf2b98d62010-12-20 18:26:13 +00001320 return n / 1024.0f;
1321}
1322
reed@android.com36a4c2a2009-07-22 19:52:11 +00001323void SkScalerContext::MakeRec(const SkPaint& paint,
robertphillipsfcf78292015-06-19 11:49:52 -07001324 const SkSurfaceProps* surfaceProps,
bungeman@google.com532470f2013-01-22 19:25:14 +00001325 const SkMatrix* deviceMatrix,
1326 Rec* rec) {
halcanary96fcdcc2015-08-27 07:41:13 -07001327 SkASSERT(deviceMatrix == nullptr || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001329 SkTypeface* typeface = paint.getTypeface();
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (nullptr == typeface) {
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001331 typeface = SkTypeface::GetDefaultTypeface();
1332 }
bungemanec730b92014-08-18 07:57:35 -07001333 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 rec->fTextSize = paint.getTextSize();
1335 rec->fPreScaleX = paint.getTextScaleX();
1336 rec->fPreSkewX = paint.getTextSkewX();
1337
reedb3da83a2014-10-01 12:06:12 -07001338 bool checkPost2x2 = false;
1339
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001340 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001341 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1342 if (mask & SkMatrix::kScale_Mask) {
1343 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1344 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1345 checkPost2x2 = true;
1346 } else {
1347 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1348 }
1349 if (mask & SkMatrix::kAffine_Mask) {
1350 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1351 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1352 checkPost2x2 = true;
1353 } else {
1354 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1355 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001356 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1358 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1359 }
reed@google.com72cf4922011-01-04 19:58:20 +00001360
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 SkPaint::Style style = paint.getStyle();
1362 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001363
reed@google.comffe49f52011-11-22 19:42:41 +00001364 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001365
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001366 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001367#ifdef SK_USE_FREETYPE_EMBOLDEN
1368 flags |= SkScalerContext::kEmbolden_Flag;
1369#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001370 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1371 kStdFakeBoldInterpKeys,
1372 kStdFakeBoldInterpValues,
1373 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001375
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001376 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 style = SkPaint::kStrokeAndFill_Style;
1378 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001379 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001381 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001382#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 }
1384
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001385 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001387 }
reed@google.com72cf4922011-01-04 19:58:20 +00001388
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001389 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 rec->fFrameWidth = strokeWidth;
1391 rec->fMiterLimit = paint.getStrokeMiter();
1392 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
caryclarkd7ea92f2016-03-16 07:34:02 -07001393 rec->fStrokeCap = SkToU8(paint.getStrokeCap());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001395 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001397 }
1398 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 rec->fFrameWidth = 0;
1400 rec->fMiterLimit = 0;
1401 rec->fStrokeJoin = 0;
caryclarkd7ea92f2016-03-16 07:34:02 -07001402 rec->fStrokeCap = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 }
1404
joshualitt20dac882015-07-24 13:16:24 -07001405 rec->fMaskFormat = SkToU8(compute_mask_format(paint));
reed@google.com02b53312011-05-18 19:00:53 +00001406
reedd54d3fc2014-11-13 14:39:58 -08001407 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001408 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001409 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001410 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001411 } else {
robertphillipsfcf78292015-06-19 11:49:52 -07001412 SkPixelGeometry geometry = surfaceProps
1413 ? surfaceProps->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001414 : SkSurfacePropsDefaultPixelGeometry();
1415 switch (geometry) {
1416 case kUnknown_SkPixelGeometry:
1417 // eeek, can't support LCD
1418 rec->fMaskFormat = SkMask::kA8_Format;
1419 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1420 break;
1421 case kRGB_H_SkPixelGeometry:
1422 // our default, do nothing.
1423 break;
1424 case kBGR_H_SkPixelGeometry:
1425 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1426 break;
1427 case kRGB_V_SkPixelGeometry:
1428 flags |= SkScalerContext::kLCD_Vertical_Flag;
1429 break;
1430 case kBGR_V_SkPixelGeometry:
1431 flags |= SkScalerContext::kLCD_Vertical_Flag;
1432 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1433 break;
reed@google.com02b53312011-05-18 19:00:53 +00001434 }
1435 }
1436 }
1437
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001438 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001439 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001440 }
1441 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001442 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001443 }
1444 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001445 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001446 }
reed@google.com830a23e2011-11-10 15:20:49 +00001447 if (paint.isVerticalText()) {
1448 flags |= SkScalerContext::kVertical_Flag;
1449 }
reed@google.com8351aab2012-01-18 17:06:35 +00001450 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1451 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1452 }
reed@google.com02b53312011-05-18 19:00:53 +00001453 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001454
reed@google.comffe49f52011-11-22 19:42:41 +00001455 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001456 rec->setHinting(computeHinting(paint));
1457
joshualitt9e36c1a2015-04-14 12:17:27 -07001458 rec->setLuminanceColor(paint.computeLuminanceColor());
bungeman@google.com532470f2013-01-22 19:25:14 +00001459
robertphillips9fc82752015-06-19 04:46:45 -07001460 //For now always set the paint gamma equal to the device gamma.
1461 //The math in SkMaskGamma can handle them being different,
1462 //but it requires superluminous masks when
1463 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1464 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1465 rec->setPaintGamma(SK_GAMMA_EXPONENT);
bungeman@google.com532470f2013-01-22 19:25:14 +00001466
1467#ifdef SK_GAMMA_CONTRAST
1468 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001469#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001470 /**
1471 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1472 * With lower values small text appears washed out (though correctly so).
1473 * With higher values lcd fringing is worse and the smoothing effect of
1474 * partial coverage is diminished.
1475 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001476 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001477#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001478
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001479 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001480
reed@android.com36a4c2a2009-07-22 19:52:11 +00001481 /* Allow the fonthost to modify our rec before we use it as a key into the
1482 cache. This way if we're asking for something that they will ignore,
1483 they can modify our rec up front, so we don't create duplicate cache
1484 entries.
1485 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001486 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001487
reed@google.com10d2d4d2012-03-01 22:32:51 +00001488 // be sure to call PostMakeRec(rec) before you actually use it!
1489}
1490
1491/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001492 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1493 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1494 * to hold it until the returned pointer is refed or forgotten.
1495 */
reed086eea92016-05-04 17:12:46 -07001496SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
bungeman@google.com97efada2012-07-30 20:40:50 +00001497
halcanary96fcdcc2015-08-27 07:41:13 -07001498static SkMaskGamma* gLinearMaskGamma = nullptr;
1499static SkMaskGamma* gMaskGamma = nullptr;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001500static SkScalar gContrast = SK_ScalarMin;
1501static SkScalar gPaintGamma = SK_ScalarMin;
1502static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001503/**
1504 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1505 * the returned SkMaskGamma pointer is refed or forgotten.
1506 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001507static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001508 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001509 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
halcanary96fcdcc2015-08-27 07:41:13 -07001510 if (nullptr == gLinearMaskGamma) {
halcanary385fe4d2015-08-26 13:07:48 -07001511 gLinearMaskGamma = new SkMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001512 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001513 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001514 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001515 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1516 SkSafeUnref(gMaskGamma);
halcanary385fe4d2015-08-26 13:07:48 -07001517 gMaskGamma = new SkMaskGamma(contrast, paintGamma, deviceGamma);
bungeman@google.com97efada2012-07-30 20:40:50 +00001518 gContrast = contrast;
1519 gPaintGamma = paintGamma;
1520 gDeviceGamma = deviceGamma;
1521 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001522 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001523}
1524
1525/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001526 * We ensure that the rec is self-consistent and efficient (where possible)
1527 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001528void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001529 /**
1530 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001531 * limits the number of unique entries, and the scaler will only look at
1532 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001533 */
reed@google.comdd43df92012-02-15 14:50:29 +00001534 switch (rec->fMaskFormat) {
reedd54d3fc2014-11-13 14:39:58 -08001535 case SkMask::kLCD16_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001536 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001537 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001538 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001539 break;
1540 }
1541 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001542 // filter down the luminance to a single component, since A8 can't
1543 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001544 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001545 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1546 SkColorGetG(color),
1547 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001548 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001549 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001550 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001551 break;
1552 }
1553 case SkMask::kBW_Format:
brianosmana1e8f8d2016-04-08 06:47:54 -07001554 // No need to differentiate gamma or apply contrast if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001555 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001556 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001557 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558}
1559
1560#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1561
reed@google.com17fb3872011-05-04 14:31:07 +00001562#ifdef SK_DEBUG
1563 #define TEST_DESC
1564#endif
1565
joshualittfd450792015-03-13 08:38:43 -07001566static void write_out_descriptor(SkDescriptor* desc, const SkScalerContext::Rec& rec,
brianosmanfad98562016-05-04 11:06:28 -07001567 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1568 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1569 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer,
joshualittfd450792015-03-13 08:38:43 -07001570 size_t descSize) {
1571 desc->init();
1572 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1573
1574 if (pe) {
1575 add_flattenable(desc, kPathEffect_SkDescriptorTag, peBuffer);
1576 }
1577 if (mf) {
1578 add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfBuffer);
1579 }
1580 if (ra) {
1581 add_flattenable(desc, kRasterizer_SkDescriptorTag, raBuffer);
1582 }
1583
1584 desc->computeChecksum();
1585}
1586
1587static size_t fill_out_rec(const SkPaint& paint, SkScalerContext::Rec* rec,
robertphillipsfcf78292015-06-19 11:49:52 -07001588 const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001589 bool fakeGamma, bool boostContrast,
bungemanf6d1e602016-02-22 13:20:28 -08001590 const SkMatrix* deviceMatrix,
brianosmanfad98562016-05-04 11:06:28 -07001591 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1592 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1593 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer) {
robertphillipsfcf78292015-06-19 11:49:52 -07001594 SkScalerContext::MakeRec(paint, surfaceProps, deviceMatrix, rec);
bungemanf6d1e602016-02-22 13:20:28 -08001595 if (!fakeGamma) {
brianosmana1e8f8d2016-04-08 06:47:54 -07001596 rec->ignoreGamma();
1597 }
1598 if (!boostContrast) {
1599 rec->setContrast(0);
joshualittfd450792015-03-13 08:38:43 -07001600 }
1601
1602 int entryCount = 1;
1603 size_t descSize = sizeof(*rec);
1604
1605 if (pe) {
reed80f5ea02016-04-27 13:49:32 -07001606 pe->flatten(*peBuffer);
joshualittfd450792015-03-13 08:38:43 -07001607 descSize += peBuffer->bytesWritten();
1608 entryCount += 1;
1609 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1610 // seems like we could support kLCD as well at this point...
1611 }
1612 if (mf) {
reed80f5ea02016-04-27 13:49:32 -07001613 mf->flatten(*mfBuffer);
joshualittfd450792015-03-13 08:38:43 -07001614 descSize += mfBuffer->bytesWritten();
1615 entryCount += 1;
1616 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1617 /* Pre-blend is not currently applied to filtered text.
1618 The primary filter is blur, for which contrast makes no sense,
1619 and for which the destination guess error is more visible.
1620 Also, all existing users of blur have calibrated for linear. */
1621 rec->ignorePreBlend();
1622 }
1623 if (ra) {
reed80f5ea02016-04-27 13:49:32 -07001624 ra->flatten(*raBuffer);
joshualittfd450792015-03-13 08:38:43 -07001625 descSize += raBuffer->bytesWritten();
1626 entryCount += 1;
1627 rec->fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1628 }
1629
1630 ///////////////////////////////////////////////////////////////////////////
1631 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
1632 SkScalerContext::PostMakeRec(paint, rec);
1633
1634 descSize += SkDescriptor::ComputeOverhead(entryCount);
1635 return descSize;
1636}
1637
1638#ifdef TEST_DESC
1639static void test_desc(const SkScalerContext::Rec& rec,
brianosmanfad98562016-05-04 11:06:28 -07001640 const SkPathEffect* pe, SkBinaryWriteBuffer* peBuffer,
1641 const SkMaskFilter* mf, SkBinaryWriteBuffer* mfBuffer,
1642 const SkRasterizer* ra, SkBinaryWriteBuffer* raBuffer,
joshualittfd450792015-03-13 08:38:43 -07001643 const SkDescriptor* desc, size_t descSize) {
1644 // Check that we completely write the bytes in desc (our key), and that
1645 // there are no uninitialized bytes. If there were, then we would get
1646 // false-misses (or worse, false-hits) in our fontcache.
1647 //
1648 // We do this buy filling 2 others, one with 0s and the other with 1s
1649 // and create those, and then check that all 3 are identical.
1650 SkAutoDescriptor ad1(descSize);
1651 SkAutoDescriptor ad2(descSize);
1652 SkDescriptor* desc1 = ad1.getDesc();
1653 SkDescriptor* desc2 = ad2.getDesc();
1654
1655 memset(desc1, 0x00, descSize);
1656 memset(desc2, 0xFF, descSize);
1657
1658 desc1->init();
1659 desc2->init();
1660 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1661 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1662
1663 if (pe) {
1664 add_flattenable(desc1, kPathEffect_SkDescriptorTag, peBuffer);
1665 add_flattenable(desc2, kPathEffect_SkDescriptorTag, peBuffer);
1666 }
1667 if (mf) {
1668 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, mfBuffer);
1669 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, mfBuffer);
1670 }
1671 if (ra) {
1672 add_flattenable(desc1, kRasterizer_SkDescriptorTag, raBuffer);
1673 add_flattenable(desc2, kRasterizer_SkDescriptorTag, raBuffer);
1674 }
1675
1676 SkASSERT(descSize == desc1->getLength());
1677 SkASSERT(descSize == desc2->getLength());
1678 desc1->computeChecksum();
1679 desc2->computeChecksum();
1680 SkASSERT(!memcmp(desc, desc1, descSize));
1681 SkASSERT(!memcmp(desc, desc2, descSize));
1682}
1683#endif
1684
1685/* see the note on ignoreGamma on descriptorProc */
reeda9322c22016-04-12 06:47:05 -07001686void SkPaint::getScalerContextDescriptor(SkScalerContextEffects* effects,
1687 SkAutoDescriptor* ad,
robertphillipsfcf78292015-06-19 11:49:52 -07001688 const SkSurfaceProps& surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001689 uint32_t scalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -08001690 const SkMatrix* deviceMatrix) const {
joshualittfd450792015-03-13 08:38:43 -07001691 SkScalerContext::Rec rec;
1692
1693 SkPathEffect* pe = this->getPathEffect();
1694 SkMaskFilter* mf = this->getMaskFilter();
1695 SkRasterizer* ra = this->getRasterizer();
1696
brianosmanfad98562016-05-04 11:06:28 -07001697 SkBinaryWriteBuffer peBuffer, mfBuffer, raBuffer;
brianosmana1e8f8d2016-04-08 06:47:54 -07001698 size_t descSize = fill_out_rec(*this, &rec, &surfaceProps,
1699 SkToBool(scalerContextFlags & kFakeGamma_ScalerContextFlag),
1700 SkToBool(scalerContextFlags & kBoostContrast_ScalerContextFlag),
1701 deviceMatrix, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
joshualittfd450792015-03-13 08:38:43 -07001702
joshualitt2b6acb42015-04-01 11:30:27 -07001703 ad->reset(descSize);
1704 SkDescriptor* desc = ad->getDesc();
joshualittfd450792015-03-13 08:38:43 -07001705
1706 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
1707
1708 SkASSERT(descSize == desc->getLength());
1709
1710#ifdef TEST_DESC
1711 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
1712#endif
reeda9322c22016-04-12 06:47:05 -07001713
1714 effects->fPathEffect = pe;
1715 effects->fMaskFilter = mf;
1716 effects->fRasterizer = ra;
joshualittfd450792015-03-13 08:38:43 -07001717}
1718
reed@google.comffe49f52011-11-22 19:42:41 +00001719/*
1720 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001721 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1722 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001723 */
robertphillipsfcf78292015-06-19 11:49:52 -07001724void SkPaint::descriptorProc(const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001725 uint32_t scalerContextFlags,
bungeman@google.com532470f2013-01-22 19:25:14 +00001726 const SkMatrix* deviceMatrix,
reeda9322c22016-04-12 06:47:05 -07001727 void (*proc)(SkTypeface*, const SkScalerContextEffects&,
1728 const SkDescriptor*, void*),
bungemanf6d1e602016-02-22 13:20:28 -08001729 void* context) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 SkScalerContext::Rec rec;
1731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 SkPathEffect* pe = this->getPathEffect();
1733 SkMaskFilter* mf = this->getMaskFilter();
1734 SkRasterizer* ra = this->getRasterizer();
1735
brianosmanfad98562016-05-04 11:06:28 -07001736 SkBinaryWriteBuffer peBuffer, mfBuffer, raBuffer;
brianosmana1e8f8d2016-04-08 06:47:54 -07001737 size_t descSize = fill_out_rec(*this, &rec, surfaceProps,
1738 SkToBool(scalerContextFlags & kFakeGamma_ScalerContextFlag),
1739 SkToBool(scalerContextFlags & kBoostContrast_ScalerContextFlag),
1740 deviceMatrix, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741
1742 SkAutoDescriptor ad(descSize);
1743 SkDescriptor* desc = ad.getDesc();
1744
joshualittfd450792015-03-13 08:38:43 -07001745 write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
1747 SkASSERT(descSize == desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748
reed@google.com17fb3872011-05-04 14:31:07 +00001749#ifdef TEST_DESC
joshualittfd450792015-03-13 08:38:43 -07001750 test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
reed@google.com17fb3872011-05-04 14:31:07 +00001751#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001752
reeda9322c22016-04-12 06:47:05 -07001753 proc(fTypeface.get(), { pe, mf, ra }, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754}
1755
robertphillipsfcf78292015-06-19 11:49:52 -07001756SkGlyphCache* SkPaint::detachCache(const SkSurfaceProps* surfaceProps,
brianosmana1e8f8d2016-04-08 06:47:54 -07001757 uint32_t scalerContextFlags,
bungemanf6d1e602016-02-22 13:20:28 -08001758 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 SkGlyphCache* cache;
brianosmana1e8f8d2016-04-08 06:47:54 -07001760 this->descriptorProc(surfaceProps, scalerContextFlags, deviceMatrix, DetachDescProc, &cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 return cache;
1762}
1763
bungeman@google.com97efada2012-07-30 20:40:50 +00001764/**
1765 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1766 */
1767//static
1768SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1769 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001770 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1771 rec.getPaintGamma(),
1772 rec.getDeviceGamma());
1773 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001774}
1775
jvanverth2d2a68c2014-06-10 06:42:56 -07001776size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1777 SkScalar deviceGamma, int* width, int* height) {
1778 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1779 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1780 paintGamma,
1781 deviceGamma);
1782
1783 maskGamma.getGammaTableDimensions(width, height);
1784 size_t size = (*width)*(*height)*sizeof(uint8_t);
1785
1786 return size;
1787}
1788
1789void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1790 void* data) {
1791 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1792 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1793 paintGamma,
1794 deviceGamma);
1795 int width, height;
1796 maskGamma.getGammaTableDimensions(&width, &height);
1797 size_t size = width*height*sizeof(uint8_t);
1798 const uint8_t* gammaTables = maskGamma.getGammaTables();
1799 memcpy(data, gammaTables, size);
1800}
1801
1802
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803///////////////////////////////////////////////////////////////////////////////
1804
1805#include "SkStream.h"
1806
reed@android.comaefd2bc2009-03-30 21:02:14 +00001807static uintptr_t asint(const void* p) {
1808 return reinterpret_cast<uintptr_t>(p);
1809}
1810
reed@android.comaefd2bc2009-03-30 21:02:14 +00001811static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1812 SkASSERT(a == (uint8_t)a);
1813 SkASSERT(b == (uint8_t)b);
1814 SkASSERT(c == (uint8_t)c);
1815 SkASSERT(d == (uint8_t)d);
1816 return (a << 24) | (b << 16) | (c << 8) | d;
1817}
1818
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001819#ifdef SK_DEBUG
1820 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1821 SkASSERT(bitCount > 0 && bitCount <= 32);
1822 uint32_t mask = ~0U;
1823 mask >>= (32 - bitCount);
1824 SkASSERT(0 == (value & ~mask));
1825 }
1826#else
1827 #define ASSERT_FITS_IN(value, bitcount)
1828#endif
1829
reed@android.comaefd2bc2009-03-30 21:02:14 +00001830enum FlatFlags {
mtklein88fd0fb2014-12-01 06:56:38 -08001831 kHasTypeface_FlatFlag = 0x1,
1832 kHasEffects_FlatFlag = 0x2,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001833
mtklein88fd0fb2014-12-01 06:56:38 -08001834 kFlatFlagMask = 0x3,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001835};
1836
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001837enum BitsPerField {
1838 kFlags_BPF = 16,
1839 kHint_BPF = 2,
1840 kAlign_BPF = 2,
1841 kFilter_BPF = 2,
1842 kFlatFlags_BPF = 3,
1843};
1844
1845static inline int BPF_Mask(int bits) {
1846 return (1 << bits) - 1;
1847}
1848
1849static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1850 unsigned filter, unsigned flatFlags) {
1851 ASSERT_FITS_IN(flags, kFlags_BPF);
1852 ASSERT_FITS_IN(hint, kHint_BPF);
1853 ASSERT_FITS_IN(align, kAlign_BPF);
1854 ASSERT_FITS_IN(filter, kFilter_BPF);
1855 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1856
1857 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1858 // add more bits in the future.
1859 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1860}
1861
1862static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1863 paint->setFlags(packed >> 16);
1864 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1865 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
reed93a12152015-03-16 10:08:34 -07001866 paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001867 return (FlatFlags)(packed & kFlatFlagMask);
1868}
1869
reed@android.comaefd2bc2009-03-30 21:02:14 +00001870/* To save space/time, we analyze the paint, and write a truncated version of
1871 it if there are not tricky elements like shaders, etc.
1872 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001873void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001874 uint8_t flatFlags = 0;
1875 if (this->getTypeface()) {
1876 flatFlags |= kHasTypeface_FlatFlag;
1877 }
1878 if (asint(this->getPathEffect()) |
1879 asint(this->getShader()) |
reed@android.comaefd2bc2009-03-30 21:02:14 +00001880 asint(this->getMaskFilter()) |
1881 asint(this->getColorFilter()) |
1882 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001883 asint(this->getLooper()) |
1884 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001885 flatFlags |= kHasEffects_FlatFlag;
1886 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001887
mtklein1b5dd882016-04-29 08:46:41 -07001888 buffer.writeScalar(this->getTextSize());
1889 buffer.writeScalar(this->getTextScaleX());
1890 buffer.writeScalar(this->getTextSkewX());
1891 buffer.writeScalar(this->getStrokeWidth());
1892 buffer.writeScalar(this->getStrokeMiter());
1893 buffer.writeColor(this->getColor());
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001894
mtklein1b5dd882016-04-29 08:46:41 -07001895 buffer.writeUInt(pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
1896 this->getFilterQuality(), flatFlags));
1897 buffer.writeUInt(pack_4(this->getStrokeCap(), this->getStrokeJoin(),
reed374772b2016-10-05 17:33:02 -07001898 (this->getStyle() << 4) | this->getTextEncoding(),
1899 fBlendMode));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001900
1901 // now we're done with ptr and the (pre)reserved space. If we need to write
1902 // additional fields, use the buffer directly
1903 if (flatFlags & kHasTypeface_FlatFlag) {
1904 buffer.writeTypeface(this->getTypeface());
1905 }
1906 if (flatFlags & kHasEffects_FlatFlag) {
1907 buffer.writeFlattenable(this->getPathEffect());
1908 buffer.writeFlattenable(this->getShader());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001909 buffer.writeFlattenable(this->getMaskFilter());
1910 buffer.writeFlattenable(this->getColorFilter());
1911 buffer.writeFlattenable(this->getRasterizer());
1912 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001913 buffer.writeFlattenable(this->getImageFilter());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001914 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915}
1916
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001917void SkPaint::unflatten(SkReadBuffer& buffer) {
mtklein1b5dd882016-04-29 08:46:41 -07001918 this->setTextSize(buffer.readScalar());
1919 this->setTextScaleX(buffer.readScalar());
1920 this->setTextSkewX(buffer.readScalar());
1921 this->setStrokeWidth(buffer.readScalar());
1922 this->setStrokeMiter(buffer.readScalar());
1923 this->setColor(buffer.readColor());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001924
mtklein1b5dd882016-04-29 08:46:41 -07001925 unsigned flatFlags = unpack_paint_flags(this, buffer.readUInt());
bungeman@google.com24babf42011-11-07 16:33:40 +00001926
mtklein1b5dd882016-04-29 08:46:41 -07001927 uint32_t tmp = buffer.readUInt();
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001928 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1929 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
reed374772b2016-10-05 17:33:02 -07001930 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) {
1931 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1932 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
1933 } else {
1934 this->setStyle(static_cast<Style>((tmp >> 12) & 0xF));
1935 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 8) & 0xF));
1936 this->setBlendMode((SkBlendMode)(tmp & 0xFF));
1937 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001938
1939 if (flatFlags & kHasTypeface_FlatFlag) {
reed2867e762016-08-29 06:57:28 -07001940 this->setTypeface(buffer.readTypeface());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001941 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001942 this->setTypeface(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001943 }
1944
1945 if (flatFlags & kHasEffects_FlatFlag) {
reeda4393342016-03-18 11:22:57 -07001946 this->setPathEffect(buffer.readPathEffect());
reed8a21c9f2016-03-08 18:50:00 -08001947 this->setShader(buffer.readShader());
reed374772b2016-10-05 17:33:02 -07001948 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) {
1949 sk_sp<SkXfermode> xfer = buffer.readXfermode();
1950 this->setBlendMode(xfer ? xfer->blend() : SkBlendMode::kSrcOver);
1951 }
reed60c9b582016-04-03 09:11:13 -07001952 this->setMaskFilter(buffer.readMaskFilter());
reedd053ce92016-03-22 10:17:23 -07001953 this->setColorFilter(buffer.readColorFilter());
reed7b380d02016-03-21 13:25:16 -07001954 this->setRasterizer(buffer.readRasterizer());
1955 this->setLooper(buffer.readDrawLooper());
reed60c9b582016-04-03 09:11:13 -07001956 this->setImageFilter(buffer.readImageFilter());
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00001957
reedf70b5312016-03-04 16:36:20 -08001958 if (buffer.isVersionLT(SkReadBuffer::kAnnotationsMovedToCanvas_Version)) {
1959 // We used to store annotations here (string+skdata) if this bool was true
1960 if (buffer.readBool()) {
1961 // Annotations have moved to drawAnnotation, so we just drop this one on the floor.
1962 SkString key;
1963 buffer.readString(&key);
reedfde05112016-03-11 13:02:28 -08001964 (void)buffer.readByteArrayAsData();
reedf70b5312016-03-04 16:36:20 -08001965 }
reed@google.com0cd2ac62013-10-14 20:02:44 +00001966 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001967 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001968 this->setPathEffect(nullptr);
1969 this->setShader(nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -07001970 this->setMaskFilter(nullptr);
1971 this->setColorFilter(nullptr);
1972 this->setRasterizer(nullptr);
1973 this->setLooper(nullptr);
1974 this->setImageFilter(nullptr);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001975 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976}
1977
1978///////////////////////////////////////////////////////////////////////////////
1979
reed05d90442015-02-12 13:35:52 -08001980bool SkPaint::getFillPath(const SkPath& src, SkPath* dst, const SkRect* cullRect,
1981 SkScalar resScale) const {
1982 SkStrokeRec rec(*this, resScale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983
reed@google.comfd4be262012-05-25 01:04:12 +00001984 const SkPath* srcPtr = &src;
1985 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00001986
reed@google.com4bbdeac2013-01-24 21:03:11 +00001987 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00001988 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001989 }
1990
reed@google.comfd4be262012-05-25 01:04:12 +00001991 if (!rec.applyToPath(dst, *srcPtr)) {
1992 if (srcPtr == &tmpPath) {
1993 // If path's were copy-on-write, this trick would not be needed.
1994 // As it is, we want to save making a deep-copy from tmpPath -> dst
1995 // since we know we're just going to delete tmpPath when we return,
1996 // so the swap saves that copy.
1997 dst->swap(tmpPath);
1998 } else {
1999 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000 }
2001 }
reed@google.comfd4be262012-05-25 01:04:12 +00002002 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003}
2004
senorblanco0abdf762015-08-20 11:10:41 -07002005bool SkPaint::canComputeFastBounds() const {
2006 if (this->getLooper()) {
2007 return this->getLooper()->canComputeFastBounds(*this);
2008 }
2009 if (this->getImageFilter() && !this->getImageFilter()->canComputeFastBounds()) {
2010 return false;
2011 }
2012 return !this->getRasterizer();
2013}
2014
reed@google.come4f10a72012-05-15 20:47:50 +00002015const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002016 SkRect* storage,
2017 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002018 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002019
reed@google.come4f10a72012-05-15 20:47:50 +00002020 const SkRect* src = &origSrc;
2021
reed@google.com9efd9a02012-01-30 15:41:43 +00002022 if (this->getLooper()) {
2023 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002024 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002025 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002027
reed@google.come4f10a72012-05-15 20:47:50 +00002028 SkRect tmpSrc;
2029 if (this->getPathEffect()) {
2030 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2031 src = &tmpSrc;
2032 }
2033
bsalomon56686482016-04-29 07:07:03 -07002034 SkScalar radius = SkStrokeRec::GetInflationRadius(*this, style);
2035 *storage = src->makeOutset(radius, radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002036
reed@google.com9efd9a02012-01-30 15:41:43 +00002037 if (this->getMaskFilter()) {
2038 this->getMaskFilter()->computeFastBounds(*storage, storage);
2039 }
2040
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002041 if (this->getImageFilter()) {
senorblancoe5e79842016-03-21 14:51:59 -07002042 *storage = this->getImageFilter()->computeFastBounds(*storage);
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002043 }
2044
reed@android.comd252db02009-04-01 18:31:44 +00002045 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046}
2047
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002048#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002049
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002050void SkPaint::toString(SkString* str) const {
2051 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2052
2053 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002054 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002055 SkDynamicMemoryWStream ostream;
2056 typeface->serialize(&ostream);
Ben Wagner145dbcd2016-11-03 14:40:50 -04002057 std::unique_ptr<SkStreamAsset> istream(ostream.detachAsStream());
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002058
robertphillips3552ba12016-02-25 10:58:49 -08002059 SkFontDescriptor descriptor;
Ben Wagner145dbcd2016-11-03 14:40:50 -04002060 if (!SkFontDescriptor::Deserialize(istream.get(), &descriptor)) {
robertphillips3552ba12016-02-25 10:58:49 -08002061 str->append("<dt>FontDescriptor deserialization failed</dt>");
2062 } else {
2063 str->append("<dt>Font Family Name:</dt><dd>");
2064 str->append(descriptor.getFamilyName());
2065 str->append("</dd><dt>Font Full Name:</dt><dd>");
2066 str->append(descriptor.getFullName());
2067 str->append("</dd><dt>Font PS Name:</dt><dd>");
2068 str->append(descriptor.getPostscriptName());
2069 str->append("</dd>");
2070 }
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002071 }
2072
2073 str->append("<dt>TextSize:</dt><dd>");
2074 str->appendScalar(this->getTextSize());
2075 str->append("</dd>");
2076
2077 str->append("<dt>TextScaleX:</dt><dd>");
2078 str->appendScalar(this->getTextScaleX());
2079 str->append("</dd>");
2080
2081 str->append("<dt>TextSkewX:</dt><dd>");
2082 str->appendScalar(this->getTextSkewX());
2083 str->append("</dd>");
2084
2085 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002086 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002087 str->append("<dt>PathEffect:</dt><dd>");
robertphillips42dbfa82015-01-26 06:08:52 -08002088 pathEffect->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002089 str->append("</dd>");
2090 }
2091
2092 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002093 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002094 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002095 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002096 str->append("</dd>");
2097 }
2098
reed374772b2016-10-05 17:33:02 -07002099 if (!this->isSrcOver()) {
2100 str->appendf("<dt>Xfermode:</dt><dd>%d</dd>", fBlendMode);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002101 }
2102
2103 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002104 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002105 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002106 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002107 str->append("</dd>");
2108 }
2109
2110 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002111 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002112 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002113 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002114 str->append("</dd>");
2115 }
2116
2117 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002118 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002119 str->append("<dt>Rasterizer:</dt><dd>");
2120 str->append("</dd>");
2121 }
2122
2123 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002124 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002125 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002126 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002127 str->append("</dd>");
2128 }
2129
2130 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002131 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002132 str->append("<dt>ImageFilter:</dt><dd>");
robertphillipsf3f5bad2014-12-19 13:49:15 -08002133 imageFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002134 str->append("</dd>");
2135 }
2136
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002137 str->append("<dt>Color:</dt><dd>0x");
2138 SkColor color = this->getColor();
2139 str->appendHex(color);
2140 str->append("</dd>");
2141
2142 str->append("<dt>Stroke Width:</dt><dd>");
2143 str->appendScalar(this->getStrokeWidth());
2144 str->append("</dd>");
2145
2146 str->append("<dt>Stroke Miter:</dt><dd>");
2147 str->appendScalar(this->getStrokeMiter());
2148 str->append("</dd>");
2149
2150 str->append("<dt>Flags:</dt><dd>(");
2151 if (this->getFlags()) {
2152 bool needSeparator = false;
2153 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002154 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2155 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2156 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2157 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2158 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2159 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2160 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2161 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2162 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2163 "EmbeddedBitmapText", &needSeparator);
2164 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2165 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2166 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2167 "GenA8FromLCD", &needSeparator);
2168 } else {
2169 str->append("None");
2170 }
2171 str->append(")</dd>");
2172
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002173 str->append("<dt>FilterLevel:</dt><dd>");
reed93a12152015-03-16 10:08:34 -07002174 static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High" };
2175 str->append(gFilterQualityStrings[this->getFilterQuality()]);
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002176 str->append("</dd>");
2177
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002178 str->append("<dt>TextAlign:</dt><dd>");
2179 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2180 str->append(gTextAlignStrings[this->getTextAlign()]);
2181 str->append("</dd>");
2182
2183 str->append("<dt>CapType:</dt><dd>");
2184 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2185 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2186 str->append("</dd>");
2187
2188 str->append("<dt>JoinType:</dt><dd>");
2189 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2190 str->append(gJoinStrings[this->getStrokeJoin()]);
2191 str->append("</dd>");
2192
2193 str->append("<dt>Style:</dt><dd>");
2194 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2195 str->append(gStyleStrings[this->getStyle()]);
2196 str->append("</dd>");
2197
2198 str->append("<dt>TextEncoding:</dt><dd>");
2199 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2200 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2201 str->append("</dd>");
2202
2203 str->append("<dt>Hinting:</dt><dd>");
2204 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2205 str->append(gHintingStrings[this->getHinting()]);
2206 str->append("</dd>");
2207
2208 str->append("</dd></dl></dl>");
2209}
2210#endif
2211
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002212///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002214static bool has_thick_frame(const SkPaint& paint) {
2215 return paint.getStrokeWidth() > 0 &&
2216 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217}
2218
caryclark0449bcf2016-02-09 13:25:45 -08002219SkTextBaseIter::SkTextBaseIter(const char text[], size_t length,
robertphillipsfcf78292015-06-19 11:49:52 -07002220 const SkPaint& paint,
2221 bool applyStrokeAndPathEffects)
2222 : fPaint(paint) {
robertphillipse34f17d2016-07-19 07:59:22 -07002223 fGlyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
2224 paint.isDevKernText(),
2225 true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226
djsollen@google.com166e6532012-03-20 14:24:38 +00002227 fPaint.setLinearText(true);
halcanary96fcdcc2015-08-27 07:41:13 -07002228 fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229
halcanary96fcdcc2015-08-27 07:41:13 -07002230 if (fPaint.getPathEffect() == nullptr && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002232 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233
djsollen@google.com166e6532012-03-20 14:24:38 +00002234 // can't use our canonical size if we need to apply patheffects
halcanary96fcdcc2015-08-27 07:41:13 -07002235 if (fPaint.getPathEffect() == nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002236 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2237 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002238 if (has_thick_frame(fPaint)) {
reed80ea19c2015-05-12 10:37:34 -07002239 fPaint.setStrokeWidth(fPaint.getStrokeWidth() / fScale);
djsollen@google.com166e6532012-03-20 14:24:38 +00002240 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002241 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002243 }
reed@google.com72cf4922011-01-04 19:58:20 +00002244
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002245 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 fPaint.setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -07002247 fPaint.setPathEffect(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 }
2249
brianosmana1e8f8d2016-04-08 06:47:54 -07002250 // SRGBTODO: Is this correct?
2251 fCache = fPaint.detachCache(nullptr, SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags,
2252 nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253
2254 SkPaint::Style style = SkPaint::kFill_Style;
reeda4393342016-03-18 11:22:57 -07002255 sk_sp<SkPathEffect> pe;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002257 if (!applyStrokeAndPathEffects) {
Mike Reed693fdbd2017-01-12 10:13:40 -05002258 style = paint.getStyle(); // restore
2259 pe = paint.refPathEffect(); // restore
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
2261 fPaint.setStyle(style);
2262 fPaint.setPathEffect(pe);
Mike Reed693fdbd2017-01-12 10:13:40 -05002263 fPaint.setMaskFilter(paint.refMaskFilter()); // restore
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264
2265 // now compute fXOffset if needed
2266
2267 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002268 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002270 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
halcanary96fcdcc2015-08-27 07:41:13 -07002271 &count, nullptr), fScale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002272 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 xOffset = -width;
2276 }
2277 fXPos = xOffset;
2278 fPrevAdvance = 0;
2279
2280 fText = text;
2281 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002282
reed@google.com44da42e2011-11-10 20:04:47 +00002283 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284}
2285
caryclark0449bcf2016-02-09 13:25:45 -08002286SkTextBaseIter::~SkTextBaseIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 SkGlyphCache::AttachCache(fCache);
2288}
2289
reed@google.com7b4531f2012-08-07 15:53:00 +00002290bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2291 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2293
benjaminwagner6b3eacb2016-03-24 19:07:58 -07002294 fXPos += SkScalarMul(fPrevAdvance + fAutoKern.adjust(glyph), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002295 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002297 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002298 if (path) {
2299 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002300 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002301 } else {
2302 if (path) {
halcanary96fcdcc2015-08-27 07:41:13 -07002303 *path = nullptr;
reed@google.com7b4531f2012-08-07 15:53:00 +00002304 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002306 if (xpos) {
2307 *xpos = fXPos;
2308 }
2309 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002311 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002313
caryclark0449bcf2016-02-09 13:25:45 -08002314bool SkTextInterceptsIter::next(SkScalar* array, int* count) {
2315 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
benjaminwagner6b3eacb2016-03-24 19:07:58 -07002316 fXPos += SkScalarMul(fPrevAdvance + fAutoKern.adjust(glyph), fScale);
caryclark0449bcf2016-02-09 13:25:45 -08002317 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
2318 if (fCache->findPath(glyph)) {
2319 fCache->findIntercepts(fBounds, fScale, fXPos, SkToBool(fXYIndex),
2320 const_cast<SkGlyph*>(&glyph), array, count);
2321 }
2322 return fText < fStop;
2323}
2324
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002325///////////////////////////////////////////////////////////////////////////////
2326
reedf539b8c2014-11-11 12:51:33 -08002327// return true if the filter exists, and may affect alpha
2328static bool affects_alpha(const SkColorFilter* cf) {
2329 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2330}
2331
2332// return true if the filter exists, and may affect alpha
2333static bool affects_alpha(const SkImageFilter* imf) {
2334 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2335 // ala colorfilters
halcanary96fcdcc2015-08-27 07:41:13 -07002336 return imf != nullptr;
reedf539b8c2014-11-11 12:51:33 -08002337}
2338
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002339bool SkPaint::nothingToDraw() const {
reed46f2d0a2016-09-11 05:40:31 -07002340 if (fDrawLooper) {
reed@google.com733e3022011-10-06 15:11:03 +00002341 return false;
2342 }
reed374772b2016-10-05 17:33:02 -07002343 switch ((SkBlendMode)fBlendMode) {
2344 case SkBlendMode::kSrcOver:
2345 case SkBlendMode::kSrcATop:
2346 case SkBlendMode::kDstOut:
2347 case SkBlendMode::kDstOver:
2348 case SkBlendMode::kPlus:
2349 if (0 == this->getAlpha()) {
2350 return !affects_alpha(fColorFilter.get()) && !affects_alpha(fImageFilter.get());
2351 }
2352 break;
2353 case SkBlendMode::kDst:
2354 return true;
2355 default:
2356 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002357 }
2358 return false;
2359}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002360
mtkleinfb1fe4f2014-10-07 09:26:10 -07002361uint32_t SkPaint::getHash() const {
2362 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2363 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
reed374772b2016-10-05 17:33:02 -07002364 static_assert(offsetof(SkPaint, fBitfields) == 8 * sizeof(void*) + 7 * sizeof(uint32_t),
bungeman99fe8222015-08-20 07:57:51 -07002365 "SkPaint_notPackedTightly");
mtklein4e976072016-08-08 09:06:27 -07002366 return SkOpts::hash(reinterpret_cast<const uint32_t*>(this),
2367 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
mtkleinfb1fe4f2014-10-07 09:26:10 -07002368}