blob: 67b630e850ed288d058977866fa13356fffbe6cc [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000012#include "SkData.h"
bungeman@google.com532470f2013-01-22 19:25:14 +000013#include "SkDeviceProperties.h"
rmistryc4b84ae2014-06-23 06:59:15 -070014#include "SkDraw.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000015#include "SkFontDescriptor.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkFontHost.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000017#include "SkGlyphCache.h"
reed@google.com15356a62011-11-03 19:29:08 +000018#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000020#include "SkMaskGamma.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000021#include "SkReadBuffer.h"
22#include "SkWriteBuffer.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000023#include "SkPaintDefaults.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPathEffect.h"
25#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000026#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000028#include "SkShader.h"
29#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000032#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000033#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkTypeface.h"
35#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000036
reed@google.coma3237872011-07-05 19:20:48 +000037// define this to get a printf for out-of-range parameter in setters
38// e.g. setTextSize(-1)
39//#define SK_REPORT_API_RANGE_CHECK
40
djsollen@google.com56c69772011-11-08 19:00:26 +000041#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000042#define GEN_ID_INC fGenerationID++
43#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
44#else
45#define GEN_ID_INC
46#define GEN_ID_INC_EVAL(expression)
47#endif
48
reed@android.coma3122b92009-08-13 20:38:25 +000049SkPaint::SkPaint() {
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +000050 fTypeface = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 fPathEffect = NULL;
52 fShader = NULL;
53 fXfermode = NULL;
54 fMaskFilter = NULL;
55 fColorFilter = NULL;
56 fRasterizer = NULL;
57 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000058 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000059 fAnnotation = NULL;
reed@android.coma3122b92009-08-13 20:38:25 +000060
reedf59eab22014-07-14 14:39:15 -070061 fTextSize = SkPaintDefaults_TextSize;
62 fTextScaleX = SK_Scalar1;
63 fTextSkewX = 0;
64 fColor = SK_ColorBLACK;
65 fWidth = 0;
66 fMiterLimit = SkPaintDefaults_MiterLimit;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000067
68 // Zero all bitfields, then set some non-zero defaults.
reedf59eab22014-07-14 14:39:15 -070069 fBitfieldsUInt = 0;
70 fBitfields.fFlags = SkPaintDefaults_Flags;
71 fBitfields.fCapType = kDefault_Cap;
72 fBitfields.fJoinType = kDefault_Join;
73 fBitfields.fTextAlign = kLeft_Align;
74 fBitfields.fStyle = kFill_Style;
75 fBitfields.fTextEncoding = kUTF8_TextEncoding;
76 fBitfields.fHinting = SkPaintDefaults_Hinting;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000077
djsollen@google.com56c69772011-11-08 19:00:26 +000078#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000079 fGenerationID = 0;
80#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000081}
82
reed@google.com6fb7e2e2011-02-08 22:22:52 +000083SkPaint::SkPaint(const SkPaint& src) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000084#define COPY(field) field = src.field
85#define REF_COPY(field) field = SkSafeRef(src.field)
reed@android.com8a1c16f2008-12-17 15:59:43 +000086
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +000087 REF_COPY(fTypeface);
88 REF_COPY(fPathEffect);
89 REF_COPY(fShader);
90 REF_COPY(fXfermode);
91 REF_COPY(fMaskFilter);
92 REF_COPY(fColorFilter);
93 REF_COPY(fRasterizer);
94 REF_COPY(fLooper);
95 REF_COPY(fImageFilter);
96 REF_COPY(fAnnotation);
97
98 COPY(fTextSize);
99 COPY(fTextScaleX);
100 COPY(fTextSkewX);
101 COPY(fColor);
102 COPY(fWidth);
103 COPY(fMiterLimit);
104 COPY(fBitfields);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000105
106#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000107 COPY(fGenerationID);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000108#endif
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000109
110#undef COPY
111#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112}
113
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000114SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000115 SkSafeUnref(fTypeface);
116 SkSafeUnref(fPathEffect);
117 SkSafeUnref(fShader);
118 SkSafeUnref(fXfermode);
119 SkSafeUnref(fMaskFilter);
120 SkSafeUnref(fColorFilter);
121 SkSafeUnref(fRasterizer);
122 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000123 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000124 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125}
126
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000127SkPaint& SkPaint::operator=(const SkPaint& src) {
commit-bot@chromium.orgf239d912014-05-02 20:14:59 +0000128 if (this == &src) {
129 return *this;
130 }
131
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000132#define COPY(field) field = src.field
133#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 SkASSERT(&src);
136
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000137 REF_COPY(fTypeface);
138 REF_COPY(fPathEffect);
139 REF_COPY(fShader);
140 REF_COPY(fXfermode);
141 REF_COPY(fMaskFilter);
142 REF_COPY(fColorFilter);
143 REF_COPY(fRasterizer);
144 REF_COPY(fLooper);
145 REF_COPY(fImageFilter);
146 REF_COPY(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000148 COPY(fTextSize);
149 COPY(fTextScaleX);
150 COPY(fTextSkewX);
151 COPY(fColor);
152 COPY(fWidth);
153 COPY(fMiterLimit);
154 COPY(fBitfields);
djsollen@google.com56c69772011-11-08 19:00:26 +0000155#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000156 ++fGenerationID;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000157#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158
159 return *this;
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000160
161#undef COPY
162#undef REF_COPY
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163}
164
robertphillips@google.comb2657412013-08-07 22:36:29 +0000165bool operator==(const SkPaint& a, const SkPaint& b) {
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000166#define EQUAL(field) (a.field == b.field)
mtkleinee902cd2014-09-22 11:40:33 -0700167 // Don't check fGenerationID, which can be different for logically equal paints.
mtklein610a0152014-09-25 11:57:53 -0700168 return EQUAL(fTypeface)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000169 && EQUAL(fPathEffect)
170 && EQUAL(fShader)
171 && EQUAL(fXfermode)
172 && EQUAL(fMaskFilter)
173 && EQUAL(fColorFilter)
174 && EQUAL(fRasterizer)
175 && EQUAL(fLooper)
176 && EQUAL(fImageFilter)
177 && EQUAL(fAnnotation)
178 && EQUAL(fTextSize)
179 && EQUAL(fTextScaleX)
180 && EQUAL(fTextSkewX)
181 && EQUAL(fColor)
182 && EQUAL(fWidth)
183 && EQUAL(fMiterLimit)
reedf59eab22014-07-14 14:39:15 -0700184 && EQUAL(fBitfieldsUInt)
commit-bot@chromium.orge8807f42014-03-24 23:03:11 +0000185 ;
186#undef EQUAL
robertphillips@google.comb2657412013-08-07 22:36:29 +0000187}
188
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000189void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 SkPaint init;
191
djsollen@google.com56c69772011-11-08 19:00:26 +0000192#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000193 uint32_t oldGenerationID = fGenerationID;
194#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000196#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000197 fGenerationID = oldGenerationID + 1;
198#endif
199}
200
djsollen@google.com56c69772011-11-08 19:00:26 +0000201#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000202uint32_t SkPaint::getGenerationID() const {
203 return fGenerationID;
204}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000205
206void SkPaint::setGenerationID(uint32_t generationID) {
207 fGenerationID = generationID;
208}
djsollen@google.com60abb072012-02-15 18:49:15 +0000209#endif
210
reed@google.comc9683152013-07-18 13:47:01 +0000211void SkPaint::setFilterLevel(FilterLevel level) {
reedf59eab22014-07-14 14:39:15 -0700212 GEN_ID_INC_EVAL((unsigned) level != fBitfields.fFilterLevel);
213 fBitfields.fFilterLevel = level;
reed@google.comc9683152013-07-18 13:47:01 +0000214}
215
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000216void SkPaint::setHinting(Hinting hintingLevel) {
reedf59eab22014-07-14 14:39:15 -0700217 GEN_ID_INC_EVAL((unsigned) hintingLevel != fBitfields.fHinting);
218 fBitfields.fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219}
220
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000221void SkPaint::setFlags(uint32_t flags) {
reedf59eab22014-07-14 14:39:15 -0700222 GEN_ID_INC_EVAL(flags != fBitfields.fFlags);
223 fBitfields.fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224}
225
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000226void SkPaint::setAntiAlias(bool doAA) {
reedf59eab22014-07-14 14:39:15 -0700227 this->setFlags(SkSetClearMask(fBitfields.fFlags, doAA, kAntiAlias_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228}
229
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000230void SkPaint::setDither(bool doDither) {
reedf59eab22014-07-14 14:39:15 -0700231 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDither, kDither_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232}
233
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000234void SkPaint::setSubpixelText(bool doSubpixel) {
reedf59eab22014-07-14 14:39:15 -0700235 this->setFlags(SkSetClearMask(fBitfields.fFlags, doSubpixel, kSubpixelText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236}
237
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000238void SkPaint::setLCDRenderText(bool doLCDRender) {
reedf59eab22014-07-14 14:39:15 -0700239 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLCDRender, kLCDRenderText_Flag));
agl@chromium.org309485b2009-07-21 17:41:32 +0000240}
241
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000242void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
reedf59eab22014-07-14 14:39:15 -0700243 this->setFlags(SkSetClearMask(fBitfields.fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000244}
245
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246void SkPaint::setAutohinted(bool useAutohinter) {
reedf59eab22014-07-14 14:39:15 -0700247 this->setFlags(SkSetClearMask(fBitfields.fFlags, useAutohinter, kAutoHinting_Flag));
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000248}
249
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000250void SkPaint::setLinearText(bool doLinearText) {
reedf59eab22014-07-14 14:39:15 -0700251 this->setFlags(SkSetClearMask(fBitfields.fFlags, doLinearText, kLinearText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252}
253
reed@google.com830a23e2011-11-10 15:20:49 +0000254void SkPaint::setVerticalText(bool doVertical) {
reedf59eab22014-07-14 14:39:15 -0700255 this->setFlags(SkSetClearMask(fBitfields.fFlags, doVertical, kVerticalText_Flag));
reed@google.com830a23e2011-11-10 15:20:49 +0000256}
257
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258void SkPaint::setUnderlineText(bool doUnderline) {
reedf59eab22014-07-14 14:39:15 -0700259 this->setFlags(SkSetClearMask(fBitfields.fFlags, doUnderline, kUnderlineText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setStrikeThruText(bool doStrikeThru) {
reedf59eab22014-07-14 14:39:15 -0700263 this->setFlags(SkSetClearMask(fBitfields.fFlags, doStrikeThru, kStrikeThruText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setFakeBoldText(bool doFakeBold) {
reedf59eab22014-07-14 14:39:15 -0700267 this->setFlags(SkSetClearMask(fBitfields.fFlags, doFakeBold, kFakeBoldText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setDevKernText(bool doDevKern) {
reedf59eab22014-07-14 14:39:15 -0700271 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDevKern, kDevKernText_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272}
273
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000274void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) {
reedf59eab22014-07-14 14:39:15 -0700275 this->setFlags(SkSetClearMask(fBitfields.fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag));
commit-bot@chromium.orgb97c3ff2014-03-11 17:07:15 +0000276}
277
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000278void SkPaint::setStyle(Style style) {
279 if ((unsigned)style < kStyleCount) {
reedf59eab22014-07-14 14:39:15 -0700280 GEN_ID_INC_EVAL((unsigned)style != fBitfields.fStyle);
281 fBitfields.fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000282 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000283#ifdef SK_REPORT_API_RANGE_CHECK
284 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
285#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287}
288
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000289void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000290 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 fColor = color;
292}
293
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000295 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
296 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297}
298
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000299void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000300 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301}
302
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303void SkPaint::setStrokeWidth(SkScalar width) {
304 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000305 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000307 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000308#ifdef SK_REPORT_API_RANGE_CHECK
309 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
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::setStrokeMiter(SkScalar limit) {
315 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000316 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000318 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000319#ifdef SK_REPORT_API_RANGE_CHECK
320 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
321#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323}
324
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000325void SkPaint::setStrokeCap(Cap ct) {
326 if ((unsigned)ct < kCapCount) {
reedf59eab22014-07-14 14:39:15 -0700327 GEN_ID_INC_EVAL((unsigned)ct != fBitfields.fCapType);
328 fBitfields.fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000329 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000330#ifdef SK_REPORT_API_RANGE_CHECK
331 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
332#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334}
335
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000336void SkPaint::setStrokeJoin(Join jt) {
337 if ((unsigned)jt < kJoinCount) {
reedf59eab22014-07-14 14:39:15 -0700338 GEN_ID_INC_EVAL((unsigned)jt != fBitfields.fJoinType);
339 fBitfields.fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000340 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000341#ifdef SK_REPORT_API_RANGE_CHECK
342 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
343#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345}
346
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000347///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000349void SkPaint::setTextAlign(Align align) {
350 if ((unsigned)align < kAlignCount) {
reedf59eab22014-07-14 14:39:15 -0700351 GEN_ID_INC_EVAL((unsigned)align != fBitfields.fTextAlign);
352 fBitfields.fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000353 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000354#ifdef SK_REPORT_API_RANGE_CHECK
355 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
356#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358}
359
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000360void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000361 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000362 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000364 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000365#ifdef SK_REPORT_API_RANGE_CHECK
366 SkDebugf("SkPaint::setTextSize() called with negative value\n");
367#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369}
370
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000371void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000372 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 fTextScaleX = scaleX;
374}
375
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000376void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000377 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 fTextSkewX = skewX;
379}
380
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000381void SkPaint::setTextEncoding(TextEncoding encoding) {
382 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reedf59eab22014-07-14 14:39:15 -0700383 GEN_ID_INC_EVAL((unsigned)encoding != fBitfields.fTextEncoding);
384 fBitfields.fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000385 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000386#ifdef SK_REPORT_API_RANGE_CHECK
387 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
388#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000389 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390}
391
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000392///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000394SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000396 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 return font;
398}
399
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000400SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000402 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 return r;
404}
405
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000406SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000408 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 return looper;
410}
411
reed@google.com15356a62011-11-03 19:29:08 +0000412SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
413 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
414 GEN_ID_INC;
415 return imageFilter;
416}
417
reed@google.comb0a34d82012-07-11 19:57:55 +0000418SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
419 SkRefCnt_SafeAssign(fAnnotation, annotation);
420 GEN_ID_INC;
reed@google.comb0a34d82012-07-11 19:57:55 +0000421 return annotation;
422}
423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424///////////////////////////////////////////////////////////////////////////////
425
reed@google.comed43dff2013-06-04 16:56:27 +0000426static SkScalar mag2(SkScalar x, SkScalar y) {
427 return x * x + y * y;
428}
429
430static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
431 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
432 ||
433 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
434}
435
436bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
437 SkASSERT(!ctm.hasPerspective());
438 SkASSERT(!textM.hasPerspective());
439
440 SkMatrix matrix;
441 matrix.setConcat(ctm, textM);
442 return tooBig(matrix, MaxCacheSize2());
443}
444
reed@google.comed43dff2013-06-04 16:56:27 +0000445
446///////////////////////////////////////////////////////////////////////////////
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448#include "SkGlyphCache.h"
449#include "SkUtils.h"
450
reed@google.com90808e82013-03-19 14:44:54 +0000451static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
452 void* context) {
453 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000454}
455
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
457 uint16_t glyphs[]) const {
458 if (byteLength == 0) {
459 return 0;
460 }
reed@google.com72cf4922011-01-04 19:58:20 +0000461
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkASSERT(textData != NULL);
463
464 if (NULL == glyphs) {
465 switch (this->getTextEncoding()) {
466 case kUTF8_TextEncoding:
467 return SkUTF8_CountUnichars((const char*)textData, byteLength);
468 case kUTF16_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000469 return SkUTF16_CountUnichars((const uint16_t*)textData, SkToInt(byteLength >> 1));
reed@google.com68bc6f72012-03-14 19:41:55 +0000470 case kUTF32_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000471 return SkToInt(byteLength >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 case kGlyphID_TextEncoding:
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000473 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000475 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 }
477 return 0;
478 }
reed@google.com72cf4922011-01-04 19:58:20 +0000479
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000481
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 // handle this encoding before the setup for the glyphcache
483 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
484 // we want to ignore the low bit of byteLength
485 memcpy(glyphs, textData, byteLength >> 1 << 1);
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000486 return SkToInt(byteLength >> 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 }
reed@google.com72cf4922011-01-04 19:58:20 +0000488
bungeman@google.com532470f2013-01-22 19:25:14 +0000489 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 SkGlyphCache* cache = autoCache.getCache();
491
492 const char* text = (const char*)textData;
493 const char* stop = text + byteLength;
494 uint16_t* gptr = glyphs;
495
496 switch (this->getTextEncoding()) {
497 case SkPaint::kUTF8_TextEncoding:
498 while (text < stop) {
499 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
500 }
501 break;
502 case SkPaint::kUTF16_TextEncoding: {
503 const uint16_t* text16 = (const uint16_t*)text;
504 const uint16_t* stop16 = (const uint16_t*)stop;
505 while (text16 < stop16) {
506 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
507 }
508 break;
509 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000510 case kUTF32_TextEncoding: {
511 const int32_t* text32 = (const int32_t*)text;
512 const int32_t* stop32 = (const int32_t*)stop;
513 while (text32 < stop32) {
514 *gptr++ = cache->unicharToGlyph(*text32++);
515 }
516 break;
517 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000519 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 }
commit-bot@chromium.org2cfa3202014-04-19 22:00:40 +0000521 return SkToInt(gptr - glyphs);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522}
523
reed@android.coma5dcaf62010-02-05 17:12:32 +0000524bool SkPaint::containsText(const void* textData, size_t byteLength) const {
525 if (0 == byteLength) {
526 return true;
527 }
reed@google.com72cf4922011-01-04 19:58:20 +0000528
reed@android.coma5dcaf62010-02-05 17:12:32 +0000529 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000530
reed@android.coma5dcaf62010-02-05 17:12:32 +0000531 // handle this encoding before the setup for the glyphcache
532 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
533 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
534 size_t count = byteLength >> 1;
535 for (size_t i = 0; i < count; i++) {
536 if (0 == glyphID[i]) {
537 return false;
538 }
539 }
540 return true;
541 }
reed@google.com72cf4922011-01-04 19:58:20 +0000542
bungeman@google.com532470f2013-01-22 19:25:14 +0000543 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000544 SkGlyphCache* cache = autoCache.getCache();
545
546 switch (this->getTextEncoding()) {
547 case SkPaint::kUTF8_TextEncoding: {
548 const char* text = static_cast<const char*>(textData);
549 const char* stop = text + byteLength;
550 while (text < stop) {
551 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
552 return false;
553 }
554 }
555 break;
556 }
557 case SkPaint::kUTF16_TextEncoding: {
558 const uint16_t* text = static_cast<const uint16_t*>(textData);
559 const uint16_t* stop = text + (byteLength >> 1);
560 while (text < stop) {
561 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
562 return false;
563 }
564 }
565 break;
566 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000567 case SkPaint::kUTF32_TextEncoding: {
568 const int32_t* text = static_cast<const int32_t*>(textData);
569 const int32_t* stop = text + (byteLength >> 2);
570 while (text < stop) {
571 if (0 == cache->unicharToGlyph(*text++)) {
572 return false;
573 }
574 }
575 break;
576 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000577 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000578 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000579 return false;
580 }
581 return true;
582}
583
reed@android.com9d3a9852010-01-08 14:07:42 +0000584void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
585 SkUnichar textData[]) const {
586 if (count <= 0) {
587 return;
588 }
589
590 SkASSERT(glyphs != NULL);
591 SkASSERT(textData != NULL);
592
bungeman@google.com532470f2013-01-22 19:25:14 +0000593 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000594 SkGlyphCache* cache = autoCache.getCache();
595
596 for (int index = 0; index < count; index++) {
597 textData[index] = cache->glyphToUnichar(glyphs[index]);
598 }
599}
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601///////////////////////////////////////////////////////////////////////////////
602
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000603static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
604 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(cache != NULL);
606 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
609}
610
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000611static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
612 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 SkASSERT(cache != NULL);
614 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
617}
618
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000619static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
620 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkASSERT(cache != NULL);
622 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000623
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
625}
626
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000627static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
628 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 SkASSERT(cache != NULL);
630 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000631
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
633}
634
reed@google.com68bc6f72012-03-14 19:41:55 +0000635static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
636 const char** text) {
637 SkASSERT(cache != NULL);
638 SkASSERT(text != NULL);
639
640 const int32_t* ptr = *(const int32_t**)text;
641 SkUnichar uni = *ptr++;
642 *text = (const char*)ptr;
643 return cache->getUnicharMetrics(uni);
644}
645
646static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
647 const char** text) {
648 SkASSERT(cache != NULL);
649 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000650
reed@google.com68bc6f72012-03-14 19:41:55 +0000651 const int32_t* ptr = *(const int32_t**)text;
652 SkUnichar uni = *--ptr;
653 *text = (const char*)ptr;
654 return cache->getUnicharMetrics(uni);
655}
656
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000657static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
658 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 SkASSERT(cache != NULL);
660 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 const uint16_t* ptr = *(const uint16_t**)text;
663 unsigned glyphID = *ptr;
664 ptr += 1;
665 *text = (const char*)ptr;
666 return cache->getGlyphIDMetrics(glyphID);
667}
668
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000669static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
670 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkASSERT(cache != NULL);
672 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 const uint16_t* ptr = *(const uint16_t**)text;
675 ptr -= 1;
676 unsigned glyphID = *ptr;
677 *text = (const char*)ptr;
678 return cache->getGlyphIDMetrics(glyphID);
679}
680
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000681static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
682 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 SkASSERT(cache != NULL);
684 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
687}
688
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000689static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
690 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 SkASSERT(cache != NULL);
692 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
695}
696
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000697static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
698 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 SkASSERT(cache != NULL);
700 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000701
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
703}
704
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000705static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
706 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 SkASSERT(cache != NULL);
708 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
711}
712
reed@google.com68bc6f72012-03-14 19:41:55 +0000713static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
714 const char** text) {
715 SkASSERT(cache != NULL);
716 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000717
reed@google.com68bc6f72012-03-14 19:41:55 +0000718 const int32_t* ptr = *(const int32_t**)text;
719 SkUnichar uni = *ptr++;
720 *text = (const char*)ptr;
721 return cache->getUnicharAdvance(uni);
722}
723
724static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
725 const char** text) {
726 SkASSERT(cache != NULL);
727 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000728
reed@google.com68bc6f72012-03-14 19:41:55 +0000729 const int32_t* ptr = *(const int32_t**)text;
730 SkUnichar uni = *--ptr;
731 *text = (const char*)ptr;
732 return cache->getUnicharAdvance(uni);
733}
734
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
736 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 SkASSERT(cache != NULL);
738 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 const uint16_t* ptr = *(const uint16_t**)text;
741 unsigned glyphID = *ptr;
742 ptr += 1;
743 *text = (const char*)ptr;
744 return cache->getGlyphIDAdvance(glyphID);
745}
746
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000747static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
748 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 SkASSERT(cache != NULL);
750 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000751
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 const uint16_t* ptr = *(const uint16_t**)text;
753 ptr -= 1;
754 unsigned glyphID = *ptr;
755 *text = (const char*)ptr;
756 return cache->getGlyphIDAdvance(glyphID);
757}
758
759SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000760 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
762 sk_getMetrics_utf8_next,
763 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000764 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000766
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 sk_getMetrics_utf8_prev,
768 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000769 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 sk_getAdvance_utf8_next,
773 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000774 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000776
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 sk_getAdvance_utf8_prev,
778 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000779 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 sk_getAdvance_glyph_prev
781 };
reed@google.com72cf4922011-01-04 19:58:20 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 unsigned index = this->getTextEncoding();
784
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000785 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000786 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000787 }
788 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000789 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000790 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791
792 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
793 return gMeasureCacheProcs[index];
794}
795
796///////////////////////////////////////////////////////////////////////////////
797
798static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000799 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 SkASSERT(cache != NULL);
801 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000802
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
804}
805
806static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000807 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 SkASSERT(cache != NULL);
809 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
812}
813
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000814static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
815 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 SkASSERT(cache != NULL);
817 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
820}
821
822static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000823 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 SkASSERT(cache != NULL);
825 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000826
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
828 x, y);
829}
830
reed@google.com68bc6f72012-03-14 19:41:55 +0000831static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
832 const char** text, SkFixed, SkFixed) {
833 SkASSERT(cache != NULL);
834 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000835
reed@google.com68bc6f72012-03-14 19:41:55 +0000836 const int32_t* ptr = *(const int32_t**)text;
837 SkUnichar uni = *ptr++;
838 *text = (const char*)ptr;
839 return cache->getUnicharMetrics(uni);
840}
841
842static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
843 const char** text, SkFixed x, SkFixed y) {
844 SkASSERT(cache != NULL);
845 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000846
reed@google.com68bc6f72012-03-14 19:41:55 +0000847 const int32_t* ptr = *(const int32_t**)text;
commit-bot@chromium.org584f3372014-05-19 19:04:02 +0000848 SkUnichar uni = *ptr++;
reed@google.com68bc6f72012-03-14 19:41:55 +0000849 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000850 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000851}
852
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000853static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
854 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 SkASSERT(cache != NULL);
856 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000857
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 const uint16_t* ptr = *(const uint16_t**)text;
859 unsigned glyphID = *ptr;
860 ptr += 1;
861 *text = (const char*)ptr;
862 return cache->getGlyphIDMetrics(glyphID);
863}
864
865static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000866 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 SkASSERT(cache != NULL);
868 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000869
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 const uint16_t* ptr = *(const uint16_t**)text;
871 unsigned glyphID = *ptr;
872 ptr += 1;
873 *text = (const char*)ptr;
874 return cache->getGlyphIDMetrics(glyphID, x, y);
875}
876
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000877SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 static const SkDrawCacheProc gDrawCacheProcs[] = {
879 sk_getMetrics_utf8_00,
880 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000881 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000883
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 sk_getMetrics_utf8_xy,
885 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000886 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 sk_getMetrics_glyph_xy
888 };
reed@google.com72cf4922011-01-04 19:58:20 +0000889
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 unsigned index = this->getTextEncoding();
reedf59eab22014-07-14 14:39:15 -0700891 if (fBitfields.fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000892 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000893 }
reed@google.com72cf4922011-01-04 19:58:20 +0000894
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
896 return gDrawCacheProcs[index];
897}
898
899///////////////////////////////////////////////////////////////////////////////
900
reed@google.comed43dff2013-06-04 16:56:27 +0000901#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
902SkPaint::kDevKernText_Flag | \
903SkPaint::kLinearText_Flag | \
904SkPaint::kLCDRenderText_Flag | \
905SkPaint::kEmbeddedBitmapText_Flag | \
906SkPaint::kAutoHinting_Flag | \
907SkPaint::kGenA8FromLCD_Flag )
908
909SkScalar SkPaint::setupForAsPaths() {
910 uint32_t flags = this->getFlags();
911 // clear the flags we don't care about
912 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
913 // set the flags we do care about
914 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000915
reed@google.comed43dff2013-06-04 16:56:27 +0000916 this->setFlags(flags);
917 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000918
reed@google.comed43dff2013-06-04 16:56:27 +0000919 SkScalar textSize = fTextSize;
920 this->setTextSize(kCanonicalTextSizeForPaths);
921 return textSize / kCanonicalTextSizeForPaths;
922}
923
924class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925public:
reed@google.comed43dff2013-06-04 16:56:27 +0000926 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
rmistryc4b84ae2014-06-23 06:59:15 -0700927 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
reed@google.comed43dff2013-06-04 16:56:27 +0000928 SkPaint* p = fLazy.set(paint);
929 fScale = p->setupForAsPaths();
930 fPaint = p;
931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000933
reed@google.comed43dff2013-06-04 16:56:27 +0000934 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000935
reed@google.comed43dff2013-06-04 16:56:27 +0000936 /**
937 * Returns 0 if the paint was unmodified, or the scale factor need to
938 * the original textSize
939 */
940 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000941
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942private:
reed@google.comed43dff2013-06-04 16:56:27 +0000943 const SkPaint* fPaint;
944 SkScalar fScale;
945 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946};
947
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000948static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 bounds->set(SkIntToScalar(g.fLeft),
950 SkIntToScalar(g.fTop),
951 SkIntToScalar(g.fLeft + g.fWidth),
952 SkIntToScalar(g.fTop + g.fHeight));
953}
954
reed@android.come88f5512010-03-19 14:42:28 +0000955// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
956// we don't overflow along the way
957typedef int64_t Sk48Dot16;
958
reed@google.com8f4d2302013-12-17 16:44:46 +0000959static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
960 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
961}
reed@android.come88f5512010-03-19 14:42:28 +0000962
reed@google.com44da42e2011-11-10 20:04:47 +0000963static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000964 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 bounds->join(SkIntToScalar(g.fLeft) + sx,
966 SkIntToScalar(g.fTop),
967 SkIntToScalar(g.fLeft + g.fWidth) + sx,
968 SkIntToScalar(g.fTop + g.fHeight));
969}
970
reed@google.com44da42e2011-11-10 20:04:47 +0000971static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
972 SkScalar sy = Sk48Dot16ToScalar(dy);
973 bounds->join(SkIntToScalar(g.fLeft),
974 SkIntToScalar(g.fTop) + sy,
975 SkIntToScalar(g.fLeft + g.fWidth),
976 SkIntToScalar(g.fTop + g.fHeight) + sy);
977}
978
979typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
980
981// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
982static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
983 SkASSERT(0 == xyIndex || 1 == xyIndex);
984 return (&glyph.fAdvanceX)[xyIndex];
985}
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987SkScalar SkPaint::measure_text(SkGlyphCache* cache,
988 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000989 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000991 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000993 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000995 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 return 0;
997 }
998
999 SkMeasureCacheProc glyphCacheProc;
1000 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
bsalomon49f085d2014-09-05 13:34:00 -07001001 bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002
reed@google.com44da42e2011-11-10 20:04:47 +00001003 int xyIndex;
1004 JoinBoundsProc joinBoundsProc;
1005 if (this->isVerticalText()) {
1006 xyIndex = 1;
1007 joinBoundsProc = join_bounds_y;
1008 } else {
1009 xyIndex = 0;
1010 joinBoundsProc = join_bounds_x;
1011 }
1012
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 int n = 1;
1014 const char* stop = (const char*)text + byteLength;
1015 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001016 // our accumulated fixed-point advances might overflow 16.16, so we use
1017 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1018 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001019 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020
1021 SkAutoKern autokern;
1022
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001023 if (NULL == bounds) {
1024 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 int rsb;
1026 for (; text < stop; n++) {
1027 rsb = g->fRsbDelta;
1028 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001029 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001031 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001033 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 }
1035 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001036 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001038 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 int rsb;
1040 for (; text < stop; n++) {
1041 rsb = g->fRsbDelta;
1042 g = &glyphCacheProc(cache, &text);
1043 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001044 joinBoundsProc(*g, bounds, x);
1045 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001047 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 for (; text < stop; n++) {
1049 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001050 joinBoundsProc(*g, bounds, x);
1051 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 }
1053 }
1054 }
1055 SkASSERT(text == stop);
1056
1057 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001058 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059}
1060
reed99ae8812014-08-26 11:30:01 -07001061SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 const char* text = (const char*)textData;
1063 SkASSERT(text != NULL || length == 0);
1064
reed@google.comed43dff2013-06-04 16:56:27 +00001065 SkCanonicalizePaint canon(*this);
1066 const SkPaint& paint = canon.getPaint();
1067 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001068
reed99ae8812014-08-26 11:30:01 -07001069 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 SkGlyphCache* cache = autoCache.getCache();
1071
1072 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001073
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001074 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 int tempCount;
1076
reed@google.comed43dff2013-06-04 16:56:27 +00001077 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001078 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001080 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1082 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1083 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1084 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1085 }
1086 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001087 } else if (bounds) {
1088 // ensure that even if we don't measure_text we still update the bounds
1089 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 }
1091 return width;
1092}
1093
1094typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1095
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001096static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 return text < stop;
1098}
1099
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001100static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 return text > stop;
1102}
1103
1104static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001105 const char** text, size_t length,
1106 const char** stop) {
1107 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 *stop = *text + length;
1109 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001110 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 // text should point to the end of the buffer, and stop to the beginning
1112 *stop = *text;
1113 *text += length;
1114 return backward_textBufferPred;
1115 }
1116}
1117
1118size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1119 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001120 TextBufferDirection tbd) const {
1121 if (0 == length || 0 >= maxWidth) {
1122 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001124 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 return 0;
1126 }
1127
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001128 if (0 == fTextSize) {
1129 if (measuredWidth) {
1130 *measuredWidth = 0;
1131 }
1132 return length;
1133 }
1134
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 SkASSERT(textD != NULL);
1136 const char* text = (const char*)textD;
1137
reed@google.comed43dff2013-06-04 16:56:27 +00001138 SkCanonicalizePaint canon(*this);
1139 const SkPaint& paint = canon.getPaint();
1140 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141
reed@google.comed43dff2013-06-04 16:56:27 +00001142 // adjust max in case we changed the textSize in paint
1143 if (scale) {
1144 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 }
reed@google.com72cf4922011-01-04 19:58:20 +00001146
reed@google.comed43dff2013-06-04 16:56:27 +00001147 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 SkGlyphCache* cache = autoCache.getCache();
1149
reed@google.comed43dff2013-06-04 16:56:27 +00001150 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 const char* stop;
1152 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001153 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001154 // use 64bits for our accumulator, to avoid overflowing 16.16
1155 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1156 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157
1158 SkAutoKern autokern;
1159
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001160 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001162 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 const char* curr = text;
1164 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001165 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001166 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 width -= x;
1168 text = curr;
1169 break;
1170 }
1171 rsb = g.fRsbDelta;
1172 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001173 } else {
1174 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001176 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001177 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 width -= x;
1179 text = curr;
1180 break;
1181 }
1182 }
1183 }
reed@google.com72cf4922011-01-04 19:58:20 +00001184
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001185 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001186 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001187 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 *measuredWidth = scalarWidth;
1191 }
reed@google.com72cf4922011-01-04 19:58:20 +00001192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 // return the number of bytes measured
1194 return (kForward_TextBufferDirection == tbd) ?
1195 text - stop + length : stop - text + length;
1196}
1197
1198///////////////////////////////////////////////////////////////////////////////
1199
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001200static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001201 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 return false; // don't detach the cache
1203}
1204
reed@google.com90808e82013-03-19 14:44:54 +00001205static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1206 void* context) {
1207 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208}
1209
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001210SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001211 SkCanonicalizePaint canon(*this);
1212 const SkPaint& paint = canon.getPaint();
1213 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001214
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001215 SkMatrix zoomMatrix, *zoomPtr = NULL;
1216 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 zoomMatrix.setScale(zoom, zoom);
1218 zoomPtr = &zoomMatrix;
1219 }
1220
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001222 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001224 }
reed@google.com72cf4922011-01-04 19:58:20 +00001225
reed@google.comed43dff2013-06-04 16:56:27 +00001226 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001228 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1230 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1231 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1232 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1233 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001234 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1235 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1236 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1237 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001238 metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale);
1239 metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 }
1241 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1242}
1243
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001244///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001246static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 bounds->set(g.fLeft * scale,
1248 g.fTop * scale,
1249 (g.fLeft + g.fWidth) * scale,
1250 (g.fTop + g.fHeight) * scale);
1251}
1252
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001253int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1254 SkScalar widths[], SkRect bounds[]) const {
1255 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
bsalomon49f085d2014-09-05 13:34:00 -07001259 SkASSERT(textData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001261 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264
reed@google.comed43dff2013-06-04 16:56:27 +00001265 SkCanonicalizePaint canon(*this);
1266 const SkPaint& paint = canon.getPaint();
1267 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268
reed@google.comed43dff2013-06-04 16:56:27 +00001269 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 SkGlyphCache* cache = autoCache.getCache();
1271 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001272 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
bsalomon49f085d2014-09-05 13:34:00 -07001273 bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274
1275 const char* text = (const char*)textData;
1276 const char* stop = text + byteLength;
1277 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001278 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001280 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 // we adjust the widths returned here through auto-kerning
1282 SkAutoKern autokern;
1283 SkFixed prevWidth = 0;
1284
1285 if (scale) {
1286 while (text < stop) {
1287 const SkGlyph& g = glyphCacheProc(cache, &text);
1288 if (widths) {
1289 SkFixed adjust = autokern.adjust(g);
1290
1291 if (count > 0) {
1292 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1293 *widths++ = SkScalarMul(w, scale);
1294 }
reed@google.com44da42e2011-11-10 20:04:47 +00001295 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 }
1297 if (bounds) {
1298 set_bounds(g, bounds++, scale);
1299 }
1300 ++count;
1301 }
1302 if (count > 0 && widths) {
1303 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1304 }
1305 } else {
1306 while (text < stop) {
1307 const SkGlyph& g = glyphCacheProc(cache, &text);
1308 if (widths) {
1309 SkFixed adjust = autokern.adjust(g);
1310
1311 if (count > 0) {
1312 *widths++ = SkFixedToScalar(prevWidth + adjust);
1313 }
reed@google.com44da42e2011-11-10 20:04:47 +00001314 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 }
1316 if (bounds) {
1317 set_bounds(g, bounds++);
1318 }
1319 ++count;
1320 }
1321 if (count > 0 && widths) {
1322 *widths = SkFixedToScalar(prevWidth);
1323 }
1324 }
1325 } else { // no devkern
1326 if (scale) {
1327 while (text < stop) {
1328 const SkGlyph& g = glyphCacheProc(cache, &text);
1329 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001330 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 scale);
1332 }
1333 if (bounds) {
1334 set_bounds(g, bounds++, scale);
1335 }
1336 ++count;
1337 }
1338 } else {
1339 while (text < stop) {
1340 const SkGlyph& g = glyphCacheProc(cache, &text);
1341 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001342 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 }
1344 if (bounds) {
1345 set_bounds(g, bounds++);
1346 }
1347 ++count;
1348 }
1349 }
1350 }
1351
1352 SkASSERT(text == stop);
1353 return count;
1354}
1355
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001356///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357
1358#include "SkDraw.h"
1359
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001360void SkPaint::getTextPath(const void* textData, size_t length,
1361 SkScalar x, SkScalar y, SkPath* path) const {
1362 SkASSERT(length == 0 || textData != NULL);
1363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001365 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001367 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368
djsollen@google.com166e6532012-03-20 14:24:38 +00001369 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 SkMatrix matrix;
1371 SkScalar prevXPos = 0;
1372
1373 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1374 matrix.postTranslate(x, y);
1375 path->reset();
1376
1377 SkScalar xpos;
1378 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001379 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001381 if (iterPath) {
1382 path->addPath(*iterPath, matrix);
1383 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 prevXPos = xpos;
1385 }
1386}
1387
reed@google.comca0062e2012-07-20 11:20:32 +00001388void SkPaint::getPosTextPath(const void* textData, size_t length,
1389 const SkPoint pos[], SkPath* path) const {
1390 SkASSERT(length == 0 || textData != NULL);
1391
1392 const char* text = (const char*)textData;
1393 if (text == NULL || length == 0 || path == NULL) {
1394 return;
1395 }
1396
1397 SkTextToPathIter iter(text, length, *this, false);
1398 SkMatrix matrix;
1399 SkPoint prevPos;
1400 prevPos.set(0, 0);
1401
1402 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1403 path->reset();
1404
1405 unsigned int i = 0;
1406 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001407 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001408 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001409 if (iterPath) {
1410 path->addPath(*iterPath, matrix);
1411 }
reed@google.comca0062e2012-07-20 11:20:32 +00001412 prevPos = pos[i];
1413 i++;
1414 }
1415}
1416
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417static void add_flattenable(SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001418 SkWriteBuffer* buffer) {
1419 buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420}
1421
reed@google.com2739b272011-09-28 17:26:42 +00001422// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001423static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001426 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001427 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001428 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001429 }
1430
reed@google.com65dd8f82011-03-14 13:31:16 +00001431 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001432 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001433 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001434
1435 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436}
1437
reed@android.com1cdcb512009-08-24 19:11:00 +00001438// if linear-text is on, then we force hinting to be off (since that's sort of
1439// the point of linear-text.
1440static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1441 SkPaint::Hinting h = paint.getHinting();
1442 if (paint.isLinearText()) {
1443 h = SkPaint::kNo_Hinting;
1444 }
1445 return h;
1446}
1447
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001448// return true if the paint is just a single color (i.e. not a shader). If its
1449// a shader, then we can't compute a const luminance for it :(
1450static bool justAColor(const SkPaint& paint, SkColor* color) {
reed8367b8c2014-08-22 08:30:20 -07001451 SkColor c = paint.getColor();
1452
1453 SkShader* shader = paint.getShader();
1454 if (shader && !shader->asLuminanceColor(&c)) {
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001455 return false;
1456 }
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001457 if (paint.getColorFilter()) {
1458 c = paint.getColorFilter()->filterColor(c);
1459 }
1460 if (color) {
1461 *color = c;
1462 }
1463 return true;
1464}
1465
reed@google.comce6dbb62012-02-10 22:01:45 +00001466static SkColor computeLuminanceColor(const SkPaint& paint) {
1467 SkColor c;
1468 if (!justAColor(paint, &c)) {
1469 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1470 }
1471 return c;
1472}
reed@google.com813d38b2012-02-13 21:37:57 +00001473
reed@google.comdd43df92012-02-15 14:50:29 +00001474#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1475
reed@google.com4f79b9b2011-09-13 13:23:26 +00001476// Beyond this size, LCD doesn't appreciably improve quality, but it always
1477// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001478#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1479 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1480#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001481
reedb3da83a2014-10-01 12:06:12 -07001482const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1483
reed4942e752014-10-01 13:59:33 -07001484static bool too_big_for_lcd(const SkScalerContext::Rec& rec, bool checkPost2x2) {
reedb3da83a2014-10-01 12:06:12 -07001485 if (checkPost2x2) {
1486 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1487 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
reed4942e752014-10-01 13:59:33 -07001488 area *= rec.fTextSize * rec.fTextSize;
1489 return area > gMaxSize2ForLCDText;
1490 } else {
1491 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
reedb3da83a2014-10-01 12:06:12 -07001492 }
reed@google.com4f79b9b2011-09-13 13:23:26 +00001493}
1494
reed@google.com72cf4922011-01-04 19:58:20 +00001495/*
1496 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1497 * that vary only slightly when we create our key into the font cache, since the font scaler
1498 * typically returns the same looking resuts for tiny changes in the matrix.
1499 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001500static SkScalar sk_relax(SkScalar x) {
1501 int n = sk_float_round2int(x * 1024);
1502 return n / 1024.0f;
1503}
1504
reed@android.com36a4c2a2009-07-22 19:52:11 +00001505void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001506 const SkDeviceProperties* deviceProperties,
1507 const SkMatrix* deviceMatrix,
1508 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001509 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001511 SkTypeface* typeface = paint.getTypeface();
1512 if (NULL == typeface) {
1513 typeface = SkTypeface::GetDefaultTypeface();
1514 }
bungemanec730b92014-08-18 07:57:35 -07001515 rec->fFontID = typeface->uniqueID();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 rec->fTextSize = paint.getTextSize();
1517 rec->fPreScaleX = paint.getTextScaleX();
1518 rec->fPreSkewX = paint.getTextSkewX();
1519
reedb3da83a2014-10-01 12:06:12 -07001520 bool checkPost2x2 = false;
1521
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001522 if (deviceMatrix) {
reedb3da83a2014-10-01 12:06:12 -07001523 const SkMatrix::TypeMask mask = deviceMatrix->getType();
1524 if (mask & SkMatrix::kScale_Mask) {
1525 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1526 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
1527 checkPost2x2 = true;
1528 } else {
1529 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1530 }
1531 if (mask & SkMatrix::kAffine_Mask) {
1532 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1533 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1534 checkPost2x2 = true;
1535 } else {
1536 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1537 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001538 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1540 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1541 }
reed@google.com72cf4922011-01-04 19:58:20 +00001542
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543 SkPaint::Style style = paint.getStyle();
1544 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001545
reed@google.comffe49f52011-11-22 19:42:41 +00001546 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001547
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001548 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001549#ifdef SK_USE_FREETYPE_EMBOLDEN
1550 flags |= SkScalerContext::kEmbolden_Flag;
1551#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001552 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1553 kStdFakeBoldInterpKeys,
1554 kStdFakeBoldInterpValues,
1555 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001557
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001558 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 style = SkPaint::kStrokeAndFill_Style;
1560 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001561 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001563 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001564#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565 }
1566
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001567 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001569 }
reed@google.com72cf4922011-01-04 19:58:20 +00001570
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001571 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572 rec->fFrameWidth = strokeWidth;
1573 rec->fMiterLimit = paint.getStrokeMiter();
1574 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1575
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001576 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001578 }
1579 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 rec->fFrameWidth = 0;
1581 rec->fMiterLimit = 0;
1582 rec->fStrokeJoin = 0;
1583 }
1584
reed@google.com02b53312011-05-18 19:00:53 +00001585 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1586
bungeman@google.com532470f2013-01-22 19:25:14 +00001587 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
reed4942e752014-10-01 13:59:33 -07001588 if (too_big_for_lcd(*rec, checkPost2x2)) {
reed@google.com02b53312011-05-18 19:00:53 +00001589 rec->fMaskFormat = SkMask::kA8_Format;
reed4a8126e2014-09-22 07:29:03 -07001590 flags |= SkScalerContext::kGenA8FromLCD_Flag;
reed@google.com02b53312011-05-18 19:00:53 +00001591 } else {
reed4a8126e2014-09-22 07:29:03 -07001592 SkPixelGeometry geometry = deviceProperties
1593 ? deviceProperties->fPixelGeometry
1594 : SkSurfacePropsDefaultPixelGeometry();
1595 switch (geometry) {
1596 case kUnknown_SkPixelGeometry:
1597 // eeek, can't support LCD
1598 rec->fMaskFormat = SkMask::kA8_Format;
1599 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1600 break;
1601 case kRGB_H_SkPixelGeometry:
1602 // our default, do nothing.
1603 break;
1604 case kBGR_H_SkPixelGeometry:
1605 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1606 break;
1607 case kRGB_V_SkPixelGeometry:
1608 flags |= SkScalerContext::kLCD_Vertical_Flag;
1609 break;
1610 case kBGR_V_SkPixelGeometry:
1611 flags |= SkScalerContext::kLCD_Vertical_Flag;
1612 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1613 break;
reed@google.com02b53312011-05-18 19:00:53 +00001614 }
1615 }
1616 }
1617
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001618 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001619 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001620 }
1621 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001622 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001623 }
1624 if (paint.isAutohinted()) {
bungeman@google.comf6f56872014-01-23 19:01:36 +00001625 flags |= SkScalerContext::kForceAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001626 }
reed@google.com830a23e2011-11-10 15:20:49 +00001627 if (paint.isVerticalText()) {
1628 flags |= SkScalerContext::kVertical_Flag;
1629 }
reed@google.com8351aab2012-01-18 17:06:35 +00001630 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1631 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1632 }
reed@google.com02b53312011-05-18 19:00:53 +00001633 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001634
reed@google.comffe49f52011-11-22 19:42:41 +00001635 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001636 rec->setHinting(computeHinting(paint));
1637
bungeman@google.com97efada2012-07-30 20:40:50 +00001638 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001639
1640 if (NULL == deviceProperties) {
1641 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1642 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1643 } else {
reed4a8126e2014-09-22 07:29:03 -07001644 rec->setDeviceGamma(deviceProperties->getGamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001645
1646 //For now always set the paint gamma equal to the device gamma.
1647 //The math in SkMaskGamma can handle them being different,
1648 //but it requires superluminous masks when
1649 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
reed4a8126e2014-09-22 07:29:03 -07001650 rec->setPaintGamma(deviceProperties->getGamma());
bungeman@google.com532470f2013-01-22 19:25:14 +00001651 }
1652
1653#ifdef SK_GAMMA_CONTRAST
1654 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001655#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001656 /**
1657 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1658 * With lower values small text appears washed out (though correctly so).
1659 * With higher values lcd fringing is worse and the smoothing effect of
1660 * partial coverage is diminished.
1661 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001662 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001663#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001664
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001665 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001666
reed@android.com36a4c2a2009-07-22 19:52:11 +00001667 /* Allow the fonthost to modify our rec before we use it as a key into the
1668 cache. This way if we're asking for something that they will ignore,
1669 they can modify our rec up front, so we don't create duplicate cache
1670 entries.
1671 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001672 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001673
reed@google.com10d2d4d2012-03-01 22:32:51 +00001674 // be sure to call PostMakeRec(rec) before you actually use it!
1675}
1676
1677/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001678 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1679 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1680 * to hold it until the returned pointer is refed or forgotten.
1681 */
1682SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1683
bungeman@google.comae30f562012-09-11 18:44:55 +00001684static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001685static SkMaskGamma* gMaskGamma = NULL;
1686static SkScalar gContrast = SK_ScalarMin;
1687static SkScalar gPaintGamma = SK_ScalarMin;
1688static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001689/**
1690 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1691 * the returned SkMaskGamma pointer is refed or forgotten.
1692 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001693static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
mtkleinb83f6c32014-06-09 14:18:02 -07001694 gMaskGammaCacheMutex.assertHeld();
bungeman@google.comae30f562012-09-11 18:44:55 +00001695 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1696 if (NULL == gLinearMaskGamma) {
1697 gLinearMaskGamma = SkNEW(SkMaskGamma);
1698 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001699 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001700 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001701 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1702 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001703 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001704 gContrast = contrast;
1705 gPaintGamma = paintGamma;
1706 gDeviceGamma = deviceGamma;
1707 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001708 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001709}
1710
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001711/*static*/ void SkPaint::Term() {
1712 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1713
bungeman@google.comae30f562012-09-11 18:44:55 +00001714 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001715 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001716 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001717 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001718 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1719 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1720 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001721}
1722
bungeman@google.com97efada2012-07-30 20:40:50 +00001723/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001724 * We ensure that the rec is self-consistent and efficient (where possible)
1725 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001726void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001727 /**
1728 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001729 * limits the number of unique entries, and the scaler will only look at
1730 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001731 */
reed@google.comdd43df92012-02-15 14:50:29 +00001732 switch (rec->fMaskFormat) {
1733 case SkMask::kLCD16_Format:
1734 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001735 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001736 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001737 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001738 break;
1739 }
1740 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001741 // filter down the luminance to a single component, since A8 can't
1742 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001743 SkColor color = rec->getLuminanceColor();
jvanverthdc1cf662014-07-01 13:42:59 -07001744 U8CPU lum = SkComputeLuminance(SkColorGetR(color),
1745 SkColorGetG(color),
1746 SkColorGetB(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001747 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001748 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001749 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001750 break;
1751 }
1752 case SkMask::kBW_Format:
1753 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001754 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001755 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001756 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757}
1758
1759#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1760
reed@google.com17fb3872011-05-04 14:31:07 +00001761#ifdef SK_DEBUG
1762 #define TEST_DESC
1763#endif
1764
reed@google.comffe49f52011-11-22 19:42:41 +00001765/*
1766 * ignoreGamma tells us that the caller just wants metrics that are unaffected
jvanverth2d2a68c2014-06-10 06:42:56 -07001767 * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
1768 * contrast = 0, luminanceColor = transparent black.
reed@google.comffe49f52011-11-22 19:42:41 +00001769 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001770void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1771 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001772 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001773 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 SkScalerContext::Rec rec;
1775
bungeman@google.com532470f2013-01-22 19:25:14 +00001776 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001777 if (ignoreGamma) {
jvanverth2d2a68c2014-06-10 06:42:56 -07001778 rec.ignorePreBlend();
djsollen@google.com57f49692011-02-23 20:46:31 +00001779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780
1781 size_t descSize = sizeof(rec);
1782 int entryCount = 1;
1783 SkPathEffect* pe = this->getPathEffect();
1784 SkMaskFilter* mf = this->getMaskFilter();
1785 SkRasterizer* ra = this->getRasterizer();
1786
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001787 SkWriteBuffer peBuffer, mfBuffer, raBuffer;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788
1789 if (pe) {
1790 peBuffer.writeFlattenable(pe);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001791 descSize += peBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 entryCount += 1;
1793 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1794 // seems like we could support kLCD as well at this point...
1795 }
1796 if (mf) {
1797 mfBuffer.writeFlattenable(mf);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001798 descSize += mfBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799 entryCount += 1;
1800 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001801 /* Pre-blend is not currently applied to filtered text.
1802 The primary filter is blur, for which contrast makes no sense,
1803 and for which the destination guess error is more visible.
1804 Also, all existing users of blur have calibrated for linear. */
1805 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 }
1807 if (ra) {
1808 raBuffer.writeFlattenable(ra);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00001809 descSize += raBuffer.bytesWritten();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 entryCount += 1;
1811 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1812 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001813
1814 ///////////////////////////////////////////////////////////////////////////
1815 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001816 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001817
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 descSize += SkDescriptor::ComputeOverhead(entryCount);
1819
1820 SkAutoDescriptor ad(descSize);
1821 SkDescriptor* desc = ad.getDesc();
1822
1823 desc->init();
1824 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1825
1826 if (pe) {
1827 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1828 }
1829 if (mf) {
1830 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1831 }
1832 if (ra) {
1833 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1834 }
1835
1836 SkASSERT(descSize == desc->getLength());
1837 desc->computeChecksum();
1838
reed@google.com17fb3872011-05-04 14:31:07 +00001839#ifdef TEST_DESC
1840 {
1841 // Check that we completely write the bytes in desc (our key), and that
1842 // there are no uninitialized bytes. If there were, then we would get
1843 // false-misses (or worse, false-hits) in our fontcache.
1844 //
1845 // We do this buy filling 2 others, one with 0s and the other with 1s
1846 // and create those, and then check that all 3 are identical.
1847 SkAutoDescriptor ad1(descSize);
1848 SkAutoDescriptor ad2(descSize);
1849 SkDescriptor* desc1 = ad1.getDesc();
1850 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001851
reed@google.com17fb3872011-05-04 14:31:07 +00001852 memset(desc1, 0x00, descSize);
1853 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001854
reed@google.com17fb3872011-05-04 14:31:07 +00001855 desc1->init();
1856 desc2->init();
1857 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1858 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001859
reed@google.com17fb3872011-05-04 14:31:07 +00001860 if (pe) {
1861 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1862 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1863 }
1864 if (mf) {
1865 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1866 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1867 }
1868 if (ra) {
1869 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1870 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1871 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001872
reed@google.com17fb3872011-05-04 14:31:07 +00001873 SkASSERT(descSize == desc1->getLength());
1874 SkASSERT(descSize == desc2->getLength());
1875 desc1->computeChecksum();
1876 desc2->computeChecksum();
1877 SkASSERT(!memcmp(desc, desc1, descSize));
1878 SkASSERT(!memcmp(desc, desc2, descSize));
1879 }
1880#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001881
reed@google.com90808e82013-03-19 14:44:54 +00001882 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883}
1884
bungeman@google.com532470f2013-01-22 19:25:14 +00001885SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
jvanverth2d2a68c2014-06-10 06:42:56 -07001886 const SkMatrix* deviceMatrix,
1887 bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 SkGlyphCache* cache;
jvanverth2d2a68c2014-06-10 06:42:56 -07001889 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 return cache;
1891}
1892
bungeman@google.com97efada2012-07-30 20:40:50 +00001893/**
1894 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1895 */
1896//static
1897SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1898 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001899 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1900 rec.getPaintGamma(),
1901 rec.getDeviceGamma());
1902 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001903}
1904
jvanverth2d2a68c2014-06-10 06:42:56 -07001905size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma,
1906 SkScalar deviceGamma, int* width, int* height) {
1907 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1908 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1909 paintGamma,
1910 deviceGamma);
1911
1912 maskGamma.getGammaTableDimensions(width, height);
1913 size_t size = (*width)*(*height)*sizeof(uint8_t);
1914
1915 return size;
1916}
1917
1918void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
1919 void* data) {
1920 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1921 const SkMaskGamma& maskGamma = cachedMaskGamma(contrast,
1922 paintGamma,
1923 deviceGamma);
1924 int width, height;
1925 maskGamma.getGammaTableDimensions(&width, &height);
1926 size_t size = width*height*sizeof(uint8_t);
1927 const uint8_t* gammaTables = maskGamma.getGammaTables();
1928 memcpy(data, gammaTables, size);
1929}
1930
1931
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932///////////////////////////////////////////////////////////////////////////////
1933
1934#include "SkStream.h"
1935
reed@android.comaefd2bc2009-03-30 21:02:14 +00001936static uintptr_t asint(const void* p) {
1937 return reinterpret_cast<uintptr_t>(p);
1938}
1939
1940union Scalar32 {
1941 SkScalar fScalar;
1942 uint32_t f32;
1943};
1944
1945static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1946 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1947 Scalar32 tmp;
1948 tmp.fScalar = value;
1949 *ptr = tmp.f32;
1950 return ptr + 1;
1951}
1952
1953static SkScalar read_scalar(const uint32_t*& ptr) {
1954 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1955 Scalar32 tmp;
1956 tmp.f32 = *ptr++;
1957 return tmp.fScalar;
1958}
1959
1960static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1961 SkASSERT(a == (uint8_t)a);
1962 SkASSERT(b == (uint8_t)b);
1963 SkASSERT(c == (uint8_t)c);
1964 SkASSERT(d == (uint8_t)d);
1965 return (a << 24) | (b << 16) | (c << 8) | d;
1966}
1967
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001968#ifdef SK_DEBUG
1969 static void ASSERT_FITS_IN(uint32_t value, int bitCount) {
1970 SkASSERT(bitCount > 0 && bitCount <= 32);
1971 uint32_t mask = ~0U;
1972 mask >>= (32 - bitCount);
1973 SkASSERT(0 == (value & ~mask));
1974 }
1975#else
1976 #define ASSERT_FITS_IN(value, bitcount)
1977#endif
1978
reed@android.comaefd2bc2009-03-30 21:02:14 +00001979enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00001980 kHasTypeface_FlatFlag = 0x01,
1981 kHasEffects_FlatFlag = 0x02,
1982 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00001983
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001984 kFlatFlagMask = 0x7,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001985};
1986
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00001987enum BitsPerField {
1988 kFlags_BPF = 16,
1989 kHint_BPF = 2,
1990 kAlign_BPF = 2,
1991 kFilter_BPF = 2,
1992 kFlatFlags_BPF = 3,
1993};
1994
1995static inline int BPF_Mask(int bits) {
1996 return (1 << bits) - 1;
1997}
1998
1999static uint32_t pack_paint_flags(unsigned flags, unsigned hint, unsigned align,
2000 unsigned filter, unsigned flatFlags) {
2001 ASSERT_FITS_IN(flags, kFlags_BPF);
2002 ASSERT_FITS_IN(hint, kHint_BPF);
2003 ASSERT_FITS_IN(align, kAlign_BPF);
2004 ASSERT_FITS_IN(filter, kFilter_BPF);
2005 ASSERT_FITS_IN(flatFlags, kFlatFlags_BPF);
2006
2007 // left-align the fields of "known" size, and right-align the last (flatFlags) so it can easly
2008 // add more bits in the future.
2009 return (flags << 16) | (hint << 14) | (align << 12) | (filter << 10) | flatFlags;
2010}
2011
2012static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed) {
2013 paint->setFlags(packed >> 16);
2014 paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
2015 paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
2016 paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
2017 return (FlatFlags)(packed & kFlatFlagMask);
2018}
2019
2020// V22_COMPATIBILITY_CODE
2021static FlatFlags unpack_paint_flags_v22(SkPaint* paint, uint32_t packed) {
2022 enum {
2023 kFilterBitmap_Flag = 0x02,
2024 kHighQualityFilterBitmap_Flag = 0x4000,
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002025
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002026 kAll_Flags = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag
2027 };
2028
2029 // previously flags:16, textAlign:8, flatFlags:8
2030 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2031 unsigned flags = packed >> 16;
2032 int filter = 0;
2033 if (flags & kFilterBitmap_Flag) {
2034 filter |= 1;
2035 }
2036 if (flags & kHighQualityFilterBitmap_Flag) {
2037 filter |= 2;
2038 }
2039 paint->setFilterLevel((SkPaint::FilterLevel)filter);
2040 flags &= ~kAll_Flags; // remove these (now dead) bit flags
2041
2042 paint->setFlags(flags);
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +00002043
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002044 // hinting added later. 0 in this nibble means use the default.
2045 uint32_t hinting = (packed >> 12) & 0xF;
2046 paint->setHinting(0 == hinting ? SkPaint::kNormal_Hinting : static_cast<SkPaint::Hinting>(hinting-1));
2047 paint->setTextAlign(static_cast<SkPaint::Align>((packed >> 8) & 0xF));
2048 return (FlatFlags)(packed & kFlatFlagMask);
2049}
2050
reed@android.comaefd2bc2009-03-30 21:02:14 +00002051// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00002052static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00002053 1 * sizeof(SkColor) +
2054 1 * sizeof(uint16_t) +
2055 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002056
2057/* To save space/time, we analyze the paint, and write a truncated version of
2058 it if there are not tricky elements like shaders, etc.
2059 */
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002060void SkPaint::flatten(SkWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002061 uint8_t flatFlags = 0;
2062 if (this->getTypeface()) {
2063 flatFlags |= kHasTypeface_FlatFlag;
2064 }
2065 if (asint(this->getPathEffect()) |
2066 asint(this->getShader()) |
2067 asint(this->getXfermode()) |
2068 asint(this->getMaskFilter()) |
2069 asint(this->getColorFilter()) |
2070 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002071 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002072 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002073 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002074 flatFlags |= kHasEffects_FlatFlag;
2075 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002076
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002077 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2078 uint32_t* ptr = buffer.reserve(kPODPaintSize);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002079
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002080 ptr = write_scalar(ptr, this->getTextSize());
2081 ptr = write_scalar(ptr, this->getTextScaleX());
2082 ptr = write_scalar(ptr, this->getTextSkewX());
2083 ptr = write_scalar(ptr, this->getStrokeWidth());
2084 ptr = write_scalar(ptr, this->getStrokeMiter());
2085 *ptr++ = this->getColor();
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002086
2087 *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
2088 this->getFilterLevel(), flatFlags);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002089 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2090 this->getStyle(), this->getTextEncoding());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002091
2092 // now we're done with ptr and the (pre)reserved space. If we need to write
2093 // additional fields, use the buffer directly
2094 if (flatFlags & kHasTypeface_FlatFlag) {
2095 buffer.writeTypeface(this->getTypeface());
2096 }
2097 if (flatFlags & kHasEffects_FlatFlag) {
2098 buffer.writeFlattenable(this->getPathEffect());
2099 buffer.writeFlattenable(this->getShader());
2100 buffer.writeFlattenable(this->getXfermode());
2101 buffer.writeFlattenable(this->getMaskFilter());
2102 buffer.writeFlattenable(this->getColorFilter());
2103 buffer.writeFlattenable(this->getRasterizer());
2104 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002105 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00002106
2107 if (fAnnotation) {
2108 buffer.writeBool(true);
2109 fAnnotation->writeToBuffer(buffer);
2110 } else {
2111 buffer.writeBool(false);
2112 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114}
2115
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002116void SkPaint::unflatten(SkReadBuffer& buffer) {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002117 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2118 const void* podData = buffer.skip(kPODPaintSize);
2119 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002120
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002121 // the order we read must match the order we wrote in flatten()
2122 this->setTextSize(read_scalar(pod));
2123 this->setTextScaleX(read_scalar(pod));
2124 this->setTextSkewX(read_scalar(pod));
2125 this->setStrokeWidth(read_scalar(pod));
2126 this->setStrokeMiter(read_scalar(pod));
2127 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002128
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002129 unsigned flatFlags = 0;
commit-bot@chromium.org7ed173b2014-05-20 17:31:08 +00002130 if (buffer.isVersionLT(SkReadBuffer::kFilterLevelIsEnum_Version)) {
commit-bot@chromium.org85faf502014-04-16 12:58:02 +00002131 flatFlags = unpack_paint_flags_v22(this, *pod++);
2132 } else {
2133 flatFlags = unpack_paint_flags(this, *pod++);
2134 }
2135
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002136 uint32_t tmp = *pod++;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +00002137 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2138 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2139 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2140 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002141
2142 if (flatFlags & kHasTypeface_FlatFlag) {
2143 this->setTypeface(buffer.readTypeface());
2144 } else {
2145 this->setTypeface(NULL);
2146 }
2147
2148 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002149 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2150 SkSafeUnref(this->setShader(buffer.readShader()));
2151 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2152 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2153 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2154 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2155 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2156 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002157
reed@google.com0cd2ac62013-10-14 20:02:44 +00002158 if (buffer.readBool()) {
commit-bot@chromium.orgd9579842014-02-27 11:47:36 +00002159 this->setAnnotation(SkAnnotation::Create(buffer))->unref();
reed@google.com0cd2ac62013-10-14 20:02:44 +00002160 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002161 } else {
2162 this->setPathEffect(NULL);
2163 this->setShader(NULL);
2164 this->setXfermode(NULL);
2165 this->setMaskFilter(NULL);
2166 this->setColorFilter(NULL);
2167 this->setRasterizer(NULL);
2168 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002169 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002170 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002171
djsollen3b625542014-08-14 06:29:02 -07002172 if (buffer.isVersionLT(SkReadBuffer::kRemoveAndroidPaintOpts_Version) &&
2173 flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2174 SkString tag;
2175 buffer.readUInt();
2176 buffer.readString(&tag);
2177 buffer.readBool();
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002178 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179}
2180
2181///////////////////////////////////////////////////////////////////////////////
2182
reed@google.com82065d62011-02-07 15:30:46 +00002183SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002184 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 SkRefCnt_SafeAssign(fShader, shader);
2186 return shader;
2187}
2188
reed@google.com82065d62011-02-07 15:30:46 +00002189SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002190 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 SkRefCnt_SafeAssign(fColorFilter, filter);
2192 return filter;
2193}
2194
reed@google.com82065d62011-02-07 15:30:46 +00002195SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002196 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 SkRefCnt_SafeAssign(fXfermode, mode);
2198 return mode;
2199}
2200
reed@android.com0baf1932009-06-24 12:41:42 +00002201SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002202 SkSafeUnref(fXfermode);
2203 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002204 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002205 return fXfermode;
2206}
2207
reed@google.com82065d62011-02-07 15:30:46 +00002208SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002209 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 SkRefCnt_SafeAssign(fPathEffect, effect);
2211 return effect;
2212}
2213
reed@google.com82065d62011-02-07 15:30:46 +00002214SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002215 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 SkRefCnt_SafeAssign(fMaskFilter, filter);
2217 return filter;
2218}
2219
reed@google.com82065d62011-02-07 15:30:46 +00002220///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221
reed@google.com4bbdeac2013-01-24 21:03:11 +00002222bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2223 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002224 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225
reed@google.comfd4be262012-05-25 01:04:12 +00002226 const SkPath* srcPtr = &src;
2227 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002228
reed@google.com4bbdeac2013-01-24 21:03:11 +00002229 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002230 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231 }
2232
reed@google.comfd4be262012-05-25 01:04:12 +00002233 if (!rec.applyToPath(dst, *srcPtr)) {
2234 if (srcPtr == &tmpPath) {
2235 // If path's were copy-on-write, this trick would not be needed.
2236 // As it is, we want to save making a deep-copy from tmpPath -> dst
2237 // since we know we're just going to delete tmpPath when we return,
2238 // so the swap saves that copy.
2239 dst->swap(tmpPath);
2240 } else {
2241 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 }
2243 }
reed@google.comfd4be262012-05-25 01:04:12 +00002244 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245}
2246
reed@google.come4f10a72012-05-15 20:47:50 +00002247const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002248 SkRect* storage,
2249 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002251
reed@google.come4f10a72012-05-15 20:47:50 +00002252 const SkRect* src = &origSrc;
2253
reed@google.com9efd9a02012-01-30 15:41:43 +00002254 if (this->getLooper()) {
2255 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002256 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002257 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002258 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002259
reed@google.come4f10a72012-05-15 20:47:50 +00002260 SkRect tmpSrc;
2261 if (this->getPathEffect()) {
2262 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2263 src = &tmpSrc;
2264 }
2265
reed@google.coma584aed2012-05-16 14:06:02 +00002266 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002267 // since we're stroked, outset the rect by the radius (and join type)
2268 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2269 if (0 == radius) { // hairline
2270 radius = SK_Scalar1;
2271 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2272 SkScalar scale = this->getStrokeMiter();
2273 if (scale > SK_Scalar1) {
2274 radius = SkScalarMul(radius, scale);
2275 }
2276 }
reed@google.come4f10a72012-05-15 20:47:50 +00002277 storage->set(src->fLeft - radius, src->fTop - radius,
2278 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002279 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002280 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002281 }
2282
reed@google.com9efd9a02012-01-30 15:41:43 +00002283 if (this->getMaskFilter()) {
2284 this->getMaskFilter()->computeFastBounds(*storage, storage);
2285 }
2286
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +00002287 if (this->getImageFilter()) {
2288 this->getImageFilter()->computeFastBounds(*storage, storage);
2289 }
2290
reed@android.comd252db02009-04-01 18:31:44 +00002291 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292}
2293
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00002294#ifndef SK_IGNORE_TO_STRING
reed83658302014-09-12 08:49:54 -07002295
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002296void SkPaint::toString(SkString* str) const {
2297 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2298
2299 SkTypeface* typeface = this->getTypeface();
bsalomon49f085d2014-09-05 13:34:00 -07002300 if (typeface) {
bungemand71b7572014-09-18 10:55:32 -07002301 SkDynamicMemoryWStream ostream;
2302 typeface->serialize(&ostream);
2303 SkAutoTUnref<SkStreamAsset> istream(ostream.detachAsStream());
2304 SkFontDescriptor descriptor(istream);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002305
2306 str->append("<dt>Font Family Name:</dt><dd>");
2307 str->append(descriptor.getFamilyName());
2308 str->append("</dd><dt>Font Full Name:</dt><dd>");
2309 str->append(descriptor.getFullName());
2310 str->append("</dd><dt>Font PS Name:</dt><dd>");
2311 str->append(descriptor.getPostscriptName());
2312 str->append("</dd><dt>Font File Name:</dt><dd>");
2313 str->append(descriptor.getFontFileName());
2314 str->append("</dd>");
2315 }
2316
2317 str->append("<dt>TextSize:</dt><dd>");
2318 str->appendScalar(this->getTextSize());
2319 str->append("</dd>");
2320
2321 str->append("<dt>TextScaleX:</dt><dd>");
2322 str->appendScalar(this->getTextScaleX());
2323 str->append("</dd>");
2324
2325 str->append("<dt>TextSkewX:</dt><dd>");
2326 str->appendScalar(this->getTextSkewX());
2327 str->append("</dd>");
2328
2329 SkPathEffect* pathEffect = this->getPathEffect();
bsalomon49f085d2014-09-05 13:34:00 -07002330 if (pathEffect) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002331 str->append("<dt>PathEffect:</dt><dd>");
2332 str->append("</dd>");
2333 }
2334
2335 SkShader* shader = this->getShader();
bsalomon49f085d2014-09-05 13:34:00 -07002336 if (shader) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002337 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002338 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002339 str->append("</dd>");
2340 }
2341
2342 SkXfermode* xfer = this->getXfermode();
bsalomon49f085d2014-09-05 13:34:00 -07002343 if (xfer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002344 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002345 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002346 str->append("</dd>");
2347 }
2348
2349 SkMaskFilter* maskFilter = this->getMaskFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002350 if (maskFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002351 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002352 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002353 str->append("</dd>");
2354 }
2355
2356 SkColorFilter* colorFilter = this->getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002357 if (colorFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002358 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002359 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002360 str->append("</dd>");
2361 }
2362
2363 SkRasterizer* rasterizer = this->getRasterizer();
bsalomon49f085d2014-09-05 13:34:00 -07002364 if (rasterizer) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002365 str->append("<dt>Rasterizer:</dt><dd>");
2366 str->append("</dd>");
2367 }
2368
2369 SkDrawLooper* looper = this->getLooper();
bsalomon49f085d2014-09-05 13:34:00 -07002370 if (looper) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002371 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002372 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002373 str->append("</dd>");
2374 }
2375
2376 SkImageFilter* imageFilter = this->getImageFilter();
bsalomon49f085d2014-09-05 13:34:00 -07002377 if (imageFilter) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002378 str->append("<dt>ImageFilter:</dt><dd>");
2379 str->append("</dd>");
2380 }
2381
2382 SkAnnotation* annotation = this->getAnnotation();
bsalomon49f085d2014-09-05 13:34:00 -07002383 if (annotation) {
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002384 str->append("<dt>Annotation:</dt><dd>");
2385 str->append("</dd>");
2386 }
2387
2388 str->append("<dt>Color:</dt><dd>0x");
2389 SkColor color = this->getColor();
2390 str->appendHex(color);
2391 str->append("</dd>");
2392
2393 str->append("<dt>Stroke Width:</dt><dd>");
2394 str->appendScalar(this->getStrokeWidth());
2395 str->append("</dd>");
2396
2397 str->append("<dt>Stroke Miter:</dt><dd>");
2398 str->appendScalar(this->getStrokeMiter());
2399 str->append("</dd>");
2400
2401 str->append("<dt>Flags:</dt><dd>(");
2402 if (this->getFlags()) {
2403 bool needSeparator = false;
2404 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002405 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2406 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2407 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2408 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2409 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2410 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2411 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2412 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2413 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2414 "EmbeddedBitmapText", &needSeparator);
2415 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2416 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2417 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2418 "GenA8FromLCD", &needSeparator);
2419 } else {
2420 str->append("None");
2421 }
2422 str->append(")</dd>");
2423
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002424 str->append("<dt>FilterLevel:</dt><dd>");
2425 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2426 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2427 str->append("</dd>");
2428
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002429 str->append("<dt>TextAlign:</dt><dd>");
2430 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2431 str->append(gTextAlignStrings[this->getTextAlign()]);
2432 str->append("</dd>");
2433
2434 str->append("<dt>CapType:</dt><dd>");
2435 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2436 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2437 str->append("</dd>");
2438
2439 str->append("<dt>JoinType:</dt><dd>");
2440 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2441 str->append(gJoinStrings[this->getStrokeJoin()]);
2442 str->append("</dd>");
2443
2444 str->append("<dt>Style:</dt><dd>");
2445 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2446 str->append(gStyleStrings[this->getStyle()]);
2447 str->append("</dd>");
2448
2449 str->append("<dt>TextEncoding:</dt><dd>");
2450 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2451 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2452 str->append("</dd>");
2453
2454 str->append("<dt>Hinting:</dt><dd>");
2455 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2456 str->append(gHintingStrings[this->getHinting()]);
2457 str->append("</dd>");
2458
2459 str->append("</dd></dl></dl>");
2460}
2461#endif
2462
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002463///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002464
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002465static bool has_thick_frame(const SkPaint& paint) {
2466 return paint.getStrokeWidth() > 0 &&
2467 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468}
2469
2470SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2471 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002472 bool applyStrokeAndPathEffects)
2473 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2475 true);
2476
djsollen@google.com166e6532012-03-20 14:24:38 +00002477 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2479
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002480 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002481 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002482 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002483
djsollen@google.com166e6532012-03-20 14:24:38 +00002484 // can't use our canonical size if we need to apply patheffects
2485 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2487 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002488 if (has_thick_frame(fPaint)) {
2489 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2490 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002491 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002493 }
reed@google.com72cf4922011-01-04 19:58:20 +00002494
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002495 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496 fPaint.setStyle(SkPaint::kFill_Style);
2497 fPaint.setPathEffect(NULL);
2498 }
2499
jvanverth2d2a68c2014-06-10 06:42:56 -07002500 fCache = fPaint.detachCache(NULL, NULL, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002501
2502 SkPaint::Style style = SkPaint::kFill_Style;
2503 SkPathEffect* pe = NULL;
2504
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002505 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002506 style = paint.getStyle(); // restore
2507 pe = paint.getPathEffect(); // restore
2508 }
2509 fPaint.setStyle(style);
2510 fPaint.setPathEffect(pe);
2511 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2512
2513 // now compute fXOffset if needed
2514
2515 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002516 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002518 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2519 &count, NULL), fScale);
2520 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002522 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002523 xOffset = -width;
2524 }
2525 fXPos = xOffset;
2526 fPrevAdvance = 0;
2527
2528 fText = text;
2529 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002530
reed@google.com44da42e2011-11-10 20:04:47 +00002531 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002532}
2533
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002534SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535 SkGlyphCache::AttachCache(fCache);
2536}
2537
reed@google.com7b4531f2012-08-07 15:53:00 +00002538bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2539 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002540 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2541
2542 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002543 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002544
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002545 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002546 if (path) {
2547 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002548 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002549 } else {
2550 if (path) {
2551 *path = NULL;
2552 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002553 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002554 if (xpos) {
2555 *xpos = fXPos;
2556 }
2557 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002558 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002559 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002560}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002561
2562///////////////////////////////////////////////////////////////////////////////
2563
2564bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002565 if (fLooper) {
2566 return false;
2567 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002568 SkXfermode::Mode mode;
2569 if (SkXfermode::AsMode(fXfermode, &mode)) {
2570 switch (mode) {
2571 case SkXfermode::kSrcOver_Mode:
2572 case SkXfermode::kSrcATop_Mode:
2573 case SkXfermode::kDstOut_Mode:
2574 case SkXfermode::kDstOver_Mode:
2575 case SkXfermode::kPlus_Mode:
2576 return 0 == this->getAlpha();
2577 case SkXfermode::kDst_Mode:
2578 return true;
2579 default:
2580 break;
2581 }
2582 }
2583 return false;
2584}
commit-bot@chromium.orgaca1c012014-02-21 18:18:05 +00002585