blob: 94ffa8d31bc1327a471bbc2c0ae6de0435872ab6 [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
reed@google.com8f4d2302013-12-17 16:44:46 +00001013static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
1014 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
1015}
reed@android.come88f5512010-03-19 14:42:28 +00001016
reed@google.com44da42e2011-11-10 20:04:47 +00001017static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +00001018 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 bounds->join(SkIntToScalar(g.fLeft) + sx,
1020 SkIntToScalar(g.fTop),
1021 SkIntToScalar(g.fLeft + g.fWidth) + sx,
1022 SkIntToScalar(g.fTop + g.fHeight));
1023}
1024
reed@google.com44da42e2011-11-10 20:04:47 +00001025static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
1026 SkScalar sy = Sk48Dot16ToScalar(dy);
1027 bounds->join(SkIntToScalar(g.fLeft),
1028 SkIntToScalar(g.fTop) + sy,
1029 SkIntToScalar(g.fLeft + g.fWidth),
1030 SkIntToScalar(g.fTop + g.fHeight) + sy);
1031}
1032
1033typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
1034
1035// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
1036static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
1037 SkASSERT(0 == xyIndex || 1 == xyIndex);
1038 return (&glyph.fAdvanceX)[xyIndex];
1039}
1040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041SkScalar SkPaint::measure_text(SkGlyphCache* cache,
1042 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001043 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001045 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001047 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001049 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 return 0;
1051 }
1052
1053 SkMeasureCacheProc glyphCacheProc;
1054 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1055 NULL != bounds);
1056
reed@google.com44da42e2011-11-10 20:04:47 +00001057 int xyIndex;
1058 JoinBoundsProc joinBoundsProc;
1059 if (this->isVerticalText()) {
1060 xyIndex = 1;
1061 joinBoundsProc = join_bounds_y;
1062 } else {
1063 xyIndex = 0;
1064 joinBoundsProc = join_bounds_x;
1065 }
1066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 int n = 1;
1068 const char* stop = (const char*)text + byteLength;
1069 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001070 // our accumulated fixed-point advances might overflow 16.16, so we use
1071 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1072 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001073 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074
1075 SkAutoKern autokern;
1076
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001077 if (NULL == bounds) {
1078 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 int rsb;
1080 for (; text < stop; n++) {
1081 rsb = g->fRsbDelta;
1082 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001083 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001085 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001087 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 }
1089 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001090 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001092 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 int rsb;
1094 for (; text < stop; n++) {
1095 rsb = g->fRsbDelta;
1096 g = &glyphCacheProc(cache, &text);
1097 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001098 joinBoundsProc(*g, bounds, x);
1099 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001101 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 for (; text < stop; n++) {
1103 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001104 joinBoundsProc(*g, bounds, x);
1105 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 }
1107 }
1108 }
1109 SkASSERT(text == stop);
1110
1111 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001112 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113}
1114
1115SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001116 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 const char* text = (const char*)textData;
1118 SkASSERT(text != NULL || length == 0);
1119
reed@google.comed43dff2013-06-04 16:56:27 +00001120 SkCanonicalizePaint canon(*this);
1121 const SkPaint& paint = canon.getPaint();
1122 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001123
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001124 SkMatrix zoomMatrix, *zoomPtr = NULL;
1125 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 zoomMatrix.setScale(zoom, zoom);
1127 zoomPtr = &zoomMatrix;
1128 }
1129
reed@google.comed43dff2013-06-04 16:56:27 +00001130 SkAutoGlyphCache autoCache(paint, NULL, zoomPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 SkGlyphCache* cache = autoCache.getCache();
1132
1133 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001134
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001135 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 int tempCount;
1137
reed@google.comed43dff2013-06-04 16:56:27 +00001138 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001139 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001141 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1143 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1144 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1145 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1146 }
1147 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001148 } else if (bounds) {
1149 // ensure that even if we don't measure_text we still update the bounds
1150 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 }
1152 return width;
1153}
1154
1155typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1156
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001157static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 return text < stop;
1159}
1160
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001161static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 return text > stop;
1163}
1164
1165static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001166 const char** text, size_t length,
1167 const char** stop) {
1168 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 *stop = *text + length;
1170 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001171 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172 // text should point to the end of the buffer, and stop to the beginning
1173 *stop = *text;
1174 *text += length;
1175 return backward_textBufferPred;
1176 }
1177}
1178
1179size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1180 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001181 TextBufferDirection tbd) const {
1182 if (0 == length || 0 >= maxWidth) {
1183 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001185 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 return 0;
1187 }
1188
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001189 if (0 == fTextSize) {
1190 if (measuredWidth) {
1191 *measuredWidth = 0;
1192 }
1193 return length;
1194 }
1195
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 SkASSERT(textD != NULL);
1197 const char* text = (const char*)textD;
1198
reed@google.comed43dff2013-06-04 16:56:27 +00001199 SkCanonicalizePaint canon(*this);
1200 const SkPaint& paint = canon.getPaint();
1201 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202
reed@google.comed43dff2013-06-04 16:56:27 +00001203 // adjust max in case we changed the textSize in paint
1204 if (scale) {
1205 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 }
reed@google.com72cf4922011-01-04 19:58:20 +00001207
reed@google.comed43dff2013-06-04 16:56:27 +00001208 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 SkGlyphCache* cache = autoCache.getCache();
1210
reed@google.comed43dff2013-06-04 16:56:27 +00001211 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 const char* stop;
1213 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001214 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001215 // use 64bits for our accumulator, to avoid overflowing 16.16
1216 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1217 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
1219 SkAutoKern autokern;
1220
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001221 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001223 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 const char* curr = text;
1225 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001226 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001227 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 width -= x;
1229 text = curr;
1230 break;
1231 }
1232 rsb = g.fRsbDelta;
1233 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001234 } else {
1235 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001237 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001238 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 width -= x;
1240 text = curr;
1241 break;
1242 }
1243 }
1244 }
reed@google.com72cf4922011-01-04 19:58:20 +00001245
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001246 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001247 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001248 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 *measuredWidth = scalarWidth;
1252 }
reed@google.com72cf4922011-01-04 19:58:20 +00001253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 // return the number of bytes measured
1255 return (kForward_TextBufferDirection == tbd) ?
1256 text - stop + length : stop - text + length;
1257}
1258
1259///////////////////////////////////////////////////////////////////////////////
1260
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001261static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001262 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 return false; // don't detach the cache
1264}
1265
reed@google.com90808e82013-03-19 14:44:54 +00001266static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1267 void* context) {
1268 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269}
1270
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001271SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001272 SkCanonicalizePaint canon(*this);
1273 const SkPaint& paint = canon.getPaint();
1274 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001275
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001276 SkMatrix zoomMatrix, *zoomPtr = NULL;
1277 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 zoomMatrix.setScale(zoom, zoom);
1279 zoomPtr = &zoomMatrix;
1280 }
1281
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001283 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001285 }
reed@google.com72cf4922011-01-04 19:58:20 +00001286
reed@google.comed43dff2013-06-04 16:56:27 +00001287 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001289 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1291 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1292 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1293 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1294 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001295 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1296 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1297 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1298 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 }
1300 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1301}
1302
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001303///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001305static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 bounds->set(g.fLeft * scale,
1307 g.fTop * scale,
1308 (g.fLeft + g.fWidth) * scale,
1309 (g.fTop + g.fHeight) * scale);
1310}
1311
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001312int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1313 SkScalar widths[], SkRect bounds[]) const {
1314 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317
1318 SkASSERT(NULL != textData);
1319
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001320 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323
reed@google.comed43dff2013-06-04 16:56:27 +00001324 SkCanonicalizePaint canon(*this);
1325 const SkPaint& paint = canon.getPaint();
1326 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327
reed@google.comed43dff2013-06-04 16:56:27 +00001328 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 SkGlyphCache* cache = autoCache.getCache();
1330 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001331 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 NULL != bounds);
1333
1334 const char* text = (const char*)textData;
1335 const char* stop = text + byteLength;
1336 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001337 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001339 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 // we adjust the widths returned here through auto-kerning
1341 SkAutoKern autokern;
1342 SkFixed prevWidth = 0;
1343
1344 if (scale) {
1345 while (text < stop) {
1346 const SkGlyph& g = glyphCacheProc(cache, &text);
1347 if (widths) {
1348 SkFixed adjust = autokern.adjust(g);
1349
1350 if (count > 0) {
1351 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1352 *widths++ = SkScalarMul(w, scale);
1353 }
reed@google.com44da42e2011-11-10 20:04:47 +00001354 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 }
1356 if (bounds) {
1357 set_bounds(g, bounds++, scale);
1358 }
1359 ++count;
1360 }
1361 if (count > 0 && widths) {
1362 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1363 }
1364 } else {
1365 while (text < stop) {
1366 const SkGlyph& g = glyphCacheProc(cache, &text);
1367 if (widths) {
1368 SkFixed adjust = autokern.adjust(g);
1369
1370 if (count > 0) {
1371 *widths++ = SkFixedToScalar(prevWidth + adjust);
1372 }
reed@google.com44da42e2011-11-10 20:04:47 +00001373 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 }
1375 if (bounds) {
1376 set_bounds(g, bounds++);
1377 }
1378 ++count;
1379 }
1380 if (count > 0 && widths) {
1381 *widths = SkFixedToScalar(prevWidth);
1382 }
1383 }
1384 } else { // no devkern
1385 if (scale) {
1386 while (text < stop) {
1387 const SkGlyph& g = glyphCacheProc(cache, &text);
1388 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001389 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 scale);
1391 }
1392 if (bounds) {
1393 set_bounds(g, bounds++, scale);
1394 }
1395 ++count;
1396 }
1397 } else {
1398 while (text < stop) {
1399 const SkGlyph& g = glyphCacheProc(cache, &text);
1400 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001401 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 }
1403 if (bounds) {
1404 set_bounds(g, bounds++);
1405 }
1406 ++count;
1407 }
1408 }
1409 }
1410
1411 SkASSERT(text == stop);
1412 return count;
1413}
1414
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001415///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416
1417#include "SkDraw.h"
1418
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001419void SkPaint::getTextPath(const void* textData, size_t length,
1420 SkScalar x, SkScalar y, SkPath* path) const {
1421 SkASSERT(length == 0 || textData != NULL);
1422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001424 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001426 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427
djsollen@google.com166e6532012-03-20 14:24:38 +00001428 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 SkMatrix matrix;
1430 SkScalar prevXPos = 0;
1431
1432 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1433 matrix.postTranslate(x, y);
1434 path->reset();
1435
1436 SkScalar xpos;
1437 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001438 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001440 if (iterPath) {
1441 path->addPath(*iterPath, matrix);
1442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 prevXPos = xpos;
1444 }
1445}
1446
reed@google.comca0062e2012-07-20 11:20:32 +00001447void SkPaint::getPosTextPath(const void* textData, size_t length,
1448 const SkPoint pos[], SkPath* path) const {
1449 SkASSERT(length == 0 || textData != NULL);
1450
1451 const char* text = (const char*)textData;
1452 if (text == NULL || length == 0 || path == NULL) {
1453 return;
1454 }
1455
1456 SkTextToPathIter iter(text, length, *this, false);
1457 SkMatrix matrix;
1458 SkPoint prevPos;
1459 prevPos.set(0, 0);
1460
1461 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1462 path->reset();
1463
1464 unsigned int i = 0;
1465 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001466 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001467 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001468 if (iterPath) {
1469 path->addPath(*iterPath, matrix);
1470 }
reed@google.comca0062e2012-07-20 11:20:32 +00001471 prevPos = pos[i];
1472 i++;
1473 }
1474}
1475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476static void add_flattenable(SkDescriptor* desc, uint32_t tag,
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001477 SkOrderedWriteBuffer* buffer) {
1478 buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
reed@google.com2739b272011-09-28 17:26:42 +00001481// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001482static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001485 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001486 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001487 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001488 }
1489
reed@google.com65dd8f82011-03-14 13:31:16 +00001490 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001491 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001492 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001493
1494 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495}
1496
reed@android.com1cdcb512009-08-24 19:11:00 +00001497// if linear-text is on, then we force hinting to be off (since that's sort of
1498// the point of linear-text.
1499static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1500 SkPaint::Hinting h = paint.getHinting();
1501 if (paint.isLinearText()) {
1502 h = SkPaint::kNo_Hinting;
1503 }
1504 return h;
1505}
1506
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001507// return true if the paint is just a single color (i.e. not a shader). If its
1508// a shader, then we can't compute a const luminance for it :(
1509static bool justAColor(const SkPaint& paint, SkColor* color) {
1510 if (paint.getShader()) {
1511 return false;
1512 }
1513 SkColor c = paint.getColor();
1514 if (paint.getColorFilter()) {
1515 c = paint.getColorFilter()->filterColor(c);
1516 }
1517 if (color) {
1518 *color = c;
1519 }
1520 return true;
1521}
1522
reed@google.comce6dbb62012-02-10 22:01:45 +00001523static SkColor computeLuminanceColor(const SkPaint& paint) {
1524 SkColor c;
1525 if (!justAColor(paint, &c)) {
1526 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1527 }
1528 return c;
1529}
reed@google.com813d38b2012-02-13 21:37:57 +00001530
reed@google.comdd43df92012-02-15 14:50:29 +00001531#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1532
reed@google.com4f79b9b2011-09-13 13:23:26 +00001533// Beyond this size, LCD doesn't appreciably improve quality, but it always
1534// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001535#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1536 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1537#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001538
1539static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1540 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1541 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1542 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001543 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001544}
1545
reed@google.com72cf4922011-01-04 19:58:20 +00001546/*
1547 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1548 * that vary only slightly when we create our key into the font cache, since the font scaler
1549 * typically returns the same looking resuts for tiny changes in the matrix.
1550 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001551static SkScalar sk_relax(SkScalar x) {
1552 int n = sk_float_round2int(x * 1024);
1553 return n / 1024.0f;
1554}
1555
reed@android.com36a4c2a2009-07-22 19:52:11 +00001556void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001557 const SkDeviceProperties* deviceProperties,
1558 const SkMatrix* deviceMatrix,
1559 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001560 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001562 SkTypeface* typeface = paint.getTypeface();
1563 if (NULL == typeface) {
1564 typeface = SkTypeface::GetDefaultTypeface();
1565 }
1566 rec->fOrigFontID = typeface->uniqueID();
reed@google.com7d26c592011-06-13 13:01:10 +00001567 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 rec->fTextSize = paint.getTextSize();
1569 rec->fPreScaleX = paint.getTextScaleX();
1570 rec->fPreSkewX = paint.getTextSkewX();
1571
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001572 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001573 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1574 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1575 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1576 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001577 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1579 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1580 }
reed@google.com72cf4922011-01-04 19:58:20 +00001581
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 SkPaint::Style style = paint.getStyle();
1583 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001584
reed@google.comffe49f52011-11-22 19:42:41 +00001585 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001586
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001587 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001588#ifdef SK_USE_FREETYPE_EMBOLDEN
1589 flags |= SkScalerContext::kEmbolden_Flag;
1590#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001591 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1592 kStdFakeBoldInterpKeys,
1593 kStdFakeBoldInterpValues,
1594 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001596
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001597 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598 style = SkPaint::kStrokeAndFill_Style;
1599 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001600 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001602 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001603#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 }
1605
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001606 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001608 }
reed@google.com72cf4922011-01-04 19:58:20 +00001609
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001610 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 rec->fFrameWidth = strokeWidth;
1612 rec->fMiterLimit = paint.getStrokeMiter();
1613 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1614
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001615 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001617 }
1618 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 rec->fFrameWidth = 0;
1620 rec->fMiterLimit = 0;
1621 rec->fStrokeJoin = 0;
1622 }
1623
reed@google.com02b53312011-05-18 19:00:53 +00001624 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1625
bungeman@google.com532470f2013-01-22 19:25:14 +00001626 SkDeviceProperties::Geometry geometry = deviceProperties
1627 ? deviceProperties->fGeometry
1628 : SkDeviceProperties::Geometry::MakeDefault();
1629 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
1630 if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001631 // eeek, can't support LCD
1632 rec->fMaskFormat = SkMask::kA8_Format;
1633 } else {
bungeman@google.com532470f2013-01-22 19:25:14 +00001634 if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
reed@google.com02b53312011-05-18 19:00:53 +00001635 flags |= SkScalerContext::kLCD_Vertical_Flag;
1636 }
bungeman@google.com532470f2013-01-22 19:25:14 +00001637 if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
reed@google.com02b53312011-05-18 19:00:53 +00001638 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1639 }
1640 }
1641 }
1642
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001643 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001644 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001645 }
1646 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001647 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001648 }
1649 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001650 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001651 }
reed@google.com830a23e2011-11-10 15:20:49 +00001652 if (paint.isVerticalText()) {
1653 flags |= SkScalerContext::kVertical_Flag;
1654 }
reed@google.com8351aab2012-01-18 17:06:35 +00001655 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1656 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1657 }
reed@google.com02b53312011-05-18 19:00:53 +00001658 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001659
reed@google.comffe49f52011-11-22 19:42:41 +00001660 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001661 rec->setHinting(computeHinting(paint));
1662
bungeman@google.com97efada2012-07-30 20:40:50 +00001663 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001664
1665 if (NULL == deviceProperties) {
1666 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1667 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1668 } else {
1669 rec->setDeviceGamma(deviceProperties->fGamma);
1670
1671 //For now always set the paint gamma equal to the device gamma.
1672 //The math in SkMaskGamma can handle them being different,
1673 //but it requires superluminous masks when
1674 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1675 rec->setPaintGamma(deviceProperties->fGamma);
1676 }
1677
1678#ifdef SK_GAMMA_CONTRAST
1679 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001680#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001681 /**
1682 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1683 * With lower values small text appears washed out (though correctly so).
1684 * With higher values lcd fringing is worse and the smoothing effect of
1685 * partial coverage is diminished.
1686 */
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001687 rec->setContrast(0.5f);
bungeman@google.com97efada2012-07-30 20:40:50 +00001688#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001689
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001690 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001691
reed@android.com36a4c2a2009-07-22 19:52:11 +00001692 /* Allow the fonthost to modify our rec before we use it as a key into the
1693 cache. This way if we're asking for something that they will ignore,
1694 they can modify our rec up front, so we don't create duplicate cache
1695 entries.
1696 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001697 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001698
reed@google.com10d2d4d2012-03-01 22:32:51 +00001699 // be sure to call PostMakeRec(rec) before you actually use it!
1700}
1701
1702/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001703 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1704 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1705 * to hold it until the returned pointer is refed or forgotten.
1706 */
1707SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1708
bungeman@google.comae30f562012-09-11 18:44:55 +00001709static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001710static SkMaskGamma* gMaskGamma = NULL;
1711static SkScalar gContrast = SK_ScalarMin;
1712static SkScalar gPaintGamma = SK_ScalarMin;
1713static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001714/**
1715 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1716 * the returned SkMaskGamma pointer is refed or forgotten.
1717 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001718static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001719 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1720 if (NULL == gLinearMaskGamma) {
1721 gLinearMaskGamma = SkNEW(SkMaskGamma);
1722 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001723 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001724 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001725 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1726 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001727 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001728 gContrast = contrast;
1729 gPaintGamma = paintGamma;
1730 gDeviceGamma = deviceGamma;
1731 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001732 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001733}
1734
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001735/*static*/ void SkPaint::Term() {
1736 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1737
bungeman@google.comae30f562012-09-11 18:44:55 +00001738 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001739 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001740 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001741 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001742 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1743 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1744 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001745}
1746
bungeman@google.com97efada2012-07-30 20:40:50 +00001747/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001748 * We ensure that the rec is self-consistent and efficient (where possible)
1749 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001750void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001751 /**
1752 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001753 * limits the number of unique entries, and the scaler will only look at
1754 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001755 */
reed@google.comdd43df92012-02-15 14:50:29 +00001756 switch (rec->fMaskFormat) {
1757 case SkMask::kLCD16_Format:
1758 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001759 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001760 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001761 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001762 break;
1763 }
1764 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001765 // filter down the luminance to a single component, since A8 can't
1766 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001767
bungeman@google.com97efada2012-07-30 20:40:50 +00001768 SkColor color = rec->getLuminanceColor();
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001769 U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001770 //If we are asked to look like LCD, look like LCD.
1771 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1772 // HACK: Prevents green from being pre-blended as white.
1773 lum -= ((255 - lum) * lum) / 255;
1774 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001775
reed@google.comdd43df92012-02-15 14:50:29 +00001776 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001777 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001778 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001779 break;
1780 }
1781 case SkMask::kBW_Format:
1782 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001783 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001784 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001785 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
1788#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1789
reed@google.com17fb3872011-05-04 14:31:07 +00001790#ifdef SK_DEBUG
1791 #define TEST_DESC
1792#endif
1793
reed@google.comffe49f52011-11-22 19:42:41 +00001794/*
1795 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1796 * by gamma correction, so we jam the luminance field to 0 (most common value
1797 * for black text) in hopes that we get a cache hit easier. A better solution
1798 * would be for the fontcache lookup to know to ignore the luminance field
1799 * entirely, but not sure how to do that and keep it fast.
1800 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001801void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1802 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001803 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001804 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 SkScalerContext::Rec rec;
1806
bungeman@google.com532470f2013-01-22 19:25:14 +00001807 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001808 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001809 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001810 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811
1812 size_t descSize = sizeof(rec);
1813 int entryCount = 1;
1814 SkPathEffect* pe = this->getPathEffect();
1815 SkMaskFilter* mf = this->getMaskFilter();
1816 SkRasterizer* ra = this->getRasterizer();
1817
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001818 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1819 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1820 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821
1822 if (pe) {
1823 peBuffer.writeFlattenable(pe);
1824 descSize += peBuffer.size();
1825 entryCount += 1;
1826 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1827 // seems like we could support kLCD as well at this point...
1828 }
1829 if (mf) {
1830 mfBuffer.writeFlattenable(mf);
1831 descSize += mfBuffer.size();
1832 entryCount += 1;
1833 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001834 /* Pre-blend is not currently applied to filtered text.
1835 The primary filter is blur, for which contrast makes no sense,
1836 and for which the destination guess error is more visible.
1837 Also, all existing users of blur have calibrated for linear. */
1838 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839 }
1840 if (ra) {
1841 raBuffer.writeFlattenable(ra);
1842 descSize += raBuffer.size();
1843 entryCount += 1;
1844 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1845 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001846
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001847#ifdef SK_BUILD_FOR_ANDROID
1848 SkOrderedWriteBuffer androidBuffer(128);
1849 fPaintOptionsAndroid.flatten(androidBuffer);
1850 descSize += androidBuffer.size();
1851 entryCount += 1;
1852#endif
1853
reed@google.com10d2d4d2012-03-01 22:32:51 +00001854 ///////////////////////////////////////////////////////////////////////////
1855 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001856 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 descSize += SkDescriptor::ComputeOverhead(entryCount);
1859
1860 SkAutoDescriptor ad(descSize);
1861 SkDescriptor* desc = ad.getDesc();
1862
1863 desc->init();
1864 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1865
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001866#ifdef SK_BUILD_FOR_ANDROID
1867 add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1868#endif
1869
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 if (pe) {
1871 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1872 }
1873 if (mf) {
1874 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1875 }
1876 if (ra) {
1877 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1878 }
1879
1880 SkASSERT(descSize == desc->getLength());
1881 desc->computeChecksum();
1882
reed@google.com17fb3872011-05-04 14:31:07 +00001883#ifdef TEST_DESC
1884 {
1885 // Check that we completely write the bytes in desc (our key), and that
1886 // there are no uninitialized bytes. If there were, then we would get
1887 // false-misses (or worse, false-hits) in our fontcache.
1888 //
1889 // We do this buy filling 2 others, one with 0s and the other with 1s
1890 // and create those, and then check that all 3 are identical.
1891 SkAutoDescriptor ad1(descSize);
1892 SkAutoDescriptor ad2(descSize);
1893 SkDescriptor* desc1 = ad1.getDesc();
1894 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001895
reed@google.com17fb3872011-05-04 14:31:07 +00001896 memset(desc1, 0x00, descSize);
1897 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001898
reed@google.com17fb3872011-05-04 14:31:07 +00001899 desc1->init();
1900 desc2->init();
1901 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1902 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001903
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001904#ifdef SK_BUILD_FOR_ANDROID
1905 add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1906 add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1907#endif
1908
reed@google.com17fb3872011-05-04 14:31:07 +00001909 if (pe) {
1910 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1911 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1912 }
1913 if (mf) {
1914 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1915 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1916 }
1917 if (ra) {
1918 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1919 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1920 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001921
reed@google.com17fb3872011-05-04 14:31:07 +00001922 SkASSERT(descSize == desc1->getLength());
1923 SkASSERT(descSize == desc2->getLength());
1924 desc1->computeChecksum();
1925 desc2->computeChecksum();
1926 SkASSERT(!memcmp(desc, desc1, descSize));
1927 SkASSERT(!memcmp(desc, desc2, descSize));
1928 }
1929#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001930
reed@google.com90808e82013-03-19 14:44:54 +00001931 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932}
1933
bungeman@google.com532470f2013-01-22 19:25:14 +00001934SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
1935 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 SkGlyphCache* cache;
bungeman@google.com532470f2013-01-22 19:25:14 +00001937 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 return cache;
1939}
1940
bungeman@google.com97efada2012-07-30 20:40:50 +00001941/**
1942 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1943 */
1944//static
1945SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1946 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001947 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1948 rec.getPaintGamma(),
1949 rec.getDeviceGamma());
1950 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001951}
1952
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953///////////////////////////////////////////////////////////////////////////////
1954
1955#include "SkStream.h"
1956
reed@android.comaefd2bc2009-03-30 21:02:14 +00001957static uintptr_t asint(const void* p) {
1958 return reinterpret_cast<uintptr_t>(p);
1959}
1960
1961union Scalar32 {
1962 SkScalar fScalar;
1963 uint32_t f32;
1964};
1965
1966static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1967 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1968 Scalar32 tmp;
1969 tmp.fScalar = value;
1970 *ptr = tmp.f32;
1971 return ptr + 1;
1972}
1973
1974static SkScalar read_scalar(const uint32_t*& ptr) {
1975 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1976 Scalar32 tmp;
1977 tmp.f32 = *ptr++;
1978 return tmp.fScalar;
1979}
1980
1981static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1982 SkASSERT(a == (uint8_t)a);
1983 SkASSERT(b == (uint8_t)b);
1984 SkASSERT(c == (uint8_t)c);
1985 SkASSERT(d == (uint8_t)d);
1986 return (a << 24) | (b << 16) | (c << 8) | d;
1987}
1988
1989enum FlatFlags {
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00001990 kHasTypeface_FlatFlag = 0x01,
1991 kHasEffects_FlatFlag = 0x02,
1992 kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001993};
1994
1995// The size of a flat paint's POD fields
rmistry@google.comd6bab022013-12-02 13:50:38 +00001996static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
reed@google.com1f1543f2012-09-12 21:08:33 +00001997 1 * sizeof(SkColor) +
1998 1 * sizeof(uint16_t) +
1999 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002000
2001/* To save space/time, we analyze the paint, and write a truncated version of
2002 it if there are not tricky elements like shaders, etc.
2003 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002005 uint8_t flatFlags = 0;
2006 if (this->getTypeface()) {
2007 flatFlags |= kHasTypeface_FlatFlag;
2008 }
2009 if (asint(this->getPathEffect()) |
2010 asint(this->getShader()) |
2011 asint(this->getXfermode()) |
2012 asint(this->getMaskFilter()) |
2013 asint(this->getColorFilter()) |
2014 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002015 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002016 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002017 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002018 flatFlags |= kHasEffects_FlatFlag;
2019 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002020#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002021 if (this->getPaintOptionsAndroid() != SkPaintOptionsAndroid()) {
2022 flatFlags |= kHasNonDefaultPaintOptionsAndroid_FlatFlag;
2023 }
2024#endif
reed@android.comaefd2bc2009-03-30 21:02:14 +00002025
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002026 if (buffer.isOrderedBinaryBuffer()) {
2027 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2028 uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
2029
2030 ptr = write_scalar(ptr, this->getTextSize());
2031 ptr = write_scalar(ptr, this->getTextScaleX());
2032 ptr = write_scalar(ptr, this->getTextSkewX());
2033 ptr = write_scalar(ptr, this->getStrokeWidth());
2034 ptr = write_scalar(ptr, this->getStrokeMiter());
2035 *ptr++ = this->getColor();
2036 // previously flags:16, textAlign:8, flatFlags:8
2037 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2038 *ptr++ = (this->getFlags() << 16) |
2039 // hinting added later. 0 in this nibble means use the default.
2040 ((this->getHinting()+1) << 12) |
2041 (this->getTextAlign() << 8) |
2042 flatFlags;
2043 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2044 this->getStyle(), this->getTextEncoding());
2045 } else {
2046 buffer.writeScalar(fTextSize);
2047 buffer.writeScalar(fTextScaleX);
2048 buffer.writeScalar(fTextSkewX);
2049 buffer.writeScalar(fWidth);
2050 buffer.writeScalar(fMiterLimit);
2051 buffer.writeColor(fColor);
2052 buffer.writeUInt(fFlags);
2053 buffer.writeUInt(fHinting);
2054 buffer.writeUInt(fTextAlign);
2055 buffer.writeUInt(flatFlags);
2056
2057 buffer.writeUInt(fCapType);
2058 buffer.writeUInt(fJoinType);
2059 buffer.writeUInt(fStyle);
2060 buffer.writeUInt(fTextEncoding);
2061 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002062
2063 // now we're done with ptr and the (pre)reserved space. If we need to write
2064 // additional fields, use the buffer directly
2065 if (flatFlags & kHasTypeface_FlatFlag) {
2066 buffer.writeTypeface(this->getTypeface());
2067 }
2068 if (flatFlags & kHasEffects_FlatFlag) {
2069 buffer.writeFlattenable(this->getPathEffect());
2070 buffer.writeFlattenable(this->getShader());
2071 buffer.writeFlattenable(this->getXfermode());
2072 buffer.writeFlattenable(this->getMaskFilter());
2073 buffer.writeFlattenable(this->getColorFilter());
2074 buffer.writeFlattenable(this->getRasterizer());
2075 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002076 buffer.writeFlattenable(this->getImageFilter());
reed@google.com0cd2ac62013-10-14 20:02:44 +00002077
2078 if (fAnnotation) {
2079 buffer.writeBool(true);
2080 fAnnotation->writeToBuffer(buffer);
2081 } else {
2082 buffer.writeBool(false);
2083 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002084 }
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002085#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002086 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2087 this->getPaintOptionsAndroid().flatten(buffer);
2088 }
2089#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090}
2091
2092void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002093 uint8_t flatFlags = 0;
2094 if (buffer.isOrderedBinaryBuffer()) {
2095 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2096 const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
2097 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002098
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002099 // the order we read must match the order we wrote in flatten()
2100 this->setTextSize(read_scalar(pod));
2101 this->setTextScaleX(read_scalar(pod));
2102 this->setTextSkewX(read_scalar(pod));
2103 this->setStrokeWidth(read_scalar(pod));
2104 this->setStrokeMiter(read_scalar(pod));
2105 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002106
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002107 // previously flags:16, textAlign:8, flatFlags:8
2108 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2109 uint32_t tmp = *pod++;
2110 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00002111
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002112 // hinting added later. 0 in this nibble means use the default.
2113 uint32_t hinting = (tmp >> 12) & 0xF;
2114 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
bungeman@google.com24babf42011-11-07 16:33:40 +00002115
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002116 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002117
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002118 flatFlags = tmp & 0xFF;
2119
2120 tmp = *pod++;
2121 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2122 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2123 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2124 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
2125 } else {
2126 this->setTextSize(buffer.readScalar());
2127 this->setTextScaleX(buffer.readScalar());
2128 this->setTextSkewX(buffer.readScalar());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002129 // Skip the hinting scalar factor, which is not supported.
2130 buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002131 this->setStrokeWidth(buffer.readScalar());
2132 this->setStrokeMiter(buffer.readScalar());
2133 this->setColor(buffer.readColor());
2134 this->setFlags(buffer.readUInt());
2135 this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
2136 this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
2137 flatFlags = buffer.readUInt();
2138
2139 this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
2140 this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
2141 this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
2142 this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
2143 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002144
2145 if (flatFlags & kHasTypeface_FlatFlag) {
2146 this->setTypeface(buffer.readTypeface());
2147 } else {
2148 this->setTypeface(NULL);
2149 }
2150
2151 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com35348222013-10-16 13:05:06 +00002152 SkSafeUnref(this->setPathEffect(buffer.readPathEffect()));
2153 SkSafeUnref(this->setShader(buffer.readShader()));
2154 SkSafeUnref(this->setXfermode(buffer.readXfermode()));
2155 SkSafeUnref(this->setMaskFilter(buffer.readMaskFilter()));
2156 SkSafeUnref(this->setColorFilter(buffer.readColorFilter()));
2157 SkSafeUnref(this->setRasterizer(buffer.readRasterizer()));
2158 SkSafeUnref(this->setLooper(buffer.readDrawLooper()));
2159 SkSafeUnref(this->setImageFilter(buffer.readImageFilter()));
skia.committer@gmail.comfbc58a32013-10-15 07:02:27 +00002160
reed@google.com0cd2ac62013-10-14 20:02:44 +00002161 if (buffer.readBool()) {
2162 this->setAnnotation(SkNEW_ARGS(SkAnnotation, (buffer)))->unref();
2163 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002164 } else {
2165 this->setPathEffect(NULL);
2166 this->setShader(NULL);
2167 this->setXfermode(NULL);
2168 this->setMaskFilter(NULL);
2169 this->setColorFilter(NULL);
2170 this->setRasterizer(NULL);
2171 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002172 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002173 }
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002174
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002175#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002176 this->setPaintOptionsAndroid(SkPaintOptionsAndroid());
2177#endif
2178 if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) {
2179 SkPaintOptionsAndroid options;
2180 options.unflatten(buffer);
commit-bot@chromium.org80f3fcd2013-10-08 16:24:07 +00002181#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.org97f81672013-09-26 15:16:12 +00002182 this->setPaintOptionsAndroid(options);
2183#endif
2184 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185}
2186
2187///////////////////////////////////////////////////////////////////////////////
2188
reed@google.com82065d62011-02-07 15:30:46 +00002189SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002190 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 SkRefCnt_SafeAssign(fShader, shader);
2192 return shader;
2193}
2194
reed@google.com82065d62011-02-07 15:30:46 +00002195SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002196 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 SkRefCnt_SafeAssign(fColorFilter, filter);
2198 return filter;
2199}
2200
reed@google.com82065d62011-02-07 15:30:46 +00002201SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002202 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 SkRefCnt_SafeAssign(fXfermode, mode);
2204 return mode;
2205}
2206
reed@android.com0baf1932009-06-24 12:41:42 +00002207SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002208 SkSafeUnref(fXfermode);
2209 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002210 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002211 return fXfermode;
2212}
2213
reed@google.com82065d62011-02-07 15:30:46 +00002214SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002215 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 SkRefCnt_SafeAssign(fPathEffect, effect);
2217 return effect;
2218}
2219
reed@google.com82065d62011-02-07 15:30:46 +00002220SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002221 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 SkRefCnt_SafeAssign(fMaskFilter, filter);
2223 return filter;
2224}
2225
reed@google.com82065d62011-02-07 15:30:46 +00002226///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227
reed@google.com4bbdeac2013-01-24 21:03:11 +00002228bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2229 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002230 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231
reed@google.comfd4be262012-05-25 01:04:12 +00002232 const SkPath* srcPtr = &src;
2233 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002234
reed@google.com4bbdeac2013-01-24 21:03:11 +00002235 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002236 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 }
2238
reed@google.comfd4be262012-05-25 01:04:12 +00002239 if (!rec.applyToPath(dst, *srcPtr)) {
2240 if (srcPtr == &tmpPath) {
2241 // If path's were copy-on-write, this trick would not be needed.
2242 // As it is, we want to save making a deep-copy from tmpPath -> dst
2243 // since we know we're just going to delete tmpPath when we return,
2244 // so the swap saves that copy.
2245 dst->swap(tmpPath);
2246 } else {
2247 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 }
2249 }
reed@google.comfd4be262012-05-25 01:04:12 +00002250 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251}
2252
reed@google.come4f10a72012-05-15 20:47:50 +00002253const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002254 SkRect* storage,
2255 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002257
reed@google.come4f10a72012-05-15 20:47:50 +00002258 const SkRect* src = &origSrc;
2259
reed@google.com9efd9a02012-01-30 15:41:43 +00002260 if (this->getLooper()) {
2261 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002262 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002263 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002265
reed@google.come4f10a72012-05-15 20:47:50 +00002266 SkRect tmpSrc;
2267 if (this->getPathEffect()) {
2268 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2269 src = &tmpSrc;
2270 }
2271
reed@google.coma584aed2012-05-16 14:06:02 +00002272 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002273 // since we're stroked, outset the rect by the radius (and join type)
2274 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2275 if (0 == radius) { // hairline
2276 radius = SK_Scalar1;
2277 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2278 SkScalar scale = this->getStrokeMiter();
2279 if (scale > SK_Scalar1) {
2280 radius = SkScalarMul(radius, scale);
2281 }
2282 }
reed@google.come4f10a72012-05-15 20:47:50 +00002283 storage->set(src->fLeft - radius, src->fTop - radius,
2284 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002285 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002286 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002287 }
2288
reed@google.com9efd9a02012-01-30 15:41:43 +00002289 if (this->getMaskFilter()) {
2290 this->getMaskFilter()->computeFastBounds(*storage, storage);
2291 }
2292
reed@android.comd252db02009-04-01 18:31:44 +00002293 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294}
2295
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002296#ifdef SK_DEVELOPER
2297void SkPaint::toString(SkString* str) const {
2298 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2299
2300 SkTypeface* typeface = this->getTypeface();
2301 if (NULL != typeface) {
2302 SkDynamicMemoryWStream ostream;
2303 typeface->serialize(&ostream);
2304 SkAutoTUnref<SkData> data(ostream.copyToData());
2305
2306 SkMemoryStream stream(data);
2307 SkFontDescriptor descriptor(&stream);
2308
2309 str->append("<dt>Font Family Name:</dt><dd>");
2310 str->append(descriptor.getFamilyName());
2311 str->append("</dd><dt>Font Full Name:</dt><dd>");
2312 str->append(descriptor.getFullName());
2313 str->append("</dd><dt>Font PS Name:</dt><dd>");
2314 str->append(descriptor.getPostscriptName());
2315 str->append("</dd><dt>Font File Name:</dt><dd>");
2316 str->append(descriptor.getFontFileName());
2317 str->append("</dd>");
2318 }
2319
2320 str->append("<dt>TextSize:</dt><dd>");
2321 str->appendScalar(this->getTextSize());
2322 str->append("</dd>");
2323
2324 str->append("<dt>TextScaleX:</dt><dd>");
2325 str->appendScalar(this->getTextScaleX());
2326 str->append("</dd>");
2327
2328 str->append("<dt>TextSkewX:</dt><dd>");
2329 str->appendScalar(this->getTextSkewX());
2330 str->append("</dd>");
2331
2332 SkPathEffect* pathEffect = this->getPathEffect();
2333 if (NULL != pathEffect) {
2334 str->append("<dt>PathEffect:</dt><dd>");
2335 str->append("</dd>");
2336 }
2337
2338 SkShader* shader = this->getShader();
2339 if (NULL != shader) {
2340 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002341 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002342 str->append("</dd>");
2343 }
2344
2345 SkXfermode* xfer = this->getXfermode();
2346 if (NULL != xfer) {
2347 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002348 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002349 str->append("</dd>");
2350 }
2351
2352 SkMaskFilter* maskFilter = this->getMaskFilter();
2353 if (NULL != maskFilter) {
2354 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002355 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002356 str->append("</dd>");
2357 }
2358
2359 SkColorFilter* colorFilter = this->getColorFilter();
2360 if (NULL != colorFilter) {
2361 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002362 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002363 str->append("</dd>");
2364 }
2365
2366 SkRasterizer* rasterizer = this->getRasterizer();
2367 if (NULL != rasterizer) {
2368 str->append("<dt>Rasterizer:</dt><dd>");
2369 str->append("</dd>");
2370 }
2371
2372 SkDrawLooper* looper = this->getLooper();
2373 if (NULL != looper) {
2374 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002375 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002376 str->append("</dd>");
2377 }
2378
2379 SkImageFilter* imageFilter = this->getImageFilter();
2380 if (NULL != imageFilter) {
2381 str->append("<dt>ImageFilter:</dt><dd>");
2382 str->append("</dd>");
2383 }
2384
2385 SkAnnotation* annotation = this->getAnnotation();
2386 if (NULL != annotation) {
2387 str->append("<dt>Annotation:</dt><dd>");
2388 str->append("</dd>");
2389 }
2390
2391 str->append("<dt>Color:</dt><dd>0x");
2392 SkColor color = this->getColor();
2393 str->appendHex(color);
2394 str->append("</dd>");
2395
2396 str->append("<dt>Stroke Width:</dt><dd>");
2397 str->appendScalar(this->getStrokeWidth());
2398 str->append("</dd>");
2399
2400 str->append("<dt>Stroke Miter:</dt><dd>");
2401 str->appendScalar(this->getStrokeMiter());
2402 str->append("</dd>");
2403
2404 str->append("<dt>Flags:</dt><dd>(");
2405 if (this->getFlags()) {
2406 bool needSeparator = false;
2407 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002408 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2409 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2410 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2411 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2412 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2413 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2414 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2415 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2416 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2417 "EmbeddedBitmapText", &needSeparator);
2418 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2419 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2420 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2421 "GenA8FromLCD", &needSeparator);
2422 } else {
2423 str->append("None");
2424 }
2425 str->append(")</dd>");
2426
robertphillips@google.com3e7e9922013-10-16 17:53:18 +00002427 str->append("<dt>FilterLevel:</dt><dd>");
2428 static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
2429 str->append(gFilterLevelStrings[this->getFilterLevel()]);
2430 str->append("</dd>");
2431
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002432 str->append("<dt>TextAlign:</dt><dd>");
2433 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2434 str->append(gTextAlignStrings[this->getTextAlign()]);
2435 str->append("</dd>");
2436
2437 str->append("<dt>CapType:</dt><dd>");
2438 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2439 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2440 str->append("</dd>");
2441
2442 str->append("<dt>JoinType:</dt><dd>");
2443 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2444 str->append(gJoinStrings[this->getStrokeJoin()]);
2445 str->append("</dd>");
2446
2447 str->append("<dt>Style:</dt><dd>");
2448 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2449 str->append(gStyleStrings[this->getStyle()]);
2450 str->append("</dd>");
2451
2452 str->append("<dt>TextEncoding:</dt><dd>");
2453 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2454 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2455 str->append("</dd>");
2456
2457 str->append("<dt>Hinting:</dt><dd>");
2458 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2459 str->append(gHintingStrings[this->getHinting()]);
2460 str->append("</dd>");
2461
2462 str->append("</dd></dl></dl>");
2463}
2464#endif
2465
2466
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002467///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002469static bool has_thick_frame(const SkPaint& paint) {
2470 return paint.getStrokeWidth() > 0 &&
2471 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002472}
2473
2474SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2475 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002476 bool applyStrokeAndPathEffects)
2477 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2479 true);
2480
djsollen@google.com166e6532012-03-20 14:24:38 +00002481 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2483
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002484 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002485 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002486 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002487
djsollen@google.com166e6532012-03-20 14:24:38 +00002488 // can't use our canonical size if we need to apply patheffects
2489 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002490 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2491 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002492 if (has_thick_frame(fPaint)) {
2493 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2494 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002495 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002497 }
reed@google.com72cf4922011-01-04 19:58:20 +00002498
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002499 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002500 fPaint.setStyle(SkPaint::kFill_Style);
2501 fPaint.setPathEffect(NULL);
2502 }
2503
bungeman@google.com532470f2013-01-22 19:25:14 +00002504 fCache = fPaint.detachCache(NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505
2506 SkPaint::Style style = SkPaint::kFill_Style;
2507 SkPathEffect* pe = NULL;
2508
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002509 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510 style = paint.getStyle(); // restore
2511 pe = paint.getPathEffect(); // restore
2512 }
2513 fPaint.setStyle(style);
2514 fPaint.setPathEffect(pe);
2515 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2516
2517 // now compute fXOffset if needed
2518
2519 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002520 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002522 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2523 &count, NULL), fScale);
2524 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002526 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002527 xOffset = -width;
2528 }
2529 fXPos = xOffset;
2530 fPrevAdvance = 0;
2531
2532 fText = text;
2533 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002534
reed@google.com44da42e2011-11-10 20:04:47 +00002535 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002536}
2537
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002538SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539 SkGlyphCache::AttachCache(fCache);
2540}
2541
reed@google.com7b4531f2012-08-07 15:53:00 +00002542bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2543 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002544 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2545
2546 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002547 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002549 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002550 if (path) {
2551 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002552 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002553 } else {
2554 if (path) {
2555 *path = NULL;
2556 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002557 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002558 if (xpos) {
2559 *xpos = fXPos;
2560 }
2561 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002562 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002563 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002565
2566///////////////////////////////////////////////////////////////////////////////
2567
2568bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002569 if (fLooper) {
2570 return false;
2571 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002572 SkXfermode::Mode mode;
2573 if (SkXfermode::AsMode(fXfermode, &mode)) {
2574 switch (mode) {
2575 case SkXfermode::kSrcOver_Mode:
2576 case SkXfermode::kSrcATop_Mode:
2577 case SkXfermode::kDstOut_Mode:
2578 case SkXfermode::kDstOver_Mode:
2579 case SkXfermode::kPlus_Mode:
2580 return 0 == this->getAlpha();
2581 case SkXfermode::kDst_Mode:
2582 return true;
2583 default:
2584 break;
2585 }
2586 }
2587 return false;
2588}