blob: 91a76e151fbb86ce6cbcce42348b153a6b916335 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkPaint.h"
reed@google.comb0a34d82012-07-11 19:57:55 +000010#include "SkAnnotation.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000011#include "SkAutoKern.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColorFilter.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000013#include "SkData.h"
bungeman@google.com532470f2013-01-22 19:25:14 +000014#include "SkDeviceProperties.h"
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"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000021#include "SkOrderedReadBuffer.h"
22#include "SkOrderedWriteBuffer.h"
23#include "SkPaintDefaults.h"
commit-bot@chromium.org97f81672013-09-26 15:16:12 +000024#include "SkPaintOptionsAndroid.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkPathEffect.h"
26#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000027#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000029#include "SkShader.h"
30#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000032#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000033#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000034#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000035#include "SkTypeface.h"
36#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000037
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
reed@google.coma3237872011-07-05 19:20:48 +000039// define this to get a printf for out-of-range parameter in setters
40// e.g. setTextSize(-1)
41//#define SK_REPORT_API_RANGE_CHECK
42
djsollen@google.com56c69772011-11-08 19:00:26 +000043#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000044#define GEN_ID_INC fGenerationID++
45#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
46#else
47#define GEN_ID_INC
48#define GEN_ID_INC_EVAL(expression)
49#endif
50
reed@android.coma3122b92009-08-13 20:38:25 +000051SkPaint::SkPaint() {
52 // since we may have padding, we zero everything so that our memcmp() call
53 // in operator== will work correctly.
54 // with this, we can skip 0 and null individual initializations
55 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000056
reed@android.coma3122b92009-08-13 20:38:25 +000057#if 0 // not needed with the bzero call above
58 fTypeface = NULL;
59 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 fPathEffect = NULL;
61 fShader = NULL;
62 fXfermode = NULL;
63 fMaskFilter = NULL;
64 fColorFilter = NULL;
65 fRasterizer = NULL;
66 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000067 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000068 fAnnotation = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000070#endif
71
reed@google.comaefdd062012-02-29 13:03:00 +000072 fTextSize = SkPaintDefaults_TextSize;
reed@android.coma3122b92009-08-13 20:38:25 +000073 fTextScaleX = SK_Scalar1;
74 fColor = SK_ColorBLACK;
reed@google.comaefdd062012-02-29 13:03:00 +000075 fMiterLimit = SkPaintDefaults_MiterLimit;
76 fFlags = SkPaintDefaults_Flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 fCapType = kDefault_Cap;
78 fJoinType = kDefault_Join;
79 fTextAlign = kLeft_Align;
80 fStyle = kFill_Style;
81 fTextEncoding = kUTF8_TextEncoding;
reed@google.comaefdd062012-02-29 13:03:00 +000082 fHinting = SkPaintDefaults_Hinting;
djsollen@google.com56c69772011-11-08 19:00:26 +000083#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +000084 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000085 fGenerationID = 0;
86#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000087}
88
reed@google.com6fb7e2e2011-02-08 22:22:52 +000089SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 memcpy(this, &src, sizeof(src));
91
reed@google.com82065d62011-02-07 15:30:46 +000092 SkSafeRef(fTypeface);
93 SkSafeRef(fPathEffect);
94 SkSafeRef(fShader);
95 SkSafeRef(fXfermode);
96 SkSafeRef(fMaskFilter);
97 SkSafeRef(fColorFilter);
98 SkSafeRef(fRasterizer);
99 SkSafeRef(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000100 SkSafeRef(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000101 SkSafeRef(fAnnotation);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000102
103#ifdef SK_BUILD_FOR_ANDROID
104 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
105#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106}
107
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000108SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000109 SkSafeUnref(fTypeface);
110 SkSafeUnref(fPathEffect);
111 SkSafeUnref(fShader);
112 SkSafeUnref(fXfermode);
113 SkSafeUnref(fMaskFilter);
114 SkSafeUnref(fColorFilter);
115 SkSafeUnref(fRasterizer);
116 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000117 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000118 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119}
120
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000121SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 SkASSERT(&src);
123
reed@google.com82065d62011-02-07 15:30:46 +0000124 SkSafeRef(src.fTypeface);
125 SkSafeRef(src.fPathEffect);
126 SkSafeRef(src.fShader);
127 SkSafeRef(src.fXfermode);
128 SkSafeRef(src.fMaskFilter);
129 SkSafeRef(src.fColorFilter);
130 SkSafeRef(src.fRasterizer);
131 SkSafeRef(src.fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000132 SkSafeRef(src.fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000133 SkSafeRef(src.fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134
reed@google.com82065d62011-02-07 15:30:46 +0000135 SkSafeUnref(fTypeface);
136 SkSafeUnref(fPathEffect);
137 SkSafeUnref(fShader);
138 SkSafeUnref(fXfermode);
139 SkSafeUnref(fMaskFilter);
140 SkSafeUnref(fColorFilter);
141 SkSafeUnref(fRasterizer);
142 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000143 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000144 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
djsollen@google.com56c69772011-11-08 19:00:26 +0000146#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000147 fPaintOptionsAndroid.~SkPaintOptionsAndroid();
148
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000149 uint32_t oldGenerationID = fGenerationID;
150#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 memcpy(this, &src, sizeof(src));
djsollen@google.com56c69772011-11-08 19:00:26 +0000152#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000153 fGenerationID = oldGenerationID + 1;
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000154
155 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000156#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157
158 return *this;
159}
160
robertphillips@google.comb2657412013-08-07 22:36:29 +0000161bool operator==(const SkPaint& a, const SkPaint& b) {
162#ifdef SK_BUILD_FOR_ANDROID
163 //assumes that fGenerationID is the last field in the struct
164 return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
165#else
166 return !memcmp(&a, &b, sizeof(a));
167#endif
168}
169
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000170void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 SkPaint init;
172
djsollen@google.com56c69772011-11-08 19:00:26 +0000173#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000174 uint32_t oldGenerationID = fGenerationID;
175#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000177#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000178 fGenerationID = oldGenerationID + 1;
179#endif
180}
181
djsollen@google.com56c69772011-11-08 19:00:26 +0000182#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000183uint32_t SkPaint::getGenerationID() const {
184 return fGenerationID;
185}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000186
187void SkPaint::setGenerationID(uint32_t generationID) {
188 fGenerationID = generationID;
189}
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000190
djsollen@google.com60abb072012-02-15 18:49:15 +0000191unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
bungeman@google.comba3284e2013-01-22 19:49:33 +0000192 SkAutoGlyphCache autoCache(*this, NULL, NULL);
djsollen@google.com60abb072012-02-15 18:49:15 +0000193 SkGlyphCache* cache = autoCache.getCache();
194 return cache->getBaseGlyphCount(text);
195}
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000196
197void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) {
198 if (options != fPaintOptionsAndroid) {
199 fPaintOptionsAndroid = options;
200 GEN_ID_INC;
201 }
202}
djsollen@google.com60abb072012-02-15 18:49:15 +0000203#endif
204
reed@google.comc9683152013-07-18 13:47:01 +0000205SkPaint::FilterLevel SkPaint::getFilterLevel() const {
206 int level = 0;
207 if (fFlags & kFilterBitmap_Flag) {
208 level |= 1;
209 }
210 if (fFlags & kHighQualityFilterBitmap_Flag) {
211 level |= 2;
212 }
213 return (FilterLevel)level;
214}
215
216void SkPaint::setFilterLevel(FilterLevel level) {
217 unsigned mask = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag;
218 unsigned flags = 0;
219 if (level & 1) {
220 flags |= kFilterBitmap_Flag;
221 }
222 if (level & 2) {
223 flags |= kHighQualityFilterBitmap_Flag;
224 }
225 this->setFlags((fFlags & ~mask) | flags);
226}
227
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000228void SkPaint::setHinting(Hinting hintingLevel) {
229 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
230 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231}
232
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000233void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000234 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fFlags = flags;
236}
237
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000238void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
240}
241
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000242void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
244}
245
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
248}
249
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000250void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000251 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
252}
253
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000254void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000255 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
256}
257
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000259 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
264}
265
reed@google.com830a23e2011-11-10 15:20:49 +0000266void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000267 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
272}
273
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000274void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
276}
277
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000278void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
280}
281
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000282void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
284}
285
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286void SkPaint::setStyle(Style style) {
287 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000288 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000290 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000291#ifdef SK_REPORT_API_RANGE_CHECK
292 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
293#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295}
296
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000297void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000298 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 fColor = color;
300}
301
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000302void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000303 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
304 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305}
306
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000307void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000308 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309}
310
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000311void SkPaint::setStrokeWidth(SkScalar width) {
312 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000313 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000315 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000316#ifdef SK_REPORT_API_RANGE_CHECK
317 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
318#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000319 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320}
321
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000322void SkPaint::setStrokeMiter(SkScalar limit) {
323 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000324 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000326 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000327#ifdef SK_REPORT_API_RANGE_CHECK
328 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
329#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000330 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331}
332
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000333void SkPaint::setStrokeCap(Cap ct) {
334 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000335 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000337 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000338#ifdef SK_REPORT_API_RANGE_CHECK
339 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
340#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342}
343
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344void SkPaint::setStrokeJoin(Join jt) {
345 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000346 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000349#ifdef SK_REPORT_API_RANGE_CHECK
350 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
351#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353}
354
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000355///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000357void SkPaint::setTextAlign(Align align) {
358 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000359 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000361 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000362#ifdef SK_REPORT_API_RANGE_CHECK
363 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
364#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000365 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366}
367
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000368void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000369 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000370 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000372 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000373#ifdef SK_REPORT_API_RANGE_CHECK
374 SkDebugf("SkPaint::setTextSize() called with negative value\n");
375#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377}
378
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000379void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000380 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 fTextScaleX = scaleX;
382}
383
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000384void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000385 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 fTextSkewX = skewX;
387}
388
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000389void SkPaint::setTextEncoding(TextEncoding encoding) {
390 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000391 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000393 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000394#ifdef SK_REPORT_API_RANGE_CHECK
395 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
396#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000397 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398}
399
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000400///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000402SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000404 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 return font;
406}
407
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000408SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000410 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 return r;
412}
413
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000414SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000416 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 return looper;
418}
419
reed@google.com15356a62011-11-03 19:29:08 +0000420SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
421 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
422 GEN_ID_INC;
423 return imageFilter;
424}
425
reed@google.comb0a34d82012-07-11 19:57:55 +0000426SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
427 SkRefCnt_SafeAssign(fAnnotation, annotation);
428 GEN_ID_INC;
reed@google.comb0a34d82012-07-11 19:57:55 +0000429 return annotation;
430}
431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432///////////////////////////////////////////////////////////////////////////////
433
reed@google.comed43dff2013-06-04 16:56:27 +0000434static SkScalar mag2(SkScalar x, SkScalar y) {
435 return x * x + y * y;
436}
437
438static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
439 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
440 ||
441 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
442}
443
444bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
445 SkASSERT(!ctm.hasPerspective());
446 SkASSERT(!textM.hasPerspective());
447
448 SkMatrix matrix;
449 matrix.setConcat(ctm, textM);
450 return tooBig(matrix, MaxCacheSize2());
451}
452
453bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const {
454 SkMatrix textM;
455 return TooBigToUseCache(ctm, *this->setTextMatrix(&textM));
456}
457
458bool SkPaint::tooBigToUseCache() const {
459 SkMatrix textM;
460 return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2());
461}
462
463///////////////////////////////////////////////////////////////////////////////
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465#include "SkGlyphCache.h"
466#include "SkUtils.h"
467
reed@google.com90808e82013-03-19 14:44:54 +0000468static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
469 void* context) {
470 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000471}
472
djsollen@google.com56c69772011-11-08 19:00:26 +0000473#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000474const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text,
475 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000476 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000477 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000478
479 const SkGlyph& glyph = cache->getUnicharMetrics(text);
480
481 SkGlyphCache::AttachCache(cache);
482 return glyph;
483}
484
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000485const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId,
486 const SkMatrix* deviceMatrix) {
djsollen@google.com60abb072012-02-15 18:49:15 +0000487 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000488 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.com60abb072012-02-15 18:49:15 +0000489
490 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
491
492 SkGlyphCache::AttachCache(cache);
493 return glyph;
494}
495
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000496const void* SkPaint::findImage(const SkGlyph& glyph,
497 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000498 // See ::detachCache()
499 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000500 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000501
502 const void* image = cache->findImage(glyph);
503
504 SkGlyphCache::AttachCache(cache);
505 return image;
506}
507#endif
508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
510 uint16_t glyphs[]) const {
511 if (byteLength == 0) {
512 return 0;
513 }
reed@google.com72cf4922011-01-04 19:58:20 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 SkASSERT(textData != NULL);
516
517 if (NULL == glyphs) {
518 switch (this->getTextEncoding()) {
519 case kUTF8_TextEncoding:
520 return SkUTF8_CountUnichars((const char*)textData, byteLength);
521 case kUTF16_TextEncoding:
522 return SkUTF16_CountUnichars((const uint16_t*)textData,
523 byteLength >> 1);
reed@google.com68bc6f72012-03-14 19:41:55 +0000524 case kUTF32_TextEncoding:
525 return byteLength >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 case kGlyphID_TextEncoding:
527 return byteLength >> 1;
528 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000529 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 }
531 return 0;
532 }
reed@google.com72cf4922011-01-04 19:58:20 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 // handle this encoding before the setup for the glyphcache
537 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
538 // we want to ignore the low bit of byteLength
539 memcpy(glyphs, textData, byteLength >> 1 << 1);
540 return byteLength >> 1;
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.com8a1c16f2008-12-17 15:59:43 +0000544 SkGlyphCache* cache = autoCache.getCache();
545
546 const char* text = (const char*)textData;
547 const char* stop = text + byteLength;
548 uint16_t* gptr = glyphs;
549
550 switch (this->getTextEncoding()) {
551 case SkPaint::kUTF8_TextEncoding:
552 while (text < stop) {
553 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
554 }
555 break;
556 case SkPaint::kUTF16_TextEncoding: {
557 const uint16_t* text16 = (const uint16_t*)text;
558 const uint16_t* stop16 = (const uint16_t*)stop;
559 while (text16 < stop16) {
560 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
561 }
562 break;
563 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000564 case kUTF32_TextEncoding: {
565 const int32_t* text32 = (const int32_t*)text;
566 const int32_t* stop32 = (const int32_t*)stop;
567 while (text32 < stop32) {
568 *gptr++ = cache->unicharToGlyph(*text32++);
569 }
570 break;
571 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000573 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 }
575 return gptr - glyphs;
576}
577
reed@android.coma5dcaf62010-02-05 17:12:32 +0000578bool SkPaint::containsText(const void* textData, size_t byteLength) const {
579 if (0 == byteLength) {
580 return true;
581 }
reed@google.com72cf4922011-01-04 19:58:20 +0000582
reed@android.coma5dcaf62010-02-05 17:12:32 +0000583 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000584
reed@android.coma5dcaf62010-02-05 17:12:32 +0000585 // handle this encoding before the setup for the glyphcache
586 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
587 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
588 size_t count = byteLength >> 1;
589 for (size_t i = 0; i < count; i++) {
590 if (0 == glyphID[i]) {
591 return false;
592 }
593 }
594 return true;
595 }
reed@google.com72cf4922011-01-04 19:58:20 +0000596
bungeman@google.com532470f2013-01-22 19:25:14 +0000597 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000598 SkGlyphCache* cache = autoCache.getCache();
599
600 switch (this->getTextEncoding()) {
601 case SkPaint::kUTF8_TextEncoding: {
602 const char* text = static_cast<const char*>(textData);
603 const char* stop = text + byteLength;
604 while (text < stop) {
605 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
606 return false;
607 }
608 }
609 break;
610 }
611 case SkPaint::kUTF16_TextEncoding: {
612 const uint16_t* text = static_cast<const uint16_t*>(textData);
613 const uint16_t* stop = text + (byteLength >> 1);
614 while (text < stop) {
615 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
616 return false;
617 }
618 }
619 break;
620 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000621 case SkPaint::kUTF32_TextEncoding: {
622 const int32_t* text = static_cast<const int32_t*>(textData);
623 const int32_t* stop = text + (byteLength >> 2);
624 while (text < stop) {
625 if (0 == cache->unicharToGlyph(*text++)) {
626 return false;
627 }
628 }
629 break;
630 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000631 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000632 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000633 return false;
634 }
635 return true;
636}
637
reed@android.com9d3a9852010-01-08 14:07:42 +0000638void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
639 SkUnichar textData[]) const {
640 if (count <= 0) {
641 return;
642 }
643
644 SkASSERT(glyphs != NULL);
645 SkASSERT(textData != NULL);
646
bungeman@google.com532470f2013-01-22 19:25:14 +0000647 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000648 SkGlyphCache* cache = autoCache.getCache();
649
650 for (int index = 0; index < count; index++) {
651 textData[index] = cache->glyphToUnichar(glyphs[index]);
652 }
653}
654
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655///////////////////////////////////////////////////////////////////////////////
656
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000657static const SkGlyph& sk_getMetrics_utf8_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 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
663}
664
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000665static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
666 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 SkASSERT(cache != NULL);
668 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000669
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
671}
672
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000673static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
674 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 SkASSERT(cache != NULL);
676 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000677
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
679}
680
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000681static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
682 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 SkASSERT(cache != NULL);
684 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
687}
688
reed@google.com68bc6f72012-03-14 19:41:55 +0000689static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
690 const char** text) {
691 SkASSERT(cache != NULL);
692 SkASSERT(text != NULL);
693
694 const int32_t* ptr = *(const int32_t**)text;
695 SkUnichar uni = *ptr++;
696 *text = (const char*)ptr;
697 return cache->getUnicharMetrics(uni);
698}
699
700static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
701 const char** text) {
702 SkASSERT(cache != NULL);
703 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000704
reed@google.com68bc6f72012-03-14 19:41:55 +0000705 const int32_t* ptr = *(const int32_t**)text;
706 SkUnichar uni = *--ptr;
707 *text = (const char*)ptr;
708 return cache->getUnicharMetrics(uni);
709}
710
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000711static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
712 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 SkASSERT(cache != NULL);
714 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 const uint16_t* ptr = *(const uint16_t**)text;
717 unsigned glyphID = *ptr;
718 ptr += 1;
719 *text = (const char*)ptr;
720 return cache->getGlyphIDMetrics(glyphID);
721}
722
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000723static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
724 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 SkASSERT(cache != NULL);
726 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 const uint16_t* ptr = *(const uint16_t**)text;
729 ptr -= 1;
730 unsigned glyphID = *ptr;
731 *text = (const char*)ptr;
732 return cache->getGlyphIDMetrics(glyphID);
733}
734
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735static const SkGlyph& sk_getAdvance_utf8_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 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
741}
742
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000743static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
744 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 SkASSERT(cache != NULL);
746 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
749}
750
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000751static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
752 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 SkASSERT(cache != NULL);
754 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000755
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
757}
758
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000759static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
760 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 SkASSERT(cache != NULL);
762 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
765}
766
reed@google.com68bc6f72012-03-14 19:41:55 +0000767static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
768 const char** text) {
769 SkASSERT(cache != NULL);
770 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000771
reed@google.com68bc6f72012-03-14 19:41:55 +0000772 const int32_t* ptr = *(const int32_t**)text;
773 SkUnichar uni = *ptr++;
774 *text = (const char*)ptr;
775 return cache->getUnicharAdvance(uni);
776}
777
778static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
779 const char** text) {
780 SkASSERT(cache != NULL);
781 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000782
reed@google.com68bc6f72012-03-14 19:41:55 +0000783 const int32_t* ptr = *(const int32_t**)text;
784 SkUnichar uni = *--ptr;
785 *text = (const char*)ptr;
786 return cache->getUnicharAdvance(uni);
787}
788
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000789static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
790 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 SkASSERT(cache != NULL);
792 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 const uint16_t* ptr = *(const uint16_t**)text;
795 unsigned glyphID = *ptr;
796 ptr += 1;
797 *text = (const char*)ptr;
798 return cache->getGlyphIDAdvance(glyphID);
799}
800
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000801static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
802 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 SkASSERT(cache != NULL);
804 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000805
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 const uint16_t* ptr = *(const uint16_t**)text;
807 ptr -= 1;
808 unsigned glyphID = *ptr;
809 *text = (const char*)ptr;
810 return cache->getGlyphIDAdvance(glyphID);
811}
812
813SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000814 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
816 sk_getMetrics_utf8_next,
817 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000818 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000820
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 sk_getMetrics_utf8_prev,
822 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000823 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 sk_getAdvance_utf8_next,
827 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000828 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000830
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 sk_getAdvance_utf8_prev,
832 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000833 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 sk_getAdvance_glyph_prev
835 };
reed@google.com72cf4922011-01-04 19:58:20 +0000836
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 unsigned index = this->getTextEncoding();
838
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000839 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000840 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000841 }
842 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000843 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000844 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845
846 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
847 return gMeasureCacheProcs[index];
848}
849
850///////////////////////////////////////////////////////////////////////////////
851
852static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000853 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 SkASSERT(cache != NULL);
855 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
858}
859
860static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000861 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 SkASSERT(cache != NULL);
863 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000864
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
866}
867
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000868static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
869 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 SkASSERT(cache != NULL);
871 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000872
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
874}
875
876static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000877 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 SkASSERT(cache != NULL);
879 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
882 x, y);
883}
884
reed@google.com68bc6f72012-03-14 19:41:55 +0000885static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
886 const char** text, SkFixed, SkFixed) {
887 SkASSERT(cache != NULL);
888 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000889
reed@google.com68bc6f72012-03-14 19:41:55 +0000890 const int32_t* ptr = *(const int32_t**)text;
891 SkUnichar uni = *ptr++;
892 *text = (const char*)ptr;
893 return cache->getUnicharMetrics(uni);
894}
895
896static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
897 const char** text, SkFixed x, SkFixed y) {
898 SkASSERT(cache != NULL);
899 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000900
reed@google.com68bc6f72012-03-14 19:41:55 +0000901 const int32_t* ptr = *(const int32_t**)text;
902 SkUnichar uni = *--ptr;
903 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000904 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000905}
906
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000907static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
908 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 SkASSERT(cache != NULL);
910 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 const uint16_t* ptr = *(const uint16_t**)text;
913 unsigned glyphID = *ptr;
914 ptr += 1;
915 *text = (const char*)ptr;
916 return cache->getGlyphIDMetrics(glyphID);
917}
918
919static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000920 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 SkASSERT(cache != NULL);
922 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000923
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 const uint16_t* ptr = *(const uint16_t**)text;
925 unsigned glyphID = *ptr;
926 ptr += 1;
927 *text = (const char*)ptr;
928 return cache->getGlyphIDMetrics(glyphID, x, y);
929}
930
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000931SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 static const SkDrawCacheProc gDrawCacheProcs[] = {
933 sk_getMetrics_utf8_00,
934 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000935 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000937
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 sk_getMetrics_utf8_xy,
939 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000940 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 sk_getMetrics_glyph_xy
942 };
reed@google.com72cf4922011-01-04 19:58:20 +0000943
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000945 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000946 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000947 }
reed@google.com72cf4922011-01-04 19:58:20 +0000948
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
950 return gDrawCacheProcs[index];
951}
952
953///////////////////////////////////////////////////////////////////////////////
954
reed@google.comed43dff2013-06-04 16:56:27 +0000955#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
956SkPaint::kDevKernText_Flag | \
957SkPaint::kLinearText_Flag | \
958SkPaint::kLCDRenderText_Flag | \
959SkPaint::kEmbeddedBitmapText_Flag | \
960SkPaint::kAutoHinting_Flag | \
961SkPaint::kGenA8FromLCD_Flag )
962
963SkScalar SkPaint::setupForAsPaths() {
964 uint32_t flags = this->getFlags();
965 // clear the flags we don't care about
966 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
967 // set the flags we do care about
968 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000969
reed@google.comed43dff2013-06-04 16:56:27 +0000970 this->setFlags(flags);
971 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000972
reed@google.comed43dff2013-06-04 16:56:27 +0000973 SkScalar textSize = fTextSize;
974 this->setTextSize(kCanonicalTextSizeForPaths);
975 return textSize / kCanonicalTextSizeForPaths;
976}
977
978class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979public:
reed@google.comed43dff2013-06-04 16:56:27 +0000980 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
981 if (paint.isLinearText() || paint.tooBigToUseCache()) {
982 SkPaint* p = fLazy.set(paint);
983 fScale = p->setupForAsPaths();
984 fPaint = p;
985 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000987
reed@google.comed43dff2013-06-04 16:56:27 +0000988 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000989
reed@google.comed43dff2013-06-04 16:56:27 +0000990 /**
991 * Returns 0 if the paint was unmodified, or the scale factor need to
992 * the original textSize
993 */
994 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996private:
reed@google.comed43dff2013-06-04 16:56:27 +0000997 const SkPaint* fPaint;
998 SkScalar fScale;
999 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000};
1001
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001002static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 bounds->set(SkIntToScalar(g.fLeft),
1004 SkIntToScalar(g.fTop),
1005 SkIntToScalar(g.fLeft + g.fWidth),
1006 SkIntToScalar(g.fTop + g.fHeight));
1007}
1008
reed@android.come88f5512010-03-19 14:42:28 +00001009// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
1010// we don't overflow along the way
1011typedef int64_t Sk48Dot16;
1012
1013#ifdef SK_SCALAR_IS_FLOAT
1014 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +00001015 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +00001016 }
1017#else
1018 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
1019 // just return the low 32bits
1020 return static_cast<SkFixed>(x);
1021 }
1022#endif
1023
reed@google.com44da42e2011-11-10 20:04:47 +00001024static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +00001025 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 bounds->join(SkIntToScalar(g.fLeft) + sx,
1027 SkIntToScalar(g.fTop),
1028 SkIntToScalar(g.fLeft + g.fWidth) + sx,
1029 SkIntToScalar(g.fTop + g.fHeight));
1030}
1031
reed@google.com44da42e2011-11-10 20:04:47 +00001032static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
1033 SkScalar sy = Sk48Dot16ToScalar(dy);
1034 bounds->join(SkIntToScalar(g.fLeft),
1035 SkIntToScalar(g.fTop) + sy,
1036 SkIntToScalar(g.fLeft + g.fWidth),
1037 SkIntToScalar(g.fTop + g.fHeight) + sy);
1038}
1039
1040typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
1041
1042// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
1043static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
1044 SkASSERT(0 == xyIndex || 1 == xyIndex);
1045 return (&glyph.fAdvanceX)[xyIndex];
1046}
1047
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048SkScalar SkPaint::measure_text(SkGlyphCache* cache,
1049 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001050 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001052 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001054 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001056 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 return 0;
1058 }
1059
1060 SkMeasureCacheProc glyphCacheProc;
1061 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1062 NULL != bounds);
1063
reed@google.com44da42e2011-11-10 20:04:47 +00001064 int xyIndex;
1065 JoinBoundsProc joinBoundsProc;
1066 if (this->isVerticalText()) {
1067 xyIndex = 1;
1068 joinBoundsProc = join_bounds_y;
1069 } else {
1070 xyIndex = 0;
1071 joinBoundsProc = join_bounds_x;
1072 }
1073
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 int n = 1;
1075 const char* stop = (const char*)text + byteLength;
1076 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001077 // our accumulated fixed-point advances might overflow 16.16, so we use
1078 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1079 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001080 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081
1082 SkAutoKern autokern;
1083
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001084 if (NULL == bounds) {
1085 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 int rsb;
1087 for (; text < stop; n++) {
1088 rsb = g->fRsbDelta;
1089 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001090 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001092 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001094 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 }
1096 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001097 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001099 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 int rsb;
1101 for (; text < stop; n++) {
1102 rsb = g->fRsbDelta;
1103 g = &glyphCacheProc(cache, &text);
1104 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001105 joinBoundsProc(*g, bounds, x);
1106 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001108 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 for (; text < stop; n++) {
1110 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001111 joinBoundsProc(*g, bounds, x);
1112 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 }
1114 }
1115 }
1116 SkASSERT(text == stop);
1117
1118 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001119 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120}
1121
1122SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001123 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 const char* text = (const char*)textData;
1125 SkASSERT(text != NULL || length == 0);
1126
reed@google.comed43dff2013-06-04 16:56:27 +00001127 SkCanonicalizePaint canon(*this);
1128 const SkPaint& paint = canon.getPaint();
1129 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001130
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001131 SkMatrix zoomMatrix, *zoomPtr = NULL;
1132 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 zoomMatrix.setScale(zoom, zoom);
1134 zoomPtr = &zoomMatrix;
1135 }
1136
reed@google.comed43dff2013-06-04 16:56:27 +00001137 SkAutoGlyphCache autoCache(paint, NULL, zoomPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 SkGlyphCache* cache = autoCache.getCache();
1139
1140 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001141
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001142 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 int tempCount;
1144
reed@google.comed43dff2013-06-04 16:56:27 +00001145 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001146 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001148 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1150 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1151 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1152 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1153 }
1154 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001155 } else if (bounds) {
1156 // ensure that even if we don't measure_text we still update the bounds
1157 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 }
1159 return width;
1160}
1161
1162typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1163
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001164static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 return text < stop;
1166}
1167
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001168static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 return text > stop;
1170}
1171
1172static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001173 const char** text, size_t length,
1174 const char** stop) {
1175 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 *stop = *text + length;
1177 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001178 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 // text should point to the end of the buffer, and stop to the beginning
1180 *stop = *text;
1181 *text += length;
1182 return backward_textBufferPred;
1183 }
1184}
1185
1186size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1187 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001188 TextBufferDirection tbd) const {
1189 if (0 == length || 0 >= maxWidth) {
1190 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001192 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 return 0;
1194 }
1195
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001196 if (0 == fTextSize) {
1197 if (measuredWidth) {
1198 *measuredWidth = 0;
1199 }
1200 return length;
1201 }
1202
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 SkASSERT(textD != NULL);
1204 const char* text = (const char*)textD;
1205
reed@google.comed43dff2013-06-04 16:56:27 +00001206 SkCanonicalizePaint canon(*this);
1207 const SkPaint& paint = canon.getPaint();
1208 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209
reed@google.comed43dff2013-06-04 16:56:27 +00001210 // adjust max in case we changed the textSize in paint
1211 if (scale) {
1212 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 }
reed@google.com72cf4922011-01-04 19:58:20 +00001214
reed@google.comed43dff2013-06-04 16:56:27 +00001215 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 SkGlyphCache* cache = autoCache.getCache();
1217
reed@google.comed43dff2013-06-04 16:56:27 +00001218 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 const char* stop;
1220 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001221 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001222 // use 64bits for our accumulator, to avoid overflowing 16.16
1223 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1224 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225
1226 SkAutoKern autokern;
1227
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001228 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001230 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231 const char* curr = text;
1232 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001233 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001234 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 width -= x;
1236 text = curr;
1237 break;
1238 }
1239 rsb = g.fRsbDelta;
1240 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001241 } else {
1242 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001244 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001245 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 width -= x;
1247 text = curr;
1248 break;
1249 }
1250 }
1251 }
reed@google.com72cf4922011-01-04 19:58:20 +00001252
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001253 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001254 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001255 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 *measuredWidth = scalarWidth;
1259 }
reed@google.com72cf4922011-01-04 19:58:20 +00001260
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 // return the number of bytes measured
1262 return (kForward_TextBufferDirection == tbd) ?
1263 text - stop + length : stop - text + length;
1264}
1265
1266///////////////////////////////////////////////////////////////////////////////
1267
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001268static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001269 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 return false; // don't detach the cache
1271}
1272
reed@google.com90808e82013-03-19 14:44:54 +00001273static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1274 void* context) {
1275 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276}
1277
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001278SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001279 SkCanonicalizePaint canon(*this);
1280 const SkPaint& paint = canon.getPaint();
1281 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001282
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001283 SkMatrix zoomMatrix, *zoomPtr = NULL;
1284 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 zoomMatrix.setScale(zoom, zoom);
1286 zoomPtr = &zoomMatrix;
1287 }
1288
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001290 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001292 }
reed@google.com72cf4922011-01-04 19:58:20 +00001293
reed@google.comed43dff2013-06-04 16:56:27 +00001294 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001296 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1298 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1299 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1300 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1301 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001302 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1303 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1304 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1305 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 }
1307 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1308}
1309
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001310///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001312static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 bounds->set(g.fLeft * scale,
1314 g.fTop * scale,
1315 (g.fLeft + g.fWidth) * scale,
1316 (g.fTop + g.fHeight) * scale);
1317}
1318
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001319int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1320 SkScalar widths[], SkRect bounds[]) const {
1321 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
1325 SkASSERT(NULL != textData);
1326
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001327 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330
reed@google.comed43dff2013-06-04 16:56:27 +00001331 SkCanonicalizePaint canon(*this);
1332 const SkPaint& paint = canon.getPaint();
1333 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334
reed@google.comed43dff2013-06-04 16:56:27 +00001335 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 SkGlyphCache* cache = autoCache.getCache();
1337 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001338 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 NULL != bounds);
1340
1341 const char* text = (const char*)textData;
1342 const char* stop = text + byteLength;
1343 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001344 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001346 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 // we adjust the widths returned here through auto-kerning
1348 SkAutoKern autokern;
1349 SkFixed prevWidth = 0;
1350
1351 if (scale) {
1352 while (text < stop) {
1353 const SkGlyph& g = glyphCacheProc(cache, &text);
1354 if (widths) {
1355 SkFixed adjust = autokern.adjust(g);
1356
1357 if (count > 0) {
1358 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1359 *widths++ = SkScalarMul(w, scale);
1360 }
reed@google.com44da42e2011-11-10 20:04:47 +00001361 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 }
1363 if (bounds) {
1364 set_bounds(g, bounds++, scale);
1365 }
1366 ++count;
1367 }
1368 if (count > 0 && widths) {
1369 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1370 }
1371 } else {
1372 while (text < stop) {
1373 const SkGlyph& g = glyphCacheProc(cache, &text);
1374 if (widths) {
1375 SkFixed adjust = autokern.adjust(g);
1376
1377 if (count > 0) {
1378 *widths++ = SkFixedToScalar(prevWidth + adjust);
1379 }
reed@google.com44da42e2011-11-10 20:04:47 +00001380 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 }
1382 if (bounds) {
1383 set_bounds(g, bounds++);
1384 }
1385 ++count;
1386 }
1387 if (count > 0 && widths) {
1388 *widths = SkFixedToScalar(prevWidth);
1389 }
1390 }
1391 } else { // no devkern
1392 if (scale) {
1393 while (text < stop) {
1394 const SkGlyph& g = glyphCacheProc(cache, &text);
1395 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001396 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 scale);
1398 }
1399 if (bounds) {
1400 set_bounds(g, bounds++, scale);
1401 }
1402 ++count;
1403 }
1404 } else {
1405 while (text < stop) {
1406 const SkGlyph& g = glyphCacheProc(cache, &text);
1407 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001408 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 }
1410 if (bounds) {
1411 set_bounds(g, bounds++);
1412 }
1413 ++count;
1414 }
1415 }
1416 }
1417
1418 SkASSERT(text == stop);
1419 return count;
1420}
1421
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001422///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423
1424#include "SkDraw.h"
1425
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001426void SkPaint::getTextPath(const void* textData, size_t length,
1427 SkScalar x, SkScalar y, SkPath* path) const {
1428 SkASSERT(length == 0 || textData != NULL);
1429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001431 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001433 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434
djsollen@google.com166e6532012-03-20 14:24:38 +00001435 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 SkMatrix matrix;
1437 SkScalar prevXPos = 0;
1438
1439 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1440 matrix.postTranslate(x, y);
1441 path->reset();
1442
1443 SkScalar xpos;
1444 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001445 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001447 if (iterPath) {
1448 path->addPath(*iterPath, matrix);
1449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 prevXPos = xpos;
1451 }
1452}
1453
reed@google.comca0062e2012-07-20 11:20:32 +00001454void SkPaint::getPosTextPath(const void* textData, size_t length,
1455 const SkPoint pos[], SkPath* path) const {
1456 SkASSERT(length == 0 || textData != NULL);
1457
1458 const char* text = (const char*)textData;
1459 if (text == NULL || length == 0 || path == NULL) {
1460 return;
1461 }
1462
1463 SkTextToPathIter iter(text, length, *this, false);
1464 SkMatrix matrix;
1465 SkPoint prevPos;
1466 prevPos.set(0, 0);
1467
1468 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1469 path->reset();
1470
1471 unsigned int i = 0;
1472 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001473 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001474 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001475 if (iterPath) {
1476 path->addPath(*iterPath, matrix);
1477 }
reed@google.comca0062e2012-07-20 11:20:32 +00001478 prevPos = pos[i];
1479 i++;
1480 }
1481}
1482
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483static void add_flattenable(SkDescriptor* desc, uint32_t tag,
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001484 SkOrderedWriteBuffer* buffer) {
1485 buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486}
1487
reed@google.com2739b272011-09-28 17:26:42 +00001488// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001489static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001492 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001493 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001494 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001495 }
1496
reed@google.com65dd8f82011-03-14 13:31:16 +00001497 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001498 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001499 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001500
1501 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
reed@android.com1cdcb512009-08-24 19:11:00 +00001504// if linear-text is on, then we force hinting to be off (since that's sort of
1505// the point of linear-text.
1506static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1507 SkPaint::Hinting h = paint.getHinting();
1508 if (paint.isLinearText()) {
1509 h = SkPaint::kNo_Hinting;
1510 }
1511 return h;
1512}
1513
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001514// return true if the paint is just a single color (i.e. not a shader). If its
1515// a shader, then we can't compute a const luminance for it :(
1516static bool justAColor(const SkPaint& paint, SkColor* color) {
1517 if (paint.getShader()) {
1518 return false;
1519 }
1520 SkColor c = paint.getColor();
1521 if (paint.getColorFilter()) {
1522 c = paint.getColorFilter()->filterColor(c);
1523 }
1524 if (color) {
1525 *color = c;
1526 }
1527 return true;
1528}
1529
reed@google.comce6dbb62012-02-10 22:01:45 +00001530static SkColor computeLuminanceColor(const SkPaint& paint) {
1531 SkColor c;
1532 if (!justAColor(paint, &c)) {
1533 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1534 }
1535 return c;
1536}
reed@google.com813d38b2012-02-13 21:37:57 +00001537
reed@google.comdd43df92012-02-15 14:50:29 +00001538#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1539
reed@google.com4f79b9b2011-09-13 13:23:26 +00001540// Beyond this size, LCD doesn't appreciably improve quality, but it always
1541// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001542#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1543 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1544#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001545
1546static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1547 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1548 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1549 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001550 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001551}
1552
reed@google.com72cf4922011-01-04 19:58:20 +00001553/*
1554 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1555 * that vary only slightly when we create our key into the font cache, since the font scaler
1556 * typically returns the same looking resuts for tiny changes in the matrix.
1557 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001558static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001559#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001560 int n = sk_float_round2int(x * 1024);
1561 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001562#else
1563 // round to the nearest 10 fractional bits
1564 return (x + (1 << 5)) & ~(1024 - 1);
1565#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001566}
1567
reed@android.com36a4c2a2009-07-22 19:52:11 +00001568void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001569 const SkDeviceProperties* deviceProperties,
1570 const SkMatrix* deviceMatrix,
1571 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001572 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001574 SkTypeface* typeface = paint.getTypeface();
1575 if (NULL == typeface) {
1576 typeface = SkTypeface::GetDefaultTypeface();
1577 }
1578 rec->fOrigFontID = typeface->uniqueID();
reed@google.com7d26c592011-06-13 13:01:10 +00001579 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 rec->fTextSize = paint.getTextSize();
1581 rec->fPreScaleX = paint.getTextScaleX();
1582 rec->fPreSkewX = paint.getTextSkewX();
1583
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001584 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001585 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1586 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1587 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1588 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001589 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1591 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1592 }
reed@google.com72cf4922011-01-04 19:58:20 +00001593
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 SkPaint::Style style = paint.getStyle();
1595 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001596
reed@google.comffe49f52011-11-22 19:42:41 +00001597 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001598
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001599 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001600#ifdef SK_USE_FREETYPE_EMBOLDEN
1601 flags |= SkScalerContext::kEmbolden_Flag;
1602#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001603 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1604 kStdFakeBoldInterpKeys,
1605 kStdFakeBoldInterpValues,
1606 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001608
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001609 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610 style = SkPaint::kStrokeAndFill_Style;
1611 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001612 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001614 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001615#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 }
1617
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001618 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001620 }
reed@google.com72cf4922011-01-04 19:58:20 +00001621
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001622 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 rec->fFrameWidth = strokeWidth;
1624 rec->fMiterLimit = paint.getStrokeMiter();
1625 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1626
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001627 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001629 }
1630 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 rec->fFrameWidth = 0;
1632 rec->fMiterLimit = 0;
1633 rec->fStrokeJoin = 0;
1634 }
1635
reed@google.com02b53312011-05-18 19:00:53 +00001636 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1637
bungeman@google.com532470f2013-01-22 19:25:14 +00001638 SkDeviceProperties::Geometry geometry = deviceProperties
1639 ? deviceProperties->fGeometry
1640 : SkDeviceProperties::Geometry::MakeDefault();
1641 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
1642 if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001643 // eeek, can't support LCD
1644 rec->fMaskFormat = SkMask::kA8_Format;
1645 } else {
bungeman@google.com532470f2013-01-22 19:25:14 +00001646 if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
reed@google.com02b53312011-05-18 19:00:53 +00001647 flags |= SkScalerContext::kLCD_Vertical_Flag;
1648 }
bungeman@google.com532470f2013-01-22 19:25:14 +00001649 if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
reed@google.com02b53312011-05-18 19:00:53 +00001650 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1651 }
1652 }
1653 }
1654
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001655 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001656 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001657 }
1658 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001659 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001660 }
1661 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001662 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001663 }
reed@google.com830a23e2011-11-10 15:20:49 +00001664 if (paint.isVerticalText()) {
1665 flags |= SkScalerContext::kVertical_Flag;
1666 }
reed@google.com8351aab2012-01-18 17:06:35 +00001667 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1668 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1669 }
reed@google.com02b53312011-05-18 19:00:53 +00001670 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001671
reed@google.comffe49f52011-11-22 19:42:41 +00001672 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001673 rec->setHinting(computeHinting(paint));
1674
bungeman@google.com97efada2012-07-30 20:40:50 +00001675 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001676
1677 if (NULL == deviceProperties) {
1678 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1679 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1680 } else {
1681 rec->setDeviceGamma(deviceProperties->fGamma);
1682
1683 //For now always set the paint gamma equal to the device gamma.
1684 //The math in SkMaskGamma can handle them being different,
1685 //but it requires superluminous masks when
1686 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1687 rec->setPaintGamma(deviceProperties->fGamma);
1688 }
1689
1690#ifdef SK_GAMMA_CONTRAST
1691 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001692#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001693 /**
1694 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1695 * With lower values small text appears washed out (though correctly so).
1696 * With higher values lcd fringing is worse and the smoothing effect of
1697 * partial coverage is diminished.
1698 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001699 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001700#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001701
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001702 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001703
reed@android.com36a4c2a2009-07-22 19:52:11 +00001704 /* Allow the fonthost to modify our rec before we use it as a key into the
1705 cache. This way if we're asking for something that they will ignore,
1706 they can modify our rec up front, so we don't create duplicate cache
1707 entries.
1708 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001709 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001710
reed@google.com10d2d4d2012-03-01 22:32:51 +00001711 // be sure to call PostMakeRec(rec) before you actually use it!
1712}
1713
1714/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001715 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1716 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1717 * to hold it until the returned pointer is refed or forgotten.
1718 */
1719SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1720
bungeman@google.comae30f562012-09-11 18:44:55 +00001721static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001722static SkMaskGamma* gMaskGamma = NULL;
1723static SkScalar gContrast = SK_ScalarMin;
1724static SkScalar gPaintGamma = SK_ScalarMin;
1725static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001726/**
1727 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1728 * the returned SkMaskGamma pointer is refed or forgotten.
1729 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001730static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001731 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1732 if (NULL == gLinearMaskGamma) {
1733 gLinearMaskGamma = SkNEW(SkMaskGamma);
1734 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001735 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001736 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001737 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1738 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001739 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001740 gContrast = contrast;
1741 gPaintGamma = paintGamma;
1742 gDeviceGamma = deviceGamma;
1743 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001744 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001745}
1746
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001747/*static*/ void SkPaint::Term() {
1748 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1749
bungeman@google.comae30f562012-09-11 18:44:55 +00001750 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001751 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001752 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001753 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001754 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1755 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1756 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001757}
1758
bungeman@google.com97efada2012-07-30 20:40:50 +00001759/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001760 * We ensure that the rec is self-consistent and efficient (where possible)
1761 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001762void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001763 /**
1764 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001765 * limits the number of unique entries, and the scaler will only look at
1766 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001767 */
reed@google.comdd43df92012-02-15 14:50:29 +00001768 switch (rec->fMaskFormat) {
1769 case SkMask::kLCD16_Format:
1770 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001771 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001772 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001773 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001774 break;
1775 }
1776 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001777 // filter down the luminance to a single component, since A8 can't
1778 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001779
bungeman@google.com97efada2012-07-30 20:40:50 +00001780 SkColor color = rec->getLuminanceColor();
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001781 U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001782 //If we are asked to look like LCD, look like LCD.
1783 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1784 // HACK: Prevents green from being pre-blended as white.
1785 lum -= ((255 - lum) * lum) / 255;
1786 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001787
reed@google.comdd43df92012-02-15 14:50:29 +00001788 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001789 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001790 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001791 break;
1792 }
1793 case SkMask::kBW_Format:
1794 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001795 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001796 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001797 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798}
1799
1800#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1801
reed@google.com17fb3872011-05-04 14:31:07 +00001802#ifdef SK_DEBUG
1803 #define TEST_DESC
1804#endif
1805
reed@google.comffe49f52011-11-22 19:42:41 +00001806/*
1807 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1808 * by gamma correction, so we jam the luminance field to 0 (most common value
1809 * for black text) in hopes that we get a cache hit easier. A better solution
1810 * would be for the fontcache lookup to know to ignore the luminance field
1811 * entirely, but not sure how to do that and keep it fast.
1812 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001813void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1814 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001815 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001816 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 SkScalerContext::Rec rec;
1818
bungeman@google.com532470f2013-01-22 19:25:14 +00001819 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001820 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001821 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001822 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823
1824 size_t descSize = sizeof(rec);
1825 int entryCount = 1;
1826 SkPathEffect* pe = this->getPathEffect();
1827 SkMaskFilter* mf = this->getMaskFilter();
1828 SkRasterizer* ra = this->getRasterizer();
1829
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001830 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1831 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1832 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833
1834 if (pe) {
1835 peBuffer.writeFlattenable(pe);
1836 descSize += peBuffer.size();
1837 entryCount += 1;
1838 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1839 // seems like we could support kLCD as well at this point...
1840 }
1841 if (mf) {
1842 mfBuffer.writeFlattenable(mf);
1843 descSize += mfBuffer.size();
1844 entryCount += 1;
1845 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001846 /* Pre-blend is not currently applied to filtered text.
1847 The primary filter is blur, for which contrast makes no sense,
1848 and for which the destination guess error is more visible.
1849 Also, all existing users of blur have calibrated for linear. */
1850 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 }
1852 if (ra) {
1853 raBuffer.writeFlattenable(ra);
1854 descSize += raBuffer.size();
1855 entryCount += 1;
1856 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1857 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001858
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001859#ifdef SK_BUILD_FOR_ANDROID
1860 SkOrderedWriteBuffer androidBuffer(128);
1861 fPaintOptionsAndroid.flatten(androidBuffer);
1862 descSize += androidBuffer.size();
1863 entryCount += 1;
1864#endif
1865
reed@google.com10d2d4d2012-03-01 22:32:51 +00001866 ///////////////////////////////////////////////////////////////////////////
1867 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001868 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001869
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 descSize += SkDescriptor::ComputeOverhead(entryCount);
1871
1872 SkAutoDescriptor ad(descSize);
1873 SkDescriptor* desc = ad.getDesc();
1874
1875 desc->init();
1876 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1877
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001878#ifdef SK_BUILD_FOR_ANDROID
1879 add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1880#endif
1881
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 if (pe) {
1883 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1884 }
1885 if (mf) {
1886 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1887 }
1888 if (ra) {
1889 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1890 }
1891
1892 SkASSERT(descSize == desc->getLength());
1893 desc->computeChecksum();
1894
reed@google.com17fb3872011-05-04 14:31:07 +00001895#ifdef TEST_DESC
1896 {
1897 // Check that we completely write the bytes in desc (our key), and that
1898 // there are no uninitialized bytes. If there were, then we would get
1899 // false-misses (or worse, false-hits) in our fontcache.
1900 //
1901 // We do this buy filling 2 others, one with 0s and the other with 1s
1902 // and create those, and then check that all 3 are identical.
1903 SkAutoDescriptor ad1(descSize);
1904 SkAutoDescriptor ad2(descSize);
1905 SkDescriptor* desc1 = ad1.getDesc();
1906 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001907
reed@google.com17fb3872011-05-04 14:31:07 +00001908 memset(desc1, 0x00, descSize);
1909 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001910
reed@google.com17fb3872011-05-04 14:31:07 +00001911 desc1->init();
1912 desc2->init();
1913 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1914 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001915
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001916#ifdef SK_BUILD_FOR_ANDROID
1917 add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1918 add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1919#endif
1920
reed@google.com17fb3872011-05-04 14:31:07 +00001921 if (pe) {
1922 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1923 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1924 }
1925 if (mf) {
1926 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1927 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1928 }
1929 if (ra) {
1930 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1931 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1932 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001933
reed@google.com17fb3872011-05-04 14:31:07 +00001934 SkASSERT(descSize == desc1->getLength());
1935 SkASSERT(descSize == desc2->getLength());
1936 desc1->computeChecksum();
1937 desc2->computeChecksum();
1938 SkASSERT(!memcmp(desc, desc1, descSize));
1939 SkASSERT(!memcmp(desc, desc2, descSize));
1940 }
1941#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001942
reed@google.com90808e82013-03-19 14:44:54 +00001943 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944}
1945
bungeman@google.com532470f2013-01-22 19:25:14 +00001946SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
1947 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 SkGlyphCache* cache;
bungeman@google.com532470f2013-01-22 19:25:14 +00001949 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950 return cache;
1951}
1952
bungeman@google.com97efada2012-07-30 20:40:50 +00001953/**
1954 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1955 */
1956//static
1957SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1958 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001959 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1960 rec.getPaintGamma(),
1961 rec.getDeviceGamma());
1962 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001963}
1964
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965///////////////////////////////////////////////////////////////////////////////
1966
1967#include "SkStream.h"
1968
reed@android.comaefd2bc2009-03-30 21:02:14 +00001969static uintptr_t asint(const void* p) {
1970 return reinterpret_cast<uintptr_t>(p);
1971}
1972
1973union Scalar32 {
1974 SkScalar fScalar;
1975 uint32_t f32;
1976};
1977
1978static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1979 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1980 Scalar32 tmp;
1981 tmp.fScalar = value;
1982 *ptr = tmp.f32;
1983 return ptr + 1;
1984}
1985
1986static SkScalar read_scalar(const uint32_t*& ptr) {
1987 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1988 Scalar32 tmp;
1989 tmp.f32 = *ptr++;
1990 return tmp.fScalar;
1991}
1992
1993static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1994 SkASSERT(a == (uint8_t)a);
1995 SkASSERT(b == (uint8_t)b);
1996 SkASSERT(c == (uint8_t)c);
1997 SkASSERT(d == (uint8_t)d);
1998 return (a << 24) | (b << 16) | (c << 8) | d;
1999}
2000
2001enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002002 kHasTypeface_FlatFlag = 0x01,
2003 kHasEffects_FlatFlag = 0x02,
2004 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
reed@android.comaefd2bc2009-03-30 21:02:14 +00002005};
2006
2007// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00002008static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00002009 1 * sizeof(SkColor) +
2010 1 * sizeof(uint16_t) +
2011 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002012
2013/* To save space/time, we analyze the paint, and write a truncated version of
2014 it if there are not tricky elements like shaders, etc.
2015 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002017 uint8_t flatFlags = 0;
2018 if (this->getTypeface()) {
2019 flatFlags |= kHasTypeface_FlatFlag;
2020 }
2021 if (asint(this->getPathEffect()) |
2022 asint(this->getShader()) |
2023 asint(this->getXfermode()) |
2024 asint(this->getMaskFilter()) |
2025 asint(this->getColorFilter()) |
2026 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002027 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002028 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002029 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002030 flatFlags |= kHasEffects_FlatFlag;
2031 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002032#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002033 if (this->getPaintOptionsAndroid() != SkPaintOptionsAndroid()) {
2034 flatFlags |= kHasNonDefaultPaintOptionsAndroid_FlatFlag;
2035 }
2036#endif
reed@android.comaefd2bc2009-03-30 21:02:14 +00002037
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002038 if (buffer.isOrderedBinaryBuffer()) {
2039 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2040 uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
2041
2042 ptr = write_scalar(ptr, this->getTextSize());
2043 ptr = write_scalar(ptr, this->getTextScaleX());
2044 ptr = write_scalar(ptr, this->getTextSkewX());
2045 ptr = write_scalar(ptr, this->getStrokeWidth());
2046 ptr = write_scalar(ptr, this->getStrokeMiter());
2047 *ptr++ = this->getColor();
2048 // previously flags:16, textAlign:8, flatFlags:8
2049 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2050 *ptr++ = (this->getFlags() << 16) |
2051 // hinting added later. 0 in this nibble means use the default.
2052 ((this->getHinting()+1) << 12) |
2053 (this->getTextAlign() << 8) |
2054 flatFlags;
2055 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2056 this->getStyle(), this->getTextEncoding());
2057 } else {
2058 buffer.writeScalar(fTextSize);
2059 buffer.writeScalar(fTextScaleX);
2060 buffer.writeScalar(fTextSkewX);
2061 buffer.writeScalar(fWidth);
2062 buffer.writeScalar(fMiterLimit);
2063 buffer.writeColor(fColor);
2064 buffer.writeUInt(fFlags);
2065 buffer.writeUInt(fHinting);
2066 buffer.writeUInt(fTextAlign);
2067 buffer.writeUInt(flatFlags);
2068
2069 buffer.writeUInt(fCapType);
2070 buffer.writeUInt(fJoinType);
2071 buffer.writeUInt(fStyle);
2072 buffer.writeUInt(fTextEncoding);
2073 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002074
2075 // now we're done with ptr and the (pre)reserved space. If we need to write
2076 // additional fields, use the buffer directly
2077 if (flatFlags & kHasTypeface_FlatFlag) {
2078 buffer.writeTypeface(this->getTypeface());
2079 }
2080 if (flatFlags & kHasEffects_FlatFlag) {
2081 buffer.writeFlattenable(this->getPathEffect());
2082 buffer.writeFlattenable(this->getShader());
2083 buffer.writeFlattenable(this->getXfermode());
2084 buffer.writeFlattenable(this->getMaskFilter());
2085 buffer.writeFlattenable(this->getColorFilter());
2086 buffer.writeFlattenable(this->getRasterizer());
2087 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002088 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00002089
2090 if (fAnnotation) {
2091 buffer.writeBool(true);
2092 fAnnotation->writeToBuffer(buffer);
2093 } else {
2094 buffer.writeBool(false);
2095 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002096 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002097#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002098 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2099 this->getPaintOptionsAndroid().flatten(buffer);
2100 }
2101#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102}
2103
2104void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002105 uint8_t flatFlags = 0;
2106 if (buffer.isOrderedBinaryBuffer()) {
2107 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2108 const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
2109 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002110
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002111 // the order we read must match the order we wrote in flatten()
2112 this->setTextSize(read_scalar(pod));
2113 this->setTextScaleX(read_scalar(pod));
2114 this->setTextSkewX(read_scalar(pod));
2115 this->setStrokeWidth(read_scalar(pod));
2116 this->setStrokeMiter(read_scalar(pod));
2117 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002118
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002119 // previously flags:16, textAlign:8, flatFlags:8
2120 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2121 uint32_t tmp = *pod++;
2122 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00002123
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002124 // hinting added later. 0 in this nibble means use the default.
2125 uint32_t hinting = (tmp >> 12) & 0xF;
2126 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
bungeman@google.com24babf42011-11-07 16:33:40 +00002127
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002128 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002129
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002130 flatFlags = tmp & 0xFF;
2131
2132 tmp = *pod++;
2133 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2134 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2135 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2136 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
2137 } else {
2138 this->setTextSize(buffer.readScalar());
2139 this->setTextScaleX(buffer.readScalar());
2140 this->setTextSkewX(buffer.readScalar());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002141 // Skip the hinting scalar factor, which is not supported.
2142 buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002143 this->setStrokeWidth(buffer.readScalar());
2144 this->setStrokeMiter(buffer.readScalar());
2145 this->setColor(buffer.readColor());
2146 this->setFlags(buffer.readUInt());
2147 this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
2148 this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
2149 flatFlags = buffer.readUInt();
2150
2151 this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
2152 this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
2153 this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
2154 this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
2155 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002156
2157 if (flatFlags & kHasTypeface_FlatFlag) {
2158 this->setTypeface(buffer.readTypeface());
2159 } else {
2160 this->setTypeface(NULL);
2161 }
2162
2163 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002164 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2165 SkSafeUnref(this->setShader(buffer.readShader()));
2166 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2167 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2168 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2169 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2170 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2171 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002172
reed@google.com0cd2ac62013-10-14 20:02:44 +00002173 if (buffer.readBool()) {
2174 this->setAnnotation(SkNEW_ARGS(SkAnnotation, (buffer)))->unref();
2175 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002176 } else {
2177 this->setPathEffect(NULL);
2178 this->setShader(NULL);
2179 this->setXfermode(NULL);
2180 this->setMaskFilter(NULL);
2181 this->setColorFilter(NULL);
2182 this->setRasterizer(NULL);
2183 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002184 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002185 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002186
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002187#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002188 this->setPaintOptionsAndroid(SkPaintOptionsAndroid());
2189#endif
2190 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2191 SkPaintOptionsAndroid options;
2192 options.unflatten(buffer);
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002193#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002194 this->setPaintOptionsAndroid(options);
2195#endif
2196 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
2199///////////////////////////////////////////////////////////////////////////////
2200
reed@google.com82065d62011-02-07 15:30:46 +00002201SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002202 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 SkRefCnt_SafeAssign(fShader, shader);
2204 return shader;
2205}
2206
reed@google.com82065d62011-02-07 15:30:46 +00002207SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002208 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209 SkRefCnt_SafeAssign(fColorFilter, filter);
2210 return filter;
2211}
2212
reed@google.com82065d62011-02-07 15:30:46 +00002213SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002214 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 SkRefCnt_SafeAssign(fXfermode, mode);
2216 return mode;
2217}
2218
reed@android.com0baf1932009-06-24 12:41:42 +00002219SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002220 SkSafeUnref(fXfermode);
2221 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002222 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002223 return fXfermode;
2224}
2225
reed@google.com82065d62011-02-07 15:30:46 +00002226SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002227 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228 SkRefCnt_SafeAssign(fPathEffect, effect);
2229 return effect;
2230}
2231
reed@google.com82065d62011-02-07 15:30:46 +00002232SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002233 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002234 SkRefCnt_SafeAssign(fMaskFilter, filter);
2235 return filter;
2236}
2237
reed@google.com82065d62011-02-07 15:30:46 +00002238///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239
reed@google.com4bbdeac2013-01-24 21:03:11 +00002240bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2241 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002242 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243
reed@google.comfd4be262012-05-25 01:04:12 +00002244 const SkPath* srcPtr = &src;
2245 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002246
reed@google.com4bbdeac2013-01-24 21:03:11 +00002247 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002248 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 }
2250
reed@google.comfd4be262012-05-25 01:04:12 +00002251 if (!rec.applyToPath(dst, *srcPtr)) {
2252 if (srcPtr == &tmpPath) {
2253 // If path's were copy-on-write, this trick would not be needed.
2254 // As it is, we want to save making a deep-copy from tmpPath -> dst
2255 // since we know we're just going to delete tmpPath when we return,
2256 // so the swap saves that copy.
2257 dst->swap(tmpPath);
2258 } else {
2259 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
2261 }
reed@google.comfd4be262012-05-25 01:04:12 +00002262 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
reed@google.come4f10a72012-05-15 20:47:50 +00002265const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002266 SkRect* storage,
2267 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002269
reed@google.come4f10a72012-05-15 20:47:50 +00002270 const SkRect* src = &origSrc;
2271
reed@google.com9efd9a02012-01-30 15:41:43 +00002272 if (this->getLooper()) {
2273 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002274 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002275 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002277
reed@google.come4f10a72012-05-15 20:47:50 +00002278 SkRect tmpSrc;
2279 if (this->getPathEffect()) {
2280 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2281 src = &tmpSrc;
2282 }
2283
reed@google.coma584aed2012-05-16 14:06:02 +00002284 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002285 // since we're stroked, outset the rect by the radius (and join type)
2286 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2287 if (0 == radius) { // hairline
2288 radius = SK_Scalar1;
2289 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2290 SkScalar scale = this->getStrokeMiter();
2291 if (scale > SK_Scalar1) {
2292 radius = SkScalarMul(radius, scale);
2293 }
2294 }
reed@google.come4f10a72012-05-15 20:47:50 +00002295 storage->set(src->fLeft - radius, src->fTop - radius,
2296 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002297 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002298 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002299 }
2300
reed@google.com9efd9a02012-01-30 15:41:43 +00002301 if (this->getMaskFilter()) {
2302 this->getMaskFilter()->computeFastBounds(*storage, storage);
2303 }
2304
reed@android.comd252db02009-04-01 18:31:44 +00002305 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306}
2307
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002308#ifdef SK_DEVELOPER
2309void SkPaint::toString(SkString* str) const {
2310 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2311
2312 SkTypeface* typeface = this->getTypeface();
2313 if (NULL != typeface) {
2314 SkDynamicMemoryWStream ostream;
2315 typeface->serialize(&ostream);
2316 SkAutoTUnref<SkData> data(ostream.copyToData());
2317
2318 SkMemoryStream stream(data);
2319 SkFontDescriptor descriptor(&stream);
2320
2321 str->append("<dt>Font Family Name:</dt><dd>");
2322 str->append(descriptor.getFamilyName());
2323 str->append("</dd><dt>Font Full Name:</dt><dd>");
2324 str->append(descriptor.getFullName());
2325 str->append("</dd><dt>Font PS Name:</dt><dd>");
2326 str->append(descriptor.getPostscriptName());
2327 str->append("</dd><dt>Font File Name:</dt><dd>");
2328 str->append(descriptor.getFontFileName());
2329 str->append("</dd>");
2330 }
2331
2332 str->append("<dt>TextSize:</dt><dd>");
2333 str->appendScalar(this->getTextSize());
2334 str->append("</dd>");
2335
2336 str->append("<dt>TextScaleX:</dt><dd>");
2337 str->appendScalar(this->getTextScaleX());
2338 str->append("</dd>");
2339
2340 str->append("<dt>TextSkewX:</dt><dd>");
2341 str->appendScalar(this->getTextSkewX());
2342 str->append("</dd>");
2343
2344 SkPathEffect* pathEffect = this->getPathEffect();
2345 if (NULL != pathEffect) {
2346 str->append("<dt>PathEffect:</dt><dd>");
2347 str->append("</dd>");
2348 }
2349
2350 SkShader* shader = this->getShader();
2351 if (NULL != shader) {
2352 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002353 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002354 str->append("</dd>");
2355 }
2356
2357 SkXfermode* xfer = this->getXfermode();
2358 if (NULL != xfer) {
2359 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002360 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002361 str->append("</dd>");
2362 }
2363
2364 SkMaskFilter* maskFilter = this->getMaskFilter();
2365 if (NULL != maskFilter) {
2366 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002367 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002368 str->append("</dd>");
2369 }
2370
2371 SkColorFilter* colorFilter = this->getColorFilter();
2372 if (NULL != colorFilter) {
2373 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002374 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002375 str->append("</dd>");
2376 }
2377
2378 SkRasterizer* rasterizer = this->getRasterizer();
2379 if (NULL != rasterizer) {
2380 str->append("<dt>Rasterizer:</dt><dd>");
2381 str->append("</dd>");
2382 }
2383
2384 SkDrawLooper* looper = this->getLooper();
2385 if (NULL != looper) {
2386 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002387 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002388 str->append("</dd>");
2389 }
2390
2391 SkImageFilter* imageFilter = this->getImageFilter();
2392 if (NULL != imageFilter) {
2393 str->append("<dt>ImageFilter:</dt><dd>");
2394 str->append("</dd>");
2395 }
2396
2397 SkAnnotation* annotation = this->getAnnotation();
2398 if (NULL != annotation) {
2399 str->append("<dt>Annotation:</dt><dd>");
2400 str->append("</dd>");
2401 }
2402
2403 str->append("<dt>Color:</dt><dd>0x");
2404 SkColor color = this->getColor();
2405 str->appendHex(color);
2406 str->append("</dd>");
2407
2408 str->append("<dt>Stroke Width:</dt><dd>");
2409 str->appendScalar(this->getStrokeWidth());
2410 str->append("</dd>");
2411
2412 str->append("<dt>Stroke Miter:</dt><dd>");
2413 str->appendScalar(this->getStrokeMiter());
2414 str->append("</dd>");
2415
2416 str->append("<dt>Flags:</dt><dd>(");
2417 if (this->getFlags()) {
2418 bool needSeparator = false;
2419 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002420 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2421 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2422 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2423 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2424 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2425 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2426 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2427 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2428 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2429 "EmbeddedBitmapText", &needSeparator);
2430 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2431 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2432 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2433 "GenA8FromLCD", &needSeparator);
2434 } else {
2435 str->append("None");
2436 }
2437 str->append(")</dd>");
2438
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002439 str->append("<dt>FilterLevel:</dt><dd>");
2440 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2441 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2442 str->append("</dd>");
2443
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002444 str->append("<dt>TextAlign:</dt><dd>");
2445 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2446 str->append(gTextAlignStrings[this->getTextAlign()]);
2447 str->append("</dd>");
2448
2449 str->append("<dt>CapType:</dt><dd>");
2450 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2451 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2452 str->append("</dd>");
2453
2454 str->append("<dt>JoinType:</dt><dd>");
2455 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2456 str->append(gJoinStrings[this->getStrokeJoin()]);
2457 str->append("</dd>");
2458
2459 str->append("<dt>Style:</dt><dd>");
2460 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2461 str->append(gStyleStrings[this->getStyle()]);
2462 str->append("</dd>");
2463
2464 str->append("<dt>TextEncoding:</dt><dd>");
2465 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2466 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2467 str->append("</dd>");
2468
2469 str->append("<dt>Hinting:</dt><dd>");
2470 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2471 str->append(gHintingStrings[this->getHinting()]);
2472 str->append("</dd>");
2473
2474 str->append("</dd></dl></dl>");
2475}
2476#endif
2477
2478
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002479///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002481static bool has_thick_frame(const SkPaint& paint) {
2482 return paint.getStrokeWidth() > 0 &&
2483 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002484}
2485
2486SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2487 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002488 bool applyStrokeAndPathEffects)
2489 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002490 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2491 true);
2492
djsollen@google.com166e6532012-03-20 14:24:38 +00002493 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2495
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002496 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002498 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002499
djsollen@google.com166e6532012-03-20 14:24:38 +00002500 // can't use our canonical size if we need to apply patheffects
2501 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002502 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2503 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002504 if (has_thick_frame(fPaint)) {
2505 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2506 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002507 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002508 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002509 }
reed@google.com72cf4922011-01-04 19:58:20 +00002510
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002511 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002512 fPaint.setStyle(SkPaint::kFill_Style);
2513 fPaint.setPathEffect(NULL);
2514 }
2515
bungeman@google.com532470f2013-01-22 19:25:14 +00002516 fCache = fPaint.detachCache(NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517
2518 SkPaint::Style style = SkPaint::kFill_Style;
2519 SkPathEffect* pe = NULL;
2520
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002521 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002522 style = paint.getStyle(); // restore
2523 pe = paint.getPathEffect(); // restore
2524 }
2525 fPaint.setStyle(style);
2526 fPaint.setPathEffect(pe);
2527 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2528
2529 // now compute fXOffset if needed
2530
2531 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002532 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002533 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002534 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2535 &count, NULL), fScale);
2536 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002537 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002538 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539 xOffset = -width;
2540 }
2541 fXPos = xOffset;
2542 fPrevAdvance = 0;
2543
2544 fText = text;
2545 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002546
reed@google.com44da42e2011-11-10 20:04:47 +00002547 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548}
2549
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002550SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551 SkGlyphCache::AttachCache(fCache);
2552}
2553
reed@google.com7b4531f2012-08-07 15:53:00 +00002554bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2555 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2557
2558 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002559 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002560
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002561 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002562 if (path) {
2563 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002564 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002565 } else {
2566 if (path) {
2567 *path = NULL;
2568 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002570 if (xpos) {
2571 *xpos = fXPos;
2572 }
2573 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002574 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002575 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002576}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002577
2578///////////////////////////////////////////////////////////////////////////////
2579
2580bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002581 if (fLooper) {
2582 return false;
2583 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002584 SkXfermode::Mode mode;
2585 if (SkXfermode::AsMode(fXfermode, &mode)) {
2586 switch (mode) {
2587 case SkXfermode::kSrcOver_Mode:
2588 case SkXfermode::kSrcATop_Mode:
2589 case SkXfermode::kDstOut_Mode:
2590 case SkXfermode::kDstOver_Mode:
2591 case SkXfermode::kPlus_Mode:
2592 return 0 == this->getAlpha();
2593 case SkXfermode::kDst_Mode:
2594 return true;
2595 default:
2596 break;
2597 }
2598 }
2599 return false;
2600}