blob: 4ccfce235acc228c1690545bbba9f56bbf186334 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkPaint.h"
reed@google.comb0a34d82012-07-11 19:57:55 +00009#include "SkAnnotation.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000010#include "SkAutoKern.h"
mtkleinfb1fe4f2014-10-07 09:26:10 -070011#include "SkChecksum.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000013#include "SkData.h"
bungeman@google.com532470f2013-01-22 19:25:14 +000014#include "SkDeviceProperties.h"
rmistryc4b84ae2014-06-23 06:59:15 -070015#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000016#include "SkFontDescriptor.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkFontHost.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000018#include "SkGlyphCache.h"
reed@google.com15356a62011-11-03 19:29:08 +000019#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000021#include "SkMaskGamma.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000022#include "SkReadBuffer.h"
23#include "SkWriteBuffer.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000024#include "SkPaintDefaults.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkPathEffect.h"
26#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000027#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000029#include "SkShader.h"
30#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000032#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000033#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000034#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000035#include "SkTypeface.h"
36#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000037
reed@google.coma3237872011-07-05 19:20:48 +000038// define this to get a printf for out-of-range parameter in setters
39// e.g. setTextSize(-1)
40//#define SK_REPORT_API_RANGE_CHECK
41
reed@android.coma3122b92009-08-13 20:38:25 +000042SkPaint::SkPaint() {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000043 fTypeface = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 fPathEffect = NULL;
45 fShader = NULL;
46 fXfermode = NULL;
47 fMaskFilter = NULL;
48 fColorFilter = NULL;
49 fRasterizer = NULL;
50 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000051 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000052 fAnnotation = NULL;
reed@android.coma3122b92009-08-13 20:38:25 +000053
reedf59eab22014-07-14 14:39:15 -070054 fTextSize = SkPaintDefaults_TextSize;
55 fTextScaleX = SK_Scalar1;
56 fTextSkewX = 0;
57 fColor = SK_ColorBLACK;
58 fWidth = 0;
59 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000060
61 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070062 fBitfieldsUInt = 0;
63 fBitfields.fFlags = SkPaintDefaults_Flags;
64 fBitfields.fCapType = kDefault_Cap;
65 fBitfields.fJoinType = kDefault_Join;
66 fBitfields.fTextAlign = kLeft_Align;
67 fBitfields.fStyle = kFill_Style;
68 fBitfields.fTextEncoding = kUTF8_TextEncoding;
69 fBitfields.fHinting = SkPaintDefaults_Hinting;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070}
71
reed@google.com6fb7e2e2011-02-08 22:22:52 +000072SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000073#define COPY(field) field = src.field
74#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000076 REF_COPY(fTypeface);
77 REF_COPY(fPathEffect);
78 REF_COPY(fShader);
79 REF_COPY(fXfermode);
80 REF_COPY(fMaskFilter);
81 REF_COPY(fColorFilter);
82 REF_COPY(fRasterizer);
83 REF_COPY(fLooper);
84 REF_COPY(fImageFilter);
85 REF_COPY(fAnnotation);
86
87 COPY(fTextSize);
88 COPY(fTextScaleX);
89 COPY(fTextSkewX);
90 COPY(fColor);
91 COPY(fWidth);
92 COPY(fMiterLimit);
93 COPY(fBitfields);
djsollen@google.com40078cb2013-05-24 20:31:57 +000094
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000095#undef COPY
96#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +000097}
98
reed@google.com6fb7e2e2011-02-08 22:22:52 +000099SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000100 SkSafeUnref(fTypeface);
101 SkSafeUnref(fPathEffect);
102 SkSafeUnref(fShader);
103 SkSafeUnref(fXfermode);
104 SkSafeUnref(fMaskFilter);
105 SkSafeUnref(fColorFilter);
106 SkSafeUnref(fRasterizer);
107 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000108 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000109 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110}
111
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000112SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000113 if (this == &src) {
114 return *this;
115 }
116
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000117#define COPY(field) field = src.field
118#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
119
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 SkASSERT(&src);
121
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000122 REF_COPY(fTypeface);
123 REF_COPY(fPathEffect);
124 REF_COPY(fShader);
125 REF_COPY(fXfermode);
126 REF_COPY(fMaskFilter);
127 REF_COPY(fColorFilter);
128 REF_COPY(fRasterizer);
129 REF_COPY(fLooper);
130 REF_COPY(fImageFilter);
131 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000133 COPY(fTextSize);
134 COPY(fTextScaleX);
135 COPY(fTextSkewX);
136 COPY(fColor);
137 COPY(fWidth);
138 COPY(fMiterLimit);
139 COPY(fBitfields);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
141 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000142
143#undef COPY
144#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145}
146
robertphillips@google.comb2657412013-08-07 22:36:29 +0000147bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000148#define EQUAL(field) (a.field == b.field)
mtklein610a0152014-09-25 11:57:53 -0700149 return EQUAL(fTypeface)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000150 && EQUAL(fPathEffect)
151 && EQUAL(fShader)
152 && EQUAL(fXfermode)
153 && EQUAL(fMaskFilter)
154 && EQUAL(fColorFilter)
155 && EQUAL(fRasterizer)
156 && EQUAL(fLooper)
157 && EQUAL(fImageFilter)
158 && EQUAL(fAnnotation)
159 && EQUAL(fTextSize)
160 && EQUAL(fTextScaleX)
161 && EQUAL(fTextSkewX)
162 && EQUAL(fColor)
163 && EQUAL(fWidth)
164 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700165 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000166 ;
167#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000168}
169
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000170void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 SkPaint init;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000173}
174
reed@google.comc9683152013-07-18 13:47:01 +0000175void SkPaint::setFilterLevel(FilterLevel level) {
reedf59eab22014-07-14 14:39:15 -0700176 fBitfields.fFilterLevel = level;
reed@google.comc9683152013-07-18 13:47:01 +0000177}
178
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000179void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700180 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181}
182
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000183void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700184 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185}
186
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000187void SkPaint::setAntiAlias(bool doAA) {
reedf59eab22014-07-14 14:39:15 -0700188 this->setFlags(SkSetClearMask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189}
190
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000191void SkPaint::setDither(bool doDither) {
reedf59eab22014-07-14 14:39:15 -0700192 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193}
194
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000195void SkPaint::setSubpixelText(bool doSubpixel) {
reedf59eab22014-07-14 14:39:15 -0700196 this->setFlags(SkSetClearMask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197}
198
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000199void SkPaint::setLCDRenderText(bool doLCDRender) {
reedf59eab22014-07-14 14:39:15 -0700200 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000201}
202
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000203void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reedf59eab22014-07-14 14:39:15 -0700204 this->setFlags(SkSetClearMask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000205}
206
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000207void SkPaint::setAutohinted(bool useAutohinter) {
reedf59eab22014-07-14 14:39:15 -0700208 this->setFlags(SkSetClearMask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000209}
210
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000211void SkPaint::setLinearText(bool doLinearText) {
reedf59eab22014-07-14 14:39:15 -0700212 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213}
214
reed@google.com830a23e2011-11-10 15:20:49 +0000215void SkPaint::setVerticalText(bool doVertical) {
reedf59eab22014-07-14 14:39:15 -0700216 this->setFlags(SkSetClearMask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000217}
218
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000219void SkPaint::setUnderlineText(bool doUnderline) {
reedf59eab22014-07-14 14:39:15 -0700220 this->setFlags(SkSetClearMask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221}
222
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000223void SkPaint::setStrikeThruText(bool doStrikeThru) {
reedf59eab22014-07-14 14:39:15 -0700224 this->setFlags(SkSetClearMask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225}
226
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000227void SkPaint::setFakeBoldText(bool doFakeBold) {
reedf59eab22014-07-14 14:39:15 -0700228 this->setFlags(SkSetClearMask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229}
230
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000231void SkPaint::setDevKernText(bool doDevKern) {
reedf59eab22014-07-14 14:39:15 -0700232 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233}
234
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000235void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) {
reedf59eab22014-07-14 14:39:15 -0700236 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag));
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000237}
238
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000239void SkPaint::setStyle(Style style) {
240 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700241 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000242 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000243#ifdef SK_REPORT_API_RANGE_CHECK
244 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
245#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247}
248
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000249void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 fColor = color;
251}
252
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000253void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000254 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
255 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256}
257
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000259 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setStrokeWidth(SkScalar width) {
263 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000265 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000266#ifdef SK_REPORT_API_RANGE_CHECK
267 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
268#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000269 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270}
271
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000272void SkPaint::setStrokeMiter(SkScalar limit) {
273 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000275 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000276#ifdef SK_REPORT_API_RANGE_CHECK
277 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
278#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000282void SkPaint::setStrokeCap(Cap ct) {
283 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700284 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000285 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000286#ifdef SK_REPORT_API_RANGE_CHECK
287 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
288#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290}
291
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000292void SkPaint::setStrokeJoin(Join jt) {
293 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700294 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000295 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000296#ifdef SK_REPORT_API_RANGE_CHECK
297 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
298#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300}
301
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000302///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000304void SkPaint::setTextAlign(Align align) {
305 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700306 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000307 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000308#ifdef SK_REPORT_API_RANGE_CHECK
309 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
310#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312}
313
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000314void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000315 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000317 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000318#ifdef SK_REPORT_API_RANGE_CHECK
319 SkDebugf("SkPaint::setTextSize() called with negative value\n");
320#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000321 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322}
323
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000324void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 fTextScaleX = scaleX;
326}
327
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000328void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 fTextSkewX = skewX;
330}
331
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000332void SkPaint::setTextEncoding(TextEncoding encoding) {
333 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700334 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000335 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000336#ifdef SK_REPORT_API_RANGE_CHECK
337 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
338#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340}
341
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000342///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 SkRefCnt_SafeAssign(fTypeface, font);
346 return font;
347}
348
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000349SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 SkRefCnt_SafeAssign(fRasterizer, r);
351 return r;
352}
353
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000354SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 SkRefCnt_SafeAssign(fLooper, looper);
356 return looper;
357}
358
reed@google.com15356a62011-11-03 19:29:08 +0000359SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
360 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
reed@google.com15356a62011-11-03 19:29:08 +0000361 return imageFilter;
362}
363
reed@google.comb0a34d82012-07-11 19:57:55 +0000364SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
365 SkRefCnt_SafeAssign(fAnnotation, annotation);
reed@google.comb0a34d82012-07-11 19:57:55 +0000366 return annotation;
367}
368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369///////////////////////////////////////////////////////////////////////////////
370
reed@google.comed43dff2013-06-04 16:56:27 +0000371static SkScalar mag2(SkScalar x, SkScalar y) {
372 return x * x + y * y;
373}
374
375static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
376 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
377 ||
378 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
379}
380
381bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
382 SkASSERT(!ctm.hasPerspective());
383 SkASSERT(!textM.hasPerspective());
384
385 SkMatrix matrix;
386 matrix.setConcat(ctm, textM);
387 return tooBig(matrix, MaxCacheSize2());
388}
389
reed@google.comed43dff2013-06-04 16:56:27 +0000390
391///////////////////////////////////////////////////////////////////////////////
392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393#include "SkGlyphCache.h"
394#include "SkUtils.h"
395
reed@google.com90808e82013-03-19 14:44:54 +0000396static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
397 void* context) {
398 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000399}
400
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
402 uint16_t glyphs[]) const {
403 if (byteLength == 0) {
404 return 0;
405 }
reed@google.com72cf4922011-01-04 19:58:20 +0000406
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 SkASSERT(textData != NULL);
408
409 if (NULL == glyphs) {
410 switch (this->getTextEncoding()) {
411 case kUTF8_TextEncoding:
412 return SkUTF8_CountUnichars((const char*)textData, byteLength);
413 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000414 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000415 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000416 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000418 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000420 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 }
422 return 0;
423 }
reed@google.com72cf4922011-01-04 19:58:20 +0000424
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000426
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 // handle this encoding before the setup for the glyphcache
428 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
429 // we want to ignore the low bit of byteLength
430 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000431 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 }
reed@google.com72cf4922011-01-04 19:58:20 +0000433
bungeman@google.com532470f2013-01-22 19:25:14 +0000434 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 SkGlyphCache* cache = autoCache.getCache();
436
437 const char* text = (const char*)textData;
438 const char* stop = text + byteLength;
439 uint16_t* gptr = glyphs;
440
441 switch (this->getTextEncoding()) {
442 case SkPaint::kUTF8_TextEncoding:
443 while (text < stop) {
444 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
445 }
446 break;
447 case SkPaint::kUTF16_TextEncoding: {
448 const uint16_t* text16 = (const uint16_t*)text;
449 const uint16_t* stop16 = (const uint16_t*)stop;
450 while (text16 < stop16) {
451 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
452 }
453 break;
454 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000455 case kUTF32_TextEncoding: {
456 const int32_t* text32 = (const int32_t*)text;
457 const int32_t* stop32 = (const int32_t*)stop;
458 while (text32 < stop32) {
459 *gptr++ = cache->unicharToGlyph(*text32++);
460 }
461 break;
462 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000464 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000466 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467}
468
reed@android.coma5dcaf62010-02-05 17:12:32 +0000469bool SkPaint::containsText(const void* textData, size_t byteLength) const {
470 if (0 == byteLength) {
471 return true;
472 }
reed@google.com72cf4922011-01-04 19:58:20 +0000473
reed@android.coma5dcaf62010-02-05 17:12:32 +0000474 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000475
reed@android.coma5dcaf62010-02-05 17:12:32 +0000476 // handle this encoding before the setup for the glyphcache
477 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
478 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
479 size_t count = byteLength >> 1;
480 for (size_t i = 0; i < count; i++) {
481 if (0 == glyphID[i]) {
482 return false;
483 }
484 }
485 return true;
486 }
reed@google.com72cf4922011-01-04 19:58:20 +0000487
bungeman@google.com532470f2013-01-22 19:25:14 +0000488 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000489 SkGlyphCache* cache = autoCache.getCache();
490
491 switch (this->getTextEncoding()) {
492 case SkPaint::kUTF8_TextEncoding: {
493 const char* text = static_cast<const char*>(textData);
494 const char* stop = text + byteLength;
495 while (text < stop) {
496 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
497 return false;
498 }
499 }
500 break;
501 }
502 case SkPaint::kUTF16_TextEncoding: {
503 const uint16_t* text = static_cast<const uint16_t*>(textData);
504 const uint16_t* stop = text + (byteLength >> 1);
505 while (text < stop) {
506 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
507 return false;
508 }
509 }
510 break;
511 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000512 case SkPaint::kUTF32_TextEncoding: {
513 const int32_t* text = static_cast<const int32_t*>(textData);
514 const int32_t* stop = text + (byteLength >> 2);
515 while (text < stop) {
516 if (0 == cache->unicharToGlyph(*text++)) {
517 return false;
518 }
519 }
520 break;
521 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000522 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000523 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000524 return false;
525 }
526 return true;
527}
528
reed@android.com9d3a9852010-01-08 14:07:42 +0000529void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
530 SkUnichar textData[]) const {
531 if (count <= 0) {
532 return;
533 }
534
535 SkASSERT(glyphs != NULL);
536 SkASSERT(textData != NULL);
537
bungeman@google.com532470f2013-01-22 19:25:14 +0000538 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000539 SkGlyphCache* cache = autoCache.getCache();
540
541 for (int index = 0; index < count; index++) {
542 textData[index] = cache->glyphToUnichar(glyphs[index]);
543 }
544}
545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546///////////////////////////////////////////////////////////////////////////////
547
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000548static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
549 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 SkASSERT(cache != NULL);
551 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
554}
555
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000556static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
557 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 SkASSERT(cache != NULL);
559 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
562}
563
reed@google.com68bc6f72012-03-14 19:41:55 +0000564static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
565 const char** text) {
566 SkASSERT(cache != NULL);
567 SkASSERT(text != NULL);
568
569 const int32_t* ptr = *(const int32_t**)text;
570 SkUnichar uni = *ptr++;
571 *text = (const char*)ptr;
572 return cache->getUnicharMetrics(uni);
573}
574
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000575static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
576 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 SkASSERT(cache != NULL);
578 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 const uint16_t* ptr = *(const uint16_t**)text;
581 unsigned glyphID = *ptr;
582 ptr += 1;
583 *text = (const char*)ptr;
584 return cache->getGlyphIDMetrics(glyphID);
585}
586
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000587static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
588 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 SkASSERT(cache != NULL);
590 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
593}
594
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000595static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
596 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 SkASSERT(cache != NULL);
598 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
601}
602
reed@google.com68bc6f72012-03-14 19:41:55 +0000603static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
604 const char** text) {
605 SkASSERT(cache != NULL);
606 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000607
reed@google.com68bc6f72012-03-14 19:41:55 +0000608 const int32_t* ptr = *(const int32_t**)text;
609 SkUnichar uni = *ptr++;
610 *text = (const char*)ptr;
611 return cache->getUnicharAdvance(uni);
612}
613
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000614static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
615 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 SkASSERT(cache != NULL);
617 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 const uint16_t* ptr = *(const uint16_t**)text;
620 unsigned glyphID = *ptr;
621 ptr += 1;
622 *text = (const char*)ptr;
623 return cache->getGlyphIDAdvance(glyphID);
624}
625
reed9e96aa02014-10-03 12:44:37 -0700626SkMeasureCacheProc SkPaint::getMeasureCacheProc(bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
628 sk_getMetrics_utf8_next,
629 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000630 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000632
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 sk_getAdvance_utf8_next,
634 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000635 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 sk_getAdvance_glyph_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 };
reed@google.com72cf4922011-01-04 19:58:20 +0000638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 unsigned index = this->getTextEncoding();
640
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000641 if (!needFullMetrics && !this->isDevKernText()) {
reed9e96aa02014-10-03 12:44:37 -0700642 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000643 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644
645 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
646 return gMeasureCacheProcs[index];
647}
648
649///////////////////////////////////////////////////////////////////////////////
650
651static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000652 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 SkASSERT(cache != NULL);
654 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000655
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
657}
658
659static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000660 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 SkASSERT(cache != NULL);
662 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
665}
666
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000667static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
668 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 SkASSERT(cache != NULL);
670 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000671
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
673}
674
675static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000676 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 SkASSERT(cache != NULL);
678 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000679
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
681 x, y);
682}
683
reed@google.com68bc6f72012-03-14 19:41:55 +0000684static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
685 const char** text, SkFixed, SkFixed) {
686 SkASSERT(cache != NULL);
687 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000688
reed@google.com68bc6f72012-03-14 19:41:55 +0000689 const int32_t* ptr = *(const int32_t**)text;
690 SkUnichar uni = *ptr++;
691 *text = (const char*)ptr;
692 return cache->getUnicharMetrics(uni);
693}
694
695static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
696 const char** text, SkFixed x, SkFixed y) {
697 SkASSERT(cache != NULL);
698 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000699
reed@google.com68bc6f72012-03-14 19:41:55 +0000700 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000701 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000702 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000703 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000704}
705
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000706static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
707 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 SkASSERT(cache != NULL);
709 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000710
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 const uint16_t* ptr = *(const uint16_t**)text;
712 unsigned glyphID = *ptr;
713 ptr += 1;
714 *text = (const char*)ptr;
715 return cache->getGlyphIDMetrics(glyphID);
716}
717
718static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000719 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 SkASSERT(cache != NULL);
721 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 const uint16_t* ptr = *(const uint16_t**)text;
724 unsigned glyphID = *ptr;
725 ptr += 1;
726 *text = (const char*)ptr;
727 return cache->getGlyphIDMetrics(glyphID, x, y);
728}
729
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000730SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 static const SkDrawCacheProc gDrawCacheProcs[] = {
732 sk_getMetrics_utf8_00,
733 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000734 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000736
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 sk_getMetrics_utf8_xy,
738 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000739 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 sk_getMetrics_glyph_xy
741 };
reed@google.com72cf4922011-01-04 19:58:20 +0000742
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 unsigned index = this->getTextEncoding();
reedf59eab22014-07-14 14:39:15 -0700744 if (fBitfields.fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000745 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000746 }
reed@google.com72cf4922011-01-04 19:58:20 +0000747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
749 return gDrawCacheProcs[index];
750}
751
752///////////////////////////////////////////////////////////////////////////////
753
reed@google.comed43dff2013-06-04 16:56:27 +0000754#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
755SkPaint::kDevKernText_Flag | \
756SkPaint::kLinearText_Flag | \
757SkPaint::kLCDRenderText_Flag | \
758SkPaint::kEmbeddedBitmapText_Flag | \
759SkPaint::kAutoHinting_Flag | \
760SkPaint::kGenA8FromLCD_Flag )
761
762SkScalar SkPaint::setupForAsPaths() {
763 uint32_t flags = this->getFlags();
764 // clear the flags we don't care about
765 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
766 // set the flags we do care about
767 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000768
reed@google.comed43dff2013-06-04 16:56:27 +0000769 this->setFlags(flags);
770 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000771
reed@google.comed43dff2013-06-04 16:56:27 +0000772 SkScalar textSize = fTextSize;
773 this->setTextSize(kCanonicalTextSizeForPaths);
774 return textSize / kCanonicalTextSizeForPaths;
775}
776
777class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778public:
reed@google.comed43dff2013-06-04 16:56:27 +0000779 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700780 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000781 SkPaint* p = fLazy.set(paint);
782 fScale = p->setupForAsPaths();
783 fPaint = p;
784 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000786
reed@google.comed43dff2013-06-04 16:56:27 +0000787 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000788
reed@google.comed43dff2013-06-04 16:56:27 +0000789 /**
790 * Returns 0 if the paint was unmodified, or the scale factor need to
791 * the original textSize
792 */
793 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795private:
reed@google.comed43dff2013-06-04 16:56:27 +0000796 const SkPaint* fPaint;
797 SkScalar fScale;
798 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799};
800
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000801static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 bounds->set(SkIntToScalar(g.fLeft),
803 SkIntToScalar(g.fTop),
804 SkIntToScalar(g.fLeft + g.fWidth),
805 SkIntToScalar(g.fTop + g.fHeight));
806}
807
reed@android.come88f5512010-03-19 14:42:28 +0000808// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
809// we don't overflow along the way
810typedef int64_t Sk48Dot16;
811
reed@google.com8f4d2302013-12-17 16:44:46 +0000812static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
813 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
814}
reed@android.come88f5512010-03-19 14:42:28 +0000815
reed@google.com44da42e2011-11-10 20:04:47 +0000816static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000817 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 bounds->join(SkIntToScalar(g.fLeft) + sx,
819 SkIntToScalar(g.fTop),
820 SkIntToScalar(g.fLeft + g.fWidth) + sx,
821 SkIntToScalar(g.fTop + g.fHeight));
822}
823
reed@google.com44da42e2011-11-10 20:04:47 +0000824static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
825 SkScalar sy = Sk48Dot16ToScalar(dy);
826 bounds->join(SkIntToScalar(g.fLeft),
827 SkIntToScalar(g.fTop) + sy,
828 SkIntToScalar(g.fLeft + g.fWidth),
829 SkIntToScalar(g.fTop + g.fHeight) + sy);
830}
831
832typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
833
834// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
835static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
836 SkASSERT(0 == xyIndex || 1 == xyIndex);
837 return (&glyph.fAdvanceX)[xyIndex];
838}
839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840SkScalar SkPaint::measure_text(SkGlyphCache* cache,
841 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000842 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000844 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000846 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000848 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return 0;
850 }
851
reed9e96aa02014-10-03 12:44:37 -0700852 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853
reed@google.com44da42e2011-11-10 20:04:47 +0000854 int xyIndex;
855 JoinBoundsProc joinBoundsProc;
856 if (this->isVerticalText()) {
857 xyIndex = 1;
858 joinBoundsProc = join_bounds_y;
859 } else {
860 xyIndex = 0;
861 joinBoundsProc = join_bounds_x;
862 }
863
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 int n = 1;
865 const char* stop = (const char*)text + byteLength;
866 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000867 // our accumulated fixed-point advances might overflow 16.16, so we use
868 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
869 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000870 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871
872 SkAutoKern autokern;
873
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000874 if (NULL == bounds) {
875 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 int rsb;
877 for (; text < stop; n++) {
878 rsb = g->fRsbDelta;
879 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000880 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000882 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000884 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 }
886 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000887 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000889 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 int rsb;
891 for (; text < stop; n++) {
892 rsb = g->fRsbDelta;
893 g = &glyphCacheProc(cache, &text);
894 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +0000895 joinBoundsProc(*g, bounds, x);
896 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000898 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 for (; text < stop; n++) {
900 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000901 joinBoundsProc(*g, bounds, x);
902 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 }
904 }
905 }
906 SkASSERT(text == stop);
907
908 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000909 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910}
911
reed99ae8812014-08-26 11:30:01 -0700912SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 const char* text = (const char*)textData;
914 SkASSERT(text != NULL || length == 0);
915
reed@google.comed43dff2013-06-04 16:56:27 +0000916 SkCanonicalizePaint canon(*this);
917 const SkPaint& paint = canon.getPaint();
918 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +0000919
reed99ae8812014-08-26 11:30:01 -0700920 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 SkGlyphCache* cache = autoCache.getCache();
922
923 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000924
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000925 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 int tempCount;
927
reed@google.comed43dff2013-06-04 16:56:27 +0000928 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000929 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000931 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
933 bounds->fTop = SkScalarMul(bounds->fTop, scale);
934 bounds->fRight = SkScalarMul(bounds->fRight, scale);
935 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
936 }
937 }
djsollen@google.com46348e22013-03-04 19:47:42 +0000938 } else if (bounds) {
939 // ensure that even if we don't measure_text we still update the bounds
940 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 }
942 return width;
943}
944
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
reed9e96aa02014-10-03 12:44:37 -0700946 SkScalar* measuredWidth) const {
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000947 if (0 == length || 0 >= maxWidth) {
948 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000950 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 return 0;
952 }
953
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000954 if (0 == fTextSize) {
955 if (measuredWidth) {
956 *measuredWidth = 0;
957 }
958 return length;
959 }
960
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 SkASSERT(textD != NULL);
962 const char* text = (const char*)textD;
reed9e96aa02014-10-03 12:44:37 -0700963 const char* stop = text + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964
reed@google.comed43dff2013-06-04 16:56:27 +0000965 SkCanonicalizePaint canon(*this);
966 const SkPaint& paint = canon.getPaint();
967 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968
reed@google.comed43dff2013-06-04 16:56:27 +0000969 // adjust max in case we changed the textSize in paint
970 if (scale) {
971 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 }
reed@google.com72cf4922011-01-04 19:58:20 +0000973
reed@google.comed43dff2013-06-04 16:56:27 +0000974 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 SkGlyphCache* cache = autoCache.getCache();
976
reed9e96aa02014-10-03 12:44:37 -0700977 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(false);
reed@google.comed43dff2013-06-04 16:56:27 +0000978 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +0000979 // use 64bits for our accumulator, to avoid overflowing 16.16
980 Sk48Dot16 max = SkScalarToFixed(maxWidth);
981 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982
983 SkAutoKern autokern;
984
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000985 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 int rsb = 0;
reed9e96aa02014-10-03 12:44:37 -0700987 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 const char* curr = text;
989 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000990 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000991 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 width -= x;
993 text = curr;
994 break;
995 }
996 rsb = g.fRsbDelta;
997 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000998 } else {
reed9e96aa02014-10-03 12:44:37 -0700999 while (text < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001001 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001002 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 width -= x;
1004 text = curr;
1005 break;
1006 }
1007 }
1008 }
reed@google.com72cf4922011-01-04 19:58:20 +00001009
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001010 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001011 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001012 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001014 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 *measuredWidth = scalarWidth;
1016 }
reed@google.com72cf4922011-01-04 19:58:20 +00001017
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 // return the number of bytes measured
reed9e96aa02014-10-03 12:44:37 -07001019 return text - stop + length;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020}
1021
1022///////////////////////////////////////////////////////////////////////////////
1023
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001024static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001025 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 return false; // don't detach the cache
1027}
1028
reed@google.com90808e82013-03-19 14:44:54 +00001029static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1030 void* context) {
1031 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032}
1033
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001034SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001035 SkCanonicalizePaint canon(*this);
1036 const SkPaint& paint = canon.getPaint();
1037 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001038
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001039 SkMatrix zoomMatrix, *zoomPtr = NULL;
1040 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 zoomMatrix.setScale(zoom, zoom);
1042 zoomPtr = &zoomMatrix;
1043 }
1044
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001046 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001048 }
reed@google.com72cf4922011-01-04 19:58:20 +00001049
reed@google.comed43dff2013-06-04 16:56:27 +00001050 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001052 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1054 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1055 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1056 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1057 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001058 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1059 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1060 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1061 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001062 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1063 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 }
1065 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1066}
1067
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001068///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001070static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 bounds->set(g.fLeft * scale,
1072 g.fTop * scale,
1073 (g.fLeft + g.fWidth) * scale,
1074 (g.fTop + g.fHeight) * scale);
1075}
1076
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001077int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1078 SkScalar widths[], SkRect bounds[]) const {
1079 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001081 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082
bsalomon49f085d2014-09-05 13:34:00 -07001083 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001085 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088
reed@google.comed43dff2013-06-04 16:56:27 +00001089 SkCanonicalizePaint canon(*this);
1090 const SkPaint& paint = canon.getPaint();
1091 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
reed@google.comed43dff2013-06-04 16:56:27 +00001093 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 SkGlyphCache* cache = autoCache.getCache();
1095 SkMeasureCacheProc glyphCacheProc;
reed9e96aa02014-10-03 12:44:37 -07001096 glyphCacheProc = paint.getMeasureCacheProc(NULL != bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097
1098 const char* text = (const char*)textData;
1099 const char* stop = text + byteLength;
1100 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001101 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001103 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 // we adjust the widths returned here through auto-kerning
1105 SkAutoKern autokern;
1106 SkFixed prevWidth = 0;
1107
1108 if (scale) {
1109 while (text < stop) {
1110 const SkGlyph& g = glyphCacheProc(cache, &text);
1111 if (widths) {
1112 SkFixed adjust = autokern.adjust(g);
1113
1114 if (count > 0) {
1115 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1116 *widths++ = SkScalarMul(w, scale);
1117 }
reed@google.com44da42e2011-11-10 20:04:47 +00001118 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 }
1120 if (bounds) {
1121 set_bounds(g, bounds++, scale);
1122 }
1123 ++count;
1124 }
1125 if (count > 0 && widths) {
1126 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1127 }
1128 } else {
1129 while (text < stop) {
1130 const SkGlyph& g = glyphCacheProc(cache, &text);
1131 if (widths) {
1132 SkFixed adjust = autokern.adjust(g);
1133
1134 if (count > 0) {
1135 *widths++ = SkFixedToScalar(prevWidth + adjust);
1136 }
reed@google.com44da42e2011-11-10 20:04:47 +00001137 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 }
1139 if (bounds) {
1140 set_bounds(g, bounds++);
1141 }
1142 ++count;
1143 }
1144 if (count > 0 && widths) {
1145 *widths = SkFixedToScalar(prevWidth);
1146 }
1147 }
1148 } else { // no devkern
1149 if (scale) {
1150 while (text < stop) {
1151 const SkGlyph& g = glyphCacheProc(cache, &text);
1152 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001153 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 scale);
1155 }
1156 if (bounds) {
1157 set_bounds(g, bounds++, scale);
1158 }
1159 ++count;
1160 }
1161 } else {
1162 while (text < stop) {
1163 const SkGlyph& g = glyphCacheProc(cache, &text);
1164 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001165 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166 }
1167 if (bounds) {
1168 set_bounds(g, bounds++);
1169 }
1170 ++count;
1171 }
1172 }
1173 }
1174
1175 SkASSERT(text == stop);
1176 return count;
1177}
1178
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001179///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
1181#include "SkDraw.h"
1182
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001183void SkPaint::getTextPath(const void* textData, size_t length,
1184 SkScalar x, SkScalar y, SkPath* path) const {
1185 SkASSERT(length == 0 || textData != NULL);
1186
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001188 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001190 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191
djsollen@google.com166e6532012-03-20 14:24:38 +00001192 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 SkMatrix matrix;
1194 SkScalar prevXPos = 0;
1195
1196 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1197 matrix.postTranslate(x, y);
1198 path->reset();
1199
1200 SkScalar xpos;
1201 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001202 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001204 if (iterPath) {
1205 path->addPath(*iterPath, matrix);
1206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 prevXPos = xpos;
1208 }
1209}
1210
reed@google.comca0062e2012-07-20 11:20:32 +00001211void SkPaint::getPosTextPath(const void* textData, size_t length,
1212 const SkPoint pos[], SkPath* path) const {
1213 SkASSERT(length == 0 || textData != NULL);
1214
1215 const char* text = (const char*)textData;
1216 if (text == NULL || length == 0 || path == NULL) {
1217 return;
1218 }
1219
1220 SkTextToPathIter iter(text, length, *this, false);
1221 SkMatrix matrix;
1222 SkPoint prevPos;
1223 prevPos.set(0, 0);
1224
1225 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1226 path->reset();
1227
1228 unsigned int i = 0;
1229 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001230 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001231 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001232 if (iterPath) {
1233 path->addPath(*iterPath, matrix);
1234 }
reed@google.comca0062e2012-07-20 11:20:32 +00001235 prevPos = pos[i];
1236 i++;
1237 }
1238}
1239
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001241 SkWriteBuffer* buffer) {
1242 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243}
1244
reed@google.com2739b272011-09-28 17:26:42 +00001245// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001246static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001249 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001250 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001251 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001252 }
1253
reed@google.com65dd8f82011-03-14 13:31:16 +00001254 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001255 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001256 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001257
1258 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259}
1260
reed@android.com1cdcb512009-08-24 19:11:00 +00001261// if linear-text is on, then we force hinting to be off (since that's sort of
1262// the point of linear-text.
1263static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1264 SkPaint::Hinting h = paint.getHinting();
1265 if (paint.isLinearText()) {
1266 h = SkPaint::kNo_Hinting;
1267 }
1268 return h;
1269}
1270
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001271// return true if the paint is just a single color (i.e. not a shader). If its
1272// a shader, then we can't compute a const luminance for it :(
1273static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001274 SkColor c = paint.getColor();
1275
1276 SkShader* shader = paint.getShader();
1277 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001278 return false;
1279 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001280 if (paint.getColorFilter()) {
1281 c = paint.getColorFilter()->filterColor(c);
1282 }
1283 if (color) {
1284 *color = c;
1285 }
1286 return true;
1287}
1288
reed@google.comce6dbb62012-02-10 22:01:45 +00001289static SkColor computeLuminanceColor(const SkPaint& paint) {
1290 SkColor c;
1291 if (!justAColor(paint, &c)) {
1292 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1293 }
1294 return c;
1295}
reed@google.com813d38b2012-02-13 21:37:57 +00001296
reed@google.comdd43df92012-02-15 14:50:29 +00001297#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1298
reed@google.com4f79b9b2011-09-13 13:23:26 +00001299// Beyond this size, LCD doesn't appreciably improve quality, but it always
1300// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001301#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1302 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1303#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001304
reedb3da83a2014-10-01 12:06:12 -07001305const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1306
reed4942e752014-10-01 13:59:33 -07001307static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001308 if (checkPost2x2) {
1309 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1310 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001311 area *= rec.fTextSize * rec.fTextSize;
1312 return area > gMaxSize2ForLCDText;
1313 } else {
1314 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001315 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001316}
1317
reed@google.com72cf4922011-01-04 19:58:20 +00001318/*
1319 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1320 * that vary only slightly when we create our key into the font cache, since the font scaler
1321 * typically returns the same looking resuts for tiny changes in the matrix.
1322 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001323static SkScalar sk_relax(SkScalar x) {
1324 int n = sk_float_round2int(x * 1024);
1325 return n / 1024.0f;
1326}
1327
reed@android.com36a4c2a2009-07-22 19:52:11 +00001328void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001329 const SkDeviceProperties* deviceProperties,
1330 const SkMatrix* deviceMatrix,
1331 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001332 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001334 SkTypeface* typeface = paint.getTypeface();
1335 if (NULL == typeface) {
1336 typeface = SkTypeface::GetDefaultTypeface();
1337 }
bungemanec730b92014-08-18 07:57:35 -07001338 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 rec->fTextSize = paint.getTextSize();
1340 rec->fPreScaleX = paint.getTextScaleX();
1341 rec->fPreSkewX = paint.getTextSkewX();
1342
reedb3da83a2014-10-01 12:06:12 -07001343 bool checkPost2x2 = false;
1344
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001345 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001346 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1347 if (mask & SkMatrix::kScale_Mask) {
1348 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1349 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1350 checkPost2x2 = true;
1351 } else {
1352 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1353 }
1354 if (mask & SkMatrix::kAffine_Mask) {
1355 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1356 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1357 checkPost2x2 = true;
1358 } else {
1359 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1360 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001361 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1363 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1364 }
reed@google.com72cf4922011-01-04 19:58:20 +00001365
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 SkPaint::Style style = paint.getStyle();
1367 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001368
reed@google.comffe49f52011-11-22 19:42:41 +00001369 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001370
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001371 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001372#ifdef SK_USE_FREETYPE_EMBOLDEN
1373 flags |= SkScalerContext::kEmbolden_Flag;
1374#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001375 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1376 kStdFakeBoldInterpKeys,
1377 kStdFakeBoldInterpValues,
1378 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001380
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001381 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 style = SkPaint::kStrokeAndFill_Style;
1383 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001384 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001386 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001387#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 }
1389
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001390 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001392 }
reed@google.com72cf4922011-01-04 19:58:20 +00001393
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001394 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 rec->fFrameWidth = strokeWidth;
1396 rec->fMiterLimit = paint.getStrokeMiter();
1397 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1398
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001399 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001401 }
1402 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 rec->fFrameWidth = 0;
1404 rec->fMiterLimit = 0;
1405 rec->fStrokeJoin = 0;
1406 }
1407
reed@google.com02b53312011-05-18 19:00:53 +00001408 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1409
bungeman@google.com532470f2013-01-22 19:25:14 +00001410 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001411 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001412 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001413 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001414 } else {
reed4a8126e2014-09-22 07:29:03 -07001415 SkPixelGeometry geometry = deviceProperties
reedb2d77e42014-10-14 08:26:33 -07001416 ? deviceProperties->pixelGeometry()
reed4a8126e2014-09-22 07:29:03 -07001417 : SkSurfacePropsDefaultPixelGeometry();
1418 switch (geometry) {
1419 case kUnknown_SkPixelGeometry:
1420 // eeek, can't support LCD
1421 rec->fMaskFormat = SkMask::kA8_Format;
1422 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1423 break;
1424 case kRGB_H_SkPixelGeometry:
1425 // our default, do nothing.
1426 break;
1427 case kBGR_H_SkPixelGeometry:
1428 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1429 break;
1430 case kRGB_V_SkPixelGeometry:
1431 flags |= SkScalerContext::kLCD_Vertical_Flag;
1432 break;
1433 case kBGR_V_SkPixelGeometry:
1434 flags |= SkScalerContext::kLCD_Vertical_Flag;
1435 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1436 break;
reed@google.com02b53312011-05-18 19:00:53 +00001437 }
1438 }
1439 }
1440
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001441 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001442 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001443 }
1444 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001445 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001446 }
1447 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001448 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001449 }
reed@google.com830a23e2011-11-10 15:20:49 +00001450 if (paint.isVerticalText()) {
1451 flags |= SkScalerContext::kVertical_Flag;
1452 }
reed@google.com8351aab2012-01-18 17:06:35 +00001453 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1454 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1455 }
reed@google.com02b53312011-05-18 19:00:53 +00001456 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001457
reed@google.comffe49f52011-11-22 19:42:41 +00001458 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001459 rec->setHinting(computeHinting(paint));
1460
bungeman@google.com97efada2012-07-30 20:40:50 +00001461 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001462
1463 if (NULL == deviceProperties) {
1464 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1465 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1466 } else {
reedb2d77e42014-10-14 08:26:33 -07001467 rec->setDeviceGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001468
1469 //For now always set the paint gamma equal to the device gamma.
1470 //The math in SkMaskGamma can handle them being different,
1471 //but it requires superluminous masks when
1472 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
reedb2d77e42014-10-14 08:26:33 -07001473 rec->setPaintGamma(deviceProperties->gamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001474 }
1475
1476#ifdef SK_GAMMA_CONTRAST
1477 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001478#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001479 /**
1480 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1481 * With lower values small text appears washed out (though correctly so).
1482 * With higher values lcd fringing is worse and the smoothing effect of
1483 * partial coverage is diminished.
1484 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001485 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001486#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001487
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001488 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001489
reed@android.com36a4c2a2009-07-22 19:52:11 +00001490 /* Allow the fonthost to modify our rec before we use it as a key into the
1491 cache. This way if we're asking for something that they will ignore,
1492 they can modify our rec up front, so we don't create duplicate cache
1493 entries.
1494 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001495 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001496
reed@google.com10d2d4d2012-03-01 22:32:51 +00001497 // be sure to call PostMakeRec(rec) before you actually use it!
1498}
1499
1500/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001501 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1502 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1503 * to hold it until the returned pointer is refed or forgotten.
1504 */
1505SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1506
bungeman@google.comae30f562012-09-11 18:44:55 +00001507static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001508static SkMaskGamma* gMaskGamma = NULL;
1509static SkScalar gContrast = SK_ScalarMin;
1510static SkScalar gPaintGamma = SK_ScalarMin;
1511static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001512/**
1513 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1514 * the returned SkMaskGamma pointer is refed or forgotten.
1515 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001516static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001517 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001518 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1519 if (NULL == gLinearMaskGamma) {
1520 gLinearMaskGamma = SkNEW(SkMaskGamma);
1521 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001522 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001523 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001524 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1525 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001526 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001527 gContrast = contrast;
1528 gPaintGamma = paintGamma;
1529 gDeviceGamma = deviceGamma;
1530 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001531 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001532}
1533
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001534/*static*/ void SkPaint::Term() {
1535 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1536
bungeman@google.comae30f562012-09-11 18:44:55 +00001537 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001538 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001539 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001540 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001541 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1542 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1543 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001544}
1545
bungeman@google.com97efada2012-07-30 20:40:50 +00001546/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001547 * We ensure that the rec is self-consistent and efficient (where possible)
1548 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001549void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001550 /**
1551 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001552 * limits the number of unique entries, and the scaler will only look at
1553 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001554 */
reed@google.comdd43df92012-02-15 14:50:29 +00001555 switch (rec->fMaskFormat) {
1556 case SkMask::kLCD16_Format:
1557 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001558 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001559 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001560 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001561 break;
1562 }
1563 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001564 // filter down the luminance to a single component, since A8 can't
1565 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001566 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001567 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1568 SkColorGetG(color),
1569 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001570 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001571 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001572 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001573 break;
1574 }
1575 case SkMask::kBW_Format:
1576 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001577 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001578 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001579 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580}
1581
1582#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1583
reed@google.com17fb3872011-05-04 14:31:07 +00001584#ifdef SK_DEBUG
1585 #define TEST_DESC
1586#endif
1587
reed@google.comffe49f52011-11-22 19:42:41 +00001588/*
1589 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001590 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1591 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001592 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001593void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1594 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001595 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001596 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597 SkScalerContext::Rec rec;
1598
bungeman@google.com532470f2013-01-22 19:25:14 +00001599 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001600 if (ignoreGamma) {
jvanverth2d2a68c2014-06-10 06:42:56 -07001601 rec.ignorePreBlend();
djsollen@google.com57f49692011-02-23 20:46:31 +00001602 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603
1604 size_t descSize = sizeof(rec);
1605 int entryCount = 1;
1606 SkPathEffect* pe = this->getPathEffect();
1607 SkMaskFilter* mf = this->getMaskFilter();
1608 SkRasterizer* ra = this->getRasterizer();
1609
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001610 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611
1612 if (pe) {
1613 peBuffer.writeFlattenable(pe);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001614 descSize += peBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 entryCount += 1;
1616 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1617 // seems like we could support kLCD as well at this point...
1618 }
1619 if (mf) {
1620 mfBuffer.writeFlattenable(mf);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001621 descSize += mfBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622 entryCount += 1;
1623 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001624 /* Pre-blend is not currently applied to filtered text.
1625 The primary filter is blur, for which contrast makes no sense,
1626 and for which the destination guess error is more visible.
1627 Also, all existing users of blur have calibrated for linear. */
1628 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 }
1630 if (ra) {
1631 raBuffer.writeFlattenable(ra);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001632 descSize += raBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 entryCount += 1;
1634 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1635 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001636
1637 ///////////////////////////////////////////////////////////////////////////
1638 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001639 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001640
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 descSize += SkDescriptor::ComputeOverhead(entryCount);
1642
1643 SkAutoDescriptor ad(descSize);
1644 SkDescriptor* desc = ad.getDesc();
1645
1646 desc->init();
1647 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1648
1649 if (pe) {
1650 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1651 }
1652 if (mf) {
1653 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1654 }
1655 if (ra) {
1656 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1657 }
1658
1659 SkASSERT(descSize == desc->getLength());
1660 desc->computeChecksum();
1661
reed@google.com17fb3872011-05-04 14:31:07 +00001662#ifdef TEST_DESC
1663 {
1664 // Check that we completely write the bytes in desc (our key), and that
1665 // there are no uninitialized bytes. If there were, then we would get
1666 // false-misses (or worse, false-hits) in our fontcache.
1667 //
1668 // We do this buy filling 2 others, one with 0s and the other with 1s
1669 // and create those, and then check that all 3 are identical.
1670 SkAutoDescriptor ad1(descSize);
1671 SkAutoDescriptor ad2(descSize);
1672 SkDescriptor* desc1 = ad1.getDesc();
1673 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001674
reed@google.com17fb3872011-05-04 14:31:07 +00001675 memset(desc1, 0x00, descSize);
1676 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001677
reed@google.com17fb3872011-05-04 14:31:07 +00001678 desc1->init();
1679 desc2->init();
1680 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1681 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001682
reed@google.com17fb3872011-05-04 14:31:07 +00001683 if (pe) {
1684 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1685 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1686 }
1687 if (mf) {
1688 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1689 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1690 }
1691 if (ra) {
1692 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1693 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1694 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001695
reed@google.com17fb3872011-05-04 14:31:07 +00001696 SkASSERT(descSize == desc1->getLength());
1697 SkASSERT(descSize == desc2->getLength());
1698 desc1->computeChecksum();
1699 desc2->computeChecksum();
1700 SkASSERT(!memcmp(desc, desc1, descSize));
1701 SkASSERT(!memcmp(desc, desc2, descSize));
1702 }
1703#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001704
reed@google.com90808e82013-03-19 14:44:54 +00001705 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706}
1707
bungeman@google.com532470f2013-01-22 19:25:14 +00001708SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
jvanverth2d2a68c2014-06-10 06:42:56 -07001709 const SkMatrix* deviceMatrix,
1710 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 SkGlyphCache* cache;
jvanverth2d2a68c2014-06-10 06:42:56 -07001712 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713 return cache;
1714}
1715
bungeman@google.com97efada2012-07-30 20:40:50 +00001716/**
1717 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1718 */
1719//static
1720SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1721 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001722 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1723 rec.getPaintGamma(),
1724 rec.getDeviceGamma());
1725 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001726}
1727
jvanverth2d2a68c2014-06-10 06:42:56 -07001728size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1729 SkScalar deviceGamma, int* width, int* height) {
1730 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1731 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1732 paintGamma,
1733 deviceGamma);
1734
1735 maskGamma.getGammaTableDimensions(width, height);
1736 size_t size = (*width)*(*height)*sizeof(uint8_t);
1737
1738 return size;
1739}
1740
1741void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1742 void* data) {
1743 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1744 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1745 paintGamma,
1746 deviceGamma);
1747 int width, height;
1748 maskGamma.getGammaTableDimensions(&width, &height);
1749 size_t size = width*height*sizeof(uint8_t);
1750 const uint8_t* gammaTables = maskGamma.getGammaTables();
1751 memcpy(data, gammaTables, size);
1752}
1753
1754
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755///////////////////////////////////////////////////////////////////////////////
1756
1757#include "SkStream.h"
1758
reed@android.comaefd2bc2009-03-30 21:02:14 +00001759static uintptr_t asint(const void* p) {
1760 return reinterpret_cast<uintptr_t>(p);
1761}
1762
1763union Scalar32 {
1764 SkScalar fScalar;
1765 uint32_t f32;
1766};
1767
1768static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1769 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1770 Scalar32 tmp;
1771 tmp.fScalar = value;
1772 *ptr = tmp.f32;
1773 return ptr + 1;
1774}
1775
1776static SkScalar read_scalar(const uint32_t*& ptr) {
1777 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1778 Scalar32 tmp;
1779 tmp.f32 = *ptr++;
1780 return tmp.fScalar;
1781}
1782
1783static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1784 SkASSERT(a == (uint8_t)a);
1785 SkASSERT(b == (uint8_t)b);
1786 SkASSERT(c == (uint8_t)c);
1787 SkASSERT(d == (uint8_t)d);
1788 return (a << 24) | (b << 16) | (c << 8) | d;
1789}
1790
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001791#ifdef SK_DEBUG
1792 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1793 SkASSERT(bitCount > 0 && bitCount <= 32);
1794 uint32_t mask = ~0U;
1795 mask >>= (32 - bitCount);
1796 SkASSERT(0 == (value & ~mask));
1797 }
1798#else
1799 #define ASSERT_FITS_IN(value, bitcount)
1800#endif
1801
reed@android.comaefd2bc2009-03-30 21:02:14 +00001802enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00001803 kHasTypeface_FlatFlag = 0x01,
1804 kHasEffects_FlatFlag = 0x02,
1805 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001806
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001807 kFlatFlagMask = 0x7,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001808};
1809
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001810enum BitsPerField {
1811 kFlags_BPF = 16,
1812 kHint_BPF = 2,
1813 kAlign_BPF = 2,
1814 kFilter_BPF = 2,
1815 kFlatFlags_BPF = 3,
1816};
1817
1818static inline int BPF_Mask(int bits) {
1819 return (1 << bits) - 1;
1820}
1821
1822static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
1823 unsigned filter, unsigned flatFlags) {
1824 ASSERT_FITS_IN(flags, kFlags_BPF);
1825 ASSERT_FITS_IN(hint, kHint_BPF);
1826 ASSERT_FITS_IN(align, kAlign_BPF);
1827 ASSERT_FITS_IN(filter, kFilter_BPF);
1828 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
1829
1830 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
1831 // add more bits in the future.
1832 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
1833}
1834
1835static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
1836 paint->setFlags(packed >> 16);
1837 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
1838 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
1839 paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
1840 return (FlatFlags)(packed & kFlatFlagMask);
1841}
1842
1843// V22_COMPATIBILITY_CODE
1844static FlatFlags unpack_paint_flags_v22(SkPaint* paint, uint32_t packed) {
1845 enum {
1846 kFilterBitmap_Flag = 0x02,
1847 kHighQualityFilterBitmap_Flag = 0x4000,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001848
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001849 kAll_Flags = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag
1850 };
1851
1852 // previously flags:16, textAlign:8, flatFlags:8
1853 // now flags:16, hinting:4, textAlign:4, flatFlags:8
1854 unsigned flags = packed >> 16;
1855 int filter = 0;
1856 if (flags & kFilterBitmap_Flag) {
1857 filter |= 1;
1858 }
1859 if (flags & kHighQualityFilterBitmap_Flag) {
1860 filter |= 2;
1861 }
1862 paint->setFilterLevel((SkPaint::FilterLevel)filter);
1863 flags &= ~kAll_Flags; // remove these (now dead) bit flags
1864
1865 paint->setFlags(flags);
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001866
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001867 // hinting added later. 0 in this nibble means use the default.
1868 uint32_t hinting = (packed >> 12) & 0xF;
1869 paint->setHinting(0 == hinting ? SkPaint::kNormal_Hinting : static_cast<SkPaint::Hinting>(hinting-1));
1870 paint->setTextAlign(static_cast<SkPaint::Align>((packed >> 8) & 0xF));
1871 return (FlatFlags)(packed & kFlatFlagMask);
1872}
1873
reed@android.comaefd2bc2009-03-30 21:02:14 +00001874// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00001875static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00001876 1 * sizeof(SkColor) +
1877 1 * sizeof(uint16_t) +
1878 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001879
1880/* To save space/time, we analyze the paint, and write a truncated version of
1881 it if there are not tricky elements like shaders, etc.
1882 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001883void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001884 uint8_t flatFlags = 0;
1885 if (this->getTypeface()) {
1886 flatFlags |= kHasTypeface_FlatFlag;
1887 }
1888 if (asint(this->getPathEffect()) |
1889 asint(this->getShader()) |
1890 asint(this->getXfermode()) |
1891 asint(this->getMaskFilter()) |
1892 asint(this->getColorFilter()) |
1893 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001894 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001895 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001896 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001897 flatFlags |= kHasEffects_FlatFlag;
1898 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001899
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001900 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1901 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001902
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001903 ptr = write_scalar(ptr, this->getTextSize());
1904 ptr = write_scalar(ptr, this->getTextScaleX());
1905 ptr = write_scalar(ptr, this->getTextSkewX());
1906 ptr = write_scalar(ptr, this->getStrokeWidth());
1907 ptr = write_scalar(ptr, this->getStrokeMiter());
1908 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001909
1910 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
1911 this->getFilterLevel(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001912 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1913 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001914
1915 // now we're done with ptr and the (pre)reserved space. If we need to write
1916 // additional fields, use the buffer directly
1917 if (flatFlags & kHasTypeface_FlatFlag) {
1918 buffer.writeTypeface(this->getTypeface());
1919 }
1920 if (flatFlags & kHasEffects_FlatFlag) {
1921 buffer.writeFlattenable(this->getPathEffect());
1922 buffer.writeFlattenable(this->getShader());
1923 buffer.writeFlattenable(this->getXfermode());
1924 buffer.writeFlattenable(this->getMaskFilter());
1925 buffer.writeFlattenable(this->getColorFilter());
1926 buffer.writeFlattenable(this->getRasterizer());
1927 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001928 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00001929
1930 if (fAnnotation) {
1931 buffer.writeBool(true);
1932 fAnnotation->writeToBuffer(buffer);
1933 } else {
1934 buffer.writeBool(false);
1935 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001936 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937}
1938
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001939void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001940 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1941 const void* podData = buffer.skip(kPODPaintSize);
1942 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001943
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001944 // the order we read must match the order we wrote in flatten()
1945 this->setTextSize(read_scalar(pod));
1946 this->setTextScaleX(read_scalar(pod));
1947 this->setTextSkewX(read_scalar(pod));
1948 this->setStrokeWidth(read_scalar(pod));
1949 this->setStrokeMiter(read_scalar(pod));
1950 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00001951
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001952 unsigned flatFlags = 0;
commit-bot@chromium.org7ed173b2014-05-20 17:31:08 +00001953 if (buffer.isVersionLT(SkReadBuffer::kFilterLevelIsEnum_Version)) {
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001954 flatFlags = unpack_paint_flags_v22(this, *pod++);
1955 } else {
1956 flatFlags = unpack_paint_flags(this, *pod++);
1957 }
1958
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001959 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001960 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1961 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1962 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1963 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001964
1965 if (flatFlags & kHasTypeface_FlatFlag) {
1966 this->setTypeface(buffer.readTypeface());
1967 } else {
1968 this->setTypeface(NULL);
1969 }
1970
1971 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00001972 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
1973 SkSafeUnref(this->setShader(buffer.readShader()));
1974 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
1975 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
1976 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
1977 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
1978 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
1979 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00001980
reed@google.com0cd2ac62013-10-14 20:02:44 +00001981 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00001982 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00001983 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00001984 } else {
1985 this->setPathEffect(NULL);
1986 this->setShader(NULL);
1987 this->setXfermode(NULL);
1988 this->setMaskFilter(NULL);
1989 this->setColorFilter(NULL);
1990 this->setRasterizer(NULL);
1991 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001992 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001993 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00001994
djsollen3b625542014-08-14 06:29:02 -07001995 if (buffer.isVersionLT(SkReadBuffer::kRemoveAndroidPaintOpts_Version) &&
1996 flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
1997 SkString tag;
1998 buffer.readUInt();
1999 buffer.readString(&tag);
2000 buffer.readBool();
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002001 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002}
2003
2004///////////////////////////////////////////////////////////////////////////////
2005
reed@google.com82065d62011-02-07 15:30:46 +00002006SkShader* SkPaint::setShader(SkShader* shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007 SkRefCnt_SafeAssign(fShader, shader);
2008 return shader;
2009}
2010
reed@google.com82065d62011-02-07 15:30:46 +00002011SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 SkRefCnt_SafeAssign(fColorFilter, filter);
2013 return filter;
2014}
2015
reed@google.com82065d62011-02-07 15:30:46 +00002016SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017 SkRefCnt_SafeAssign(fXfermode, mode);
2018 return mode;
2019}
2020
reed@android.com0baf1932009-06-24 12:41:42 +00002021SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002022 SkSafeUnref(fXfermode);
2023 fXfermode = SkXfermode::Create(mode);
2024 return fXfermode;
2025}
2026
reed@google.com82065d62011-02-07 15:30:46 +00002027SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028 SkRefCnt_SafeAssign(fPathEffect, effect);
2029 return effect;
2030}
2031
reed@google.com82065d62011-02-07 15:30:46 +00002032SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 SkRefCnt_SafeAssign(fMaskFilter, filter);
2034 return filter;
2035}
2036
reed@google.com82065d62011-02-07 15:30:46 +00002037///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038
reed@google.com4bbdeac2013-01-24 21:03:11 +00002039bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2040 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002041 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042
reed@google.comfd4be262012-05-25 01:04:12 +00002043 const SkPath* srcPtr = &src;
2044 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002045
reed@google.com4bbdeac2013-01-24 21:03:11 +00002046 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002047 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002048 }
2049
reed@google.comfd4be262012-05-25 01:04:12 +00002050 if (!rec.applyToPath(dst, *srcPtr)) {
2051 if (srcPtr == &tmpPath) {
2052 // If path's were copy-on-write, this trick would not be needed.
2053 // As it is, we want to save making a deep-copy from tmpPath -> dst
2054 // since we know we're just going to delete tmpPath when we return,
2055 // so the swap saves that copy.
2056 dst->swap(tmpPath);
2057 } else {
2058 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 }
2060 }
reed@google.comfd4be262012-05-25 01:04:12 +00002061 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062}
2063
reed@google.come4f10a72012-05-15 20:47:50 +00002064const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002065 SkRect* storage,
2066 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002068
reed@google.come4f10a72012-05-15 20:47:50 +00002069 const SkRect* src = &origSrc;
2070
reed@google.com9efd9a02012-01-30 15:41:43 +00002071 if (this->getLooper()) {
2072 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002073 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002074 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002076
reed@google.come4f10a72012-05-15 20:47:50 +00002077 SkRect tmpSrc;
2078 if (this->getPathEffect()) {
2079 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2080 src = &tmpSrc;
2081 }
2082
reed@google.coma584aed2012-05-16 14:06:02 +00002083 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002084 // since we're stroked, outset the rect by the radius (and join type)
2085 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2086 if (0 == radius) { // hairline
2087 radius = SK_Scalar1;
2088 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2089 SkScalar scale = this->getStrokeMiter();
2090 if (scale > SK_Scalar1) {
2091 radius = SkScalarMul(radius, scale);
2092 }
2093 }
reed@google.come4f10a72012-05-15 20:47:50 +00002094 storage->set(src->fLeft - radius, src->fTop - radius,
2095 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002096 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002097 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002098 }
2099
reed@google.com9efd9a02012-01-30 15:41:43 +00002100 if (this->getMaskFilter()) {
2101 this->getMaskFilter()->computeFastBounds(*storage, storage);
2102 }
2103
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002104 if (this->getImageFilter()) {
2105 this->getImageFilter()->computeFastBounds(*storage, storage);
2106 }
2107
reed@android.comd252db02009-04-01 18:31:44 +00002108 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109}
2110
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002111#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002112
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002113void SkPaint::toString(SkString* str) const {
2114 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2115
2116 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002117 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002118 SkDynamicMemoryWStream ostream;
2119 typeface->serialize(&ostream);
2120 SkAutoTUnref<SkStreamAsset> istream(ostream.detachAsStream());
2121 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002122
2123 str->append("<dt>Font Family Name:</dt><dd>");
2124 str->append(descriptor.getFamilyName());
2125 str->append("</dd><dt>Font Full Name:</dt><dd>");
2126 str->append(descriptor.getFullName());
2127 str->append("</dd><dt>Font PS Name:</dt><dd>");
2128 str->append(descriptor.getPostscriptName());
2129 str->append("</dd><dt>Font File Name:</dt><dd>");
2130 str->append(descriptor.getFontFileName());
2131 str->append("</dd>");
2132 }
2133
2134 str->append("<dt>TextSize:</dt><dd>");
2135 str->appendScalar(this->getTextSize());
2136 str->append("</dd>");
2137
2138 str->append("<dt>TextScaleX:</dt><dd>");
2139 str->appendScalar(this->getTextScaleX());
2140 str->append("</dd>");
2141
2142 str->append("<dt>TextSkewX:</dt><dd>");
2143 str->appendScalar(this->getTextSkewX());
2144 str->append("</dd>");
2145
2146 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002147 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002148 str->append("<dt>PathEffect:</dt><dd>");
2149 str->append("</dd>");
2150 }
2151
2152 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002153 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002154 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002155 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002156 str->append("</dd>");
2157 }
2158
2159 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002160 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002161 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002162 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002163 str->append("</dd>");
2164 }
2165
2166 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002167 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002168 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002169 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002170 str->append("</dd>");
2171 }
2172
2173 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002174 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002175 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002176 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002177 str->append("</dd>");
2178 }
2179
2180 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002181 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002182 str->append("<dt>Rasterizer:</dt><dd>");
2183 str->append("</dd>");
2184 }
2185
2186 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002187 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002188 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002189 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002190 str->append("</dd>");
2191 }
2192
2193 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002194 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002195 str->append("<dt>ImageFilter:</dt><dd>");
2196 str->append("</dd>");
2197 }
2198
2199 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002200 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002201 str->append("<dt>Annotation:</dt><dd>");
2202 str->append("</dd>");
2203 }
2204
2205 str->append("<dt>Color:</dt><dd>0x");
2206 SkColor color = this->getColor();
2207 str->appendHex(color);
2208 str->append("</dd>");
2209
2210 str->append("<dt>Stroke Width:</dt><dd>");
2211 str->appendScalar(this->getStrokeWidth());
2212 str->append("</dd>");
2213
2214 str->append("<dt>Stroke Miter:</dt><dd>");
2215 str->appendScalar(this->getStrokeMiter());
2216 str->append("</dd>");
2217
2218 str->append("<dt>Flags:</dt><dd>(");
2219 if (this->getFlags()) {
2220 bool needSeparator = false;
2221 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002222 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2223 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2224 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2225 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2226 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2227 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2228 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2229 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2230 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2231 "EmbeddedBitmapText", &needSeparator);
2232 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2233 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2234 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2235 "GenA8FromLCD", &needSeparator);
2236 } else {
2237 str->append("None");
2238 }
2239 str->append(")</dd>");
2240
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002241 str->append("<dt>FilterLevel:</dt><dd>");
2242 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2243 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2244 str->append("</dd>");
2245
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002246 str->append("<dt>TextAlign:</dt><dd>");
2247 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2248 str->append(gTextAlignStrings[this->getTextAlign()]);
2249 str->append("</dd>");
2250
2251 str->append("<dt>CapType:</dt><dd>");
2252 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2253 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2254 str->append("</dd>");
2255
2256 str->append("<dt>JoinType:</dt><dd>");
2257 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2258 str->append(gJoinStrings[this->getStrokeJoin()]);
2259 str->append("</dd>");
2260
2261 str->append("<dt>Style:</dt><dd>");
2262 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2263 str->append(gStyleStrings[this->getStyle()]);
2264 str->append("</dd>");
2265
2266 str->append("<dt>TextEncoding:</dt><dd>");
2267 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2268 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2269 str->append("</dd>");
2270
2271 str->append("<dt>Hinting:</dt><dd>");
2272 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2273 str->append(gHintingStrings[this->getHinting()]);
2274 str->append("</dd>");
2275
2276 str->append("</dd></dl></dl>");
2277}
2278#endif
2279
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002280///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002282static bool has_thick_frame(const SkPaint& paint) {
2283 return paint.getStrokeWidth() > 0 &&
2284 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285}
2286
2287SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2288 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002289 bool applyStrokeAndPathEffects)
2290 : fPaint(paint) {
reed9e96aa02014-10-03 12:44:37 -07002291 fGlyphCacheProc = paint.getMeasureCacheProc(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292
djsollen@google.com166e6532012-03-20 14:24:38 +00002293 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2295
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002296 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299
djsollen@google.com166e6532012-03-20 14:24:38 +00002300 // can't use our canonical size if we need to apply patheffects
2301 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2303 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002304 if (has_thick_frame(fPaint)) {
2305 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2306 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002307 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002309 }
reed@google.com72cf4922011-01-04 19:58:20 +00002310
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002311 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 fPaint.setStyle(SkPaint::kFill_Style);
2313 fPaint.setPathEffect(NULL);
2314 }
2315
jvanverth2d2a68c2014-06-10 06:42:56 -07002316 fCache = fPaint.detachCache(NULL, NULL, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317
2318 SkPaint::Style style = SkPaint::kFill_Style;
2319 SkPathEffect* pe = NULL;
2320
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002321 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 style = paint.getStyle(); // restore
2323 pe = paint.getPathEffect(); // restore
2324 }
2325 fPaint.setStyle(style);
2326 fPaint.setPathEffect(pe);
2327 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2328
2329 // now compute fXOffset if needed
2330
2331 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002332 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002334 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2335 &count, NULL), fScale);
2336 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002338 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 xOffset = -width;
2340 }
2341 fXPos = xOffset;
2342 fPrevAdvance = 0;
2343
2344 fText = text;
2345 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002346
reed@google.com44da42e2011-11-10 20:04:47 +00002347 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348}
2349
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002350SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 SkGlyphCache::AttachCache(fCache);
2352}
2353
reed@google.com7b4531f2012-08-07 15:53:00 +00002354bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2355 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2357
2358 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002359 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002361 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002362 if (path) {
2363 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002364 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002365 } else {
2366 if (path) {
2367 *path = NULL;
2368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002370 if (xpos) {
2371 *xpos = fXPos;
2372 }
2373 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002375 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002377
2378///////////////////////////////////////////////////////////////////////////////
2379
reedf539b8c2014-11-11 12:51:33 -08002380// return true if the filter exists, and may affect alpha
2381static bool affects_alpha(const SkColorFilter* cf) {
2382 return cf && !(cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
2383}
2384
2385// return true if the filter exists, and may affect alpha
2386static bool affects_alpha(const SkImageFilter* imf) {
2387 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
2388 // ala colorfilters
2389 return imf != NULL;
2390}
2391
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002392bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002393 if (fLooper) {
2394 return false;
2395 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002396 SkXfermode::Mode mode;
2397 if (SkXfermode::AsMode(fXfermode, &mode)) {
2398 switch (mode) {
2399 case SkXfermode::kSrcOver_Mode:
2400 case SkXfermode::kSrcATop_Mode:
2401 case SkXfermode::kDstOut_Mode:
2402 case SkXfermode::kDstOver_Mode:
2403 case SkXfermode::kPlus_Mode:
reedf539b8c2014-11-11 12:51:33 -08002404 if (0 == this->getAlpha()) {
2405 return !affects_alpha(fColorFilter) && !affects_alpha(fImageFilter);
2406 }
2407 break;
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002408 case SkXfermode::kDst_Mode:
2409 return true;
2410 default:
2411 break;
2412 }
2413 }
2414 return false;
2415}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002416
mtkleinfb1fe4f2014-10-07 09:26:10 -07002417uint32_t SkPaint::getHash() const {
2418 // We're going to hash 10 pointers and 7 32-bit values, finishing up with fBitfields,
2419 // so fBitfields should be 10 pointers and 6 32-bit values from the start.
2420 SK_COMPILE_ASSERT(offsetof(SkPaint, fBitfields) == 10 * sizeof(void*) + 6 * sizeof(uint32_t),
2421 SkPaint_notPackedTightly);
2422 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(this),
2423 offsetof(SkPaint, fBitfields) + sizeof(fBitfields));
2424}