blob: 886794c479702e18a9c7cc3c0de2df6c69f3ae38 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPathEffect.h"
25#include "SkRasterizer.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000026#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkScalerContext.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000028#include "SkShader.h"
29#include "SkStringUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000031#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000032#include "SkTextToPathIter.h"
reed@google.comed43dff2013-06-04 16:56:27 +000033#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkTypeface.h"
35#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037
reed@google.coma3237872011-07-05 19:20:48 +000038// define this to get a printf for out-of-range parameter in setters
39// e.g. setTextSize(-1)
40//#define SK_REPORT_API_RANGE_CHECK
41
djsollen@google.com56c69772011-11-08 19:00:26 +000042#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000043#define GEN_ID_INC fGenerationID++
44#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
45#else
46#define GEN_ID_INC
47#define GEN_ID_INC_EVAL(expression)
48#endif
49
reed@android.coma3122b92009-08-13 20:38:25 +000050SkPaint::SkPaint() {
51 // since we may have padding, we zero everything so that our memcmp() call
52 // in operator== will work correctly.
53 // with this, we can skip 0 and null individual initializations
54 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000055
reed@android.coma3122b92009-08-13 20:38:25 +000056#if 0 // not needed with the bzero call above
57 fTypeface = NULL;
58 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 fPathEffect = NULL;
60 fShader = NULL;
61 fXfermode = NULL;
62 fMaskFilter = NULL;
63 fColorFilter = NULL;
64 fRasterizer = NULL;
65 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000066 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000067 fAnnotation = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000069#endif
70
reed@google.comaefdd062012-02-29 13:03:00 +000071 fTextSize = SkPaintDefaults_TextSize;
reed@android.coma3122b92009-08-13 20:38:25 +000072 fTextScaleX = SK_Scalar1;
reed@google.com1f1543f2012-09-12 21:08:33 +000073#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
74 fHintingScaleFactor = SK_Scalar1;
75#endif
reed@android.coma3122b92009-08-13 20:38:25 +000076 fColor = SK_ColorBLACK;
reed@google.comaefdd062012-02-29 13:03:00 +000077 fMiterLimit = SkPaintDefaults_MiterLimit;
78 fFlags = SkPaintDefaults_Flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 fCapType = kDefault_Cap;
80 fJoinType = kDefault_Join;
81 fTextAlign = kLeft_Align;
82 fStyle = kFill_Style;
83 fTextEncoding = kUTF8_TextEncoding;
reed@google.comaefdd062012-02-29 13:03:00 +000084 fHinting = SkPaintDefaults_Hinting;
reed@google.comb0a34d82012-07-11 19:57:55 +000085 fPrivFlags = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +000086#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +000087 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000088 fGenerationID = 0;
89#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000090}
91
reed@google.com6fb7e2e2011-02-08 22:22:52 +000092SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 memcpy(this, &src, sizeof(src));
94
reed@google.com82065d62011-02-07 15:30:46 +000095 SkSafeRef(fTypeface);
96 SkSafeRef(fPathEffect);
97 SkSafeRef(fShader);
98 SkSafeRef(fXfermode);
99 SkSafeRef(fMaskFilter);
100 SkSafeRef(fColorFilter);
101 SkSafeRef(fRasterizer);
102 SkSafeRef(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000103 SkSafeRef(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000104 SkSafeRef(fAnnotation);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000105
106#ifdef SK_BUILD_FOR_ANDROID
107 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
108#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109}
110
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000111SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000112 SkSafeUnref(fTypeface);
113 SkSafeUnref(fPathEffect);
114 SkSafeUnref(fShader);
115 SkSafeUnref(fXfermode);
116 SkSafeUnref(fMaskFilter);
117 SkSafeUnref(fColorFilter);
118 SkSafeUnref(fRasterizer);
119 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000120 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000121 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122}
123
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000124SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkASSERT(&src);
126
reed@google.com82065d62011-02-07 15:30:46 +0000127 SkSafeRef(src.fTypeface);
128 SkSafeRef(src.fPathEffect);
129 SkSafeRef(src.fShader);
130 SkSafeRef(src.fXfermode);
131 SkSafeRef(src.fMaskFilter);
132 SkSafeRef(src.fColorFilter);
133 SkSafeRef(src.fRasterizer);
134 SkSafeRef(src.fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000135 SkSafeRef(src.fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000136 SkSafeRef(src.fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137
reed@google.com82065d62011-02-07 15:30:46 +0000138 SkSafeUnref(fTypeface);
139 SkSafeUnref(fPathEffect);
140 SkSafeUnref(fShader);
141 SkSafeUnref(fXfermode);
142 SkSafeUnref(fMaskFilter);
143 SkSafeUnref(fColorFilter);
144 SkSafeUnref(fRasterizer);
145 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000146 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000147 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148
djsollen@google.com56c69772011-11-08 19:00:26 +0000149#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000150 fPaintOptionsAndroid.~SkPaintOptionsAndroid();
151
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000152 uint32_t oldGenerationID = fGenerationID;
153#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 memcpy(this, &src, sizeof(src));
djsollen@google.com56c69772011-11-08 19:00:26 +0000155#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000156 fGenerationID = oldGenerationID + 1;
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000157
158 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000159#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160
161 return *this;
162}
163
reed@google.comb530ef52011-07-20 19:55:42 +0000164bool operator==(const SkPaint& a, const SkPaint& b) {
djsollen@google.comb44cd652011-12-01 17:09:21 +0000165#ifdef SK_BUILD_FOR_ANDROID
166 //assumes that fGenerationID is the last field in the struct
167 return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
168#else
reed@google.comb530ef52011-07-20 19:55:42 +0000169 return !memcmp(&a, &b, sizeof(a));
djsollen@google.comb44cd652011-12-01 17:09:21 +0000170#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171}
172
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000173void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 SkPaint init;
175
djsollen@google.com56c69772011-11-08 19:00:26 +0000176#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000177 uint32_t oldGenerationID = fGenerationID;
178#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000180#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000181 fGenerationID = oldGenerationID + 1;
182#endif
183}
184
djsollen@google.com56c69772011-11-08 19:00:26 +0000185#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000186uint32_t SkPaint::getGenerationID() const {
187 return fGenerationID;
188}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000189
190void SkPaint::setGenerationID(uint32_t generationID) {
191 fGenerationID = generationID;
192}
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000193
djsollen@google.com60abb072012-02-15 18:49:15 +0000194unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
bungeman@google.comba3284e2013-01-22 19:49:33 +0000195 SkAutoGlyphCache autoCache(*this, NULL, NULL);
djsollen@google.com60abb072012-02-15 18:49:15 +0000196 SkGlyphCache* cache = autoCache.getCache();
197 return cache->getBaseGlyphCount(text);
198}
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000199
200void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) {
201 if (options != fPaintOptionsAndroid) {
202 fPaintOptionsAndroid = options;
203 GEN_ID_INC;
204 }
205}
djsollen@google.com60abb072012-02-15 18:49:15 +0000206#endif
207
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000208void SkPaint::setHinting(Hinting hintingLevel) {
209 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
210 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211}
212
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000213void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000214 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 fFlags = flags;
216}
217
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000218void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
220}
221
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000222void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
224}
225
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000226void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
228}
229
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000230void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000231 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
232}
233
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000234void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000235 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
236}
237
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000238void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000239 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
240}
241
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000242void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
244}
245
reed@google.com830a23e2011-11-10 15:20:49 +0000246void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000247 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
248}
249
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000250void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
252}
253
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000254void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
256}
257
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setFilterBitmap(bool doFilter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setStyle(Style style) {
271 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000272 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000274 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000275#ifdef SK_REPORT_API_RANGE_CHECK
276 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
277#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000281void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000282 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fColor = color;
284}
285
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000287 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
288 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289}
290
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000291void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000292 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293}
294
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000295void SkPaint::setStrokeWidth(SkScalar width) {
296 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000297 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000299 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000300#ifdef SK_REPORT_API_RANGE_CHECK
301 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
302#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304}
305
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000306void SkPaint::setStrokeMiter(SkScalar limit) {
307 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000308 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000310 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000311#ifdef SK_REPORT_API_RANGE_CHECK
312 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
313#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000317void SkPaint::setStrokeCap(Cap ct) {
318 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000319 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000321 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000322#ifdef SK_REPORT_API_RANGE_CHECK
323 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
324#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000325 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326}
327
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000328void SkPaint::setStrokeJoin(Join jt) {
329 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000330 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000332 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000333#ifdef SK_REPORT_API_RANGE_CHECK
334 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
335#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337}
338
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000339///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341void SkPaint::setTextAlign(Align align) {
342 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000343 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000345 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000346#ifdef SK_REPORT_API_RANGE_CHECK
347 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
348#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350}
351
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000352void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000353 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000354 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000356 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000357#ifdef SK_REPORT_API_RANGE_CHECK
358 SkDebugf("SkPaint::setTextSize() called with negative value\n");
359#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361}
362
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000363void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000364 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 fTextScaleX = scaleX;
366}
367
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000368void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000369 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 fTextSkewX = skewX;
371}
372
reed@google.com1f1543f2012-09-12 21:08:33 +0000373#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
374void SkPaint::setHintingScaleFactor(SkScalar hintingScaleFactor) {
375 GEN_ID_INC_EVAL(hintingScaleFactor != fHintingScaleFactor);
376 fHintingScaleFactor = hintingScaleFactor;
377}
378#endif
379
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000380void SkPaint::setTextEncoding(TextEncoding encoding) {
381 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000382 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000384 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000385#ifdef SK_REPORT_API_RANGE_CHECK
386 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
387#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000388 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389}
390
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000391///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000393SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000395 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 return font;
397}
398
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000399SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000401 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 return r;
403}
404
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000405SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000407 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 return looper;
409}
410
reed@google.com15356a62011-11-03 19:29:08 +0000411SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
412 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
413 GEN_ID_INC;
414 return imageFilter;
415}
416
reed@google.comb0a34d82012-07-11 19:57:55 +0000417SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
418 SkRefCnt_SafeAssign(fAnnotation, annotation);
419 GEN_ID_INC;
420
421 bool isNoDraw = annotation && annotation->isNoDraw();
422 fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag);
423
424 return annotation;
425}
426
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427///////////////////////////////////////////////////////////////////////////////
428
reed@google.comed43dff2013-06-04 16:56:27 +0000429static SkScalar mag2(SkScalar x, SkScalar y) {
430 return x * x + y * y;
431}
432
433static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
434 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
435 ||
436 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
437}
438
439bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) {
440 SkASSERT(!ctm.hasPerspective());
441 SkASSERT(!textM.hasPerspective());
442
443 SkMatrix matrix;
444 matrix.setConcat(ctm, textM);
445 return tooBig(matrix, MaxCacheSize2());
446}
447
448bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const {
449 SkMatrix textM;
450 return TooBigToUseCache(ctm, *this->setTextMatrix(&textM));
451}
452
453bool SkPaint::tooBigToUseCache() const {
454 SkMatrix textM;
455 return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2());
456}
457
458///////////////////////////////////////////////////////////////////////////////
459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460#include "SkGlyphCache.h"
461#include "SkUtils.h"
462
reed@google.com90808e82013-03-19 14:44:54 +0000463static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
464 void* context) {
465 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000466}
467
djsollen@google.com56c69772011-11-08 19:00:26 +0000468#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000469const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text,
470 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000471 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000472 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000473
474 const SkGlyph& glyph = cache->getUnicharMetrics(text);
475
476 SkGlyphCache::AttachCache(cache);
477 return glyph;
478}
479
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000480const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId,
481 const SkMatrix* deviceMatrix) {
djsollen@google.com60abb072012-02-15 18:49:15 +0000482 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000483 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.com60abb072012-02-15 18:49:15 +0000484
485 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
486
487 SkGlyphCache::AttachCache(cache);
488 return glyph;
489}
490
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000491const void* SkPaint::findImage(const SkGlyph& glyph,
492 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000493 // See ::detachCache()
494 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000495 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000496
497 const void* image = cache->findImage(glyph);
498
499 SkGlyphCache::AttachCache(cache);
500 return image;
501}
502#endif
503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
505 uint16_t glyphs[]) const {
506 if (byteLength == 0) {
507 return 0;
508 }
reed@google.com72cf4922011-01-04 19:58:20 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkASSERT(textData != NULL);
511
512 if (NULL == glyphs) {
513 switch (this->getTextEncoding()) {
514 case kUTF8_TextEncoding:
515 return SkUTF8_CountUnichars((const char*)textData, byteLength);
516 case kUTF16_TextEncoding:
517 return SkUTF16_CountUnichars((const uint16_t*)textData,
518 byteLength >> 1);
reed@google.com68bc6f72012-03-14 19:41:55 +0000519 case kUTF32_TextEncoding:
520 return byteLength >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 case kGlyphID_TextEncoding:
522 return byteLength >> 1;
523 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000524 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 }
526 return 0;
527 }
reed@google.com72cf4922011-01-04 19:58:20 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000530
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 // handle this encoding before the setup for the glyphcache
532 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
533 // we want to ignore the low bit of byteLength
534 memcpy(glyphs, textData, byteLength >> 1 << 1);
535 return byteLength >> 1;
536 }
reed@google.com72cf4922011-01-04 19:58:20 +0000537
bungeman@google.com532470f2013-01-22 19:25:14 +0000538 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 SkGlyphCache* cache = autoCache.getCache();
540
541 const char* text = (const char*)textData;
542 const char* stop = text + byteLength;
543 uint16_t* gptr = glyphs;
544
545 switch (this->getTextEncoding()) {
546 case SkPaint::kUTF8_TextEncoding:
547 while (text < stop) {
548 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
549 }
550 break;
551 case SkPaint::kUTF16_TextEncoding: {
552 const uint16_t* text16 = (const uint16_t*)text;
553 const uint16_t* stop16 = (const uint16_t*)stop;
554 while (text16 < stop16) {
555 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
556 }
557 break;
558 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000559 case kUTF32_TextEncoding: {
560 const int32_t* text32 = (const int32_t*)text;
561 const int32_t* stop32 = (const int32_t*)stop;
562 while (text32 < stop32) {
563 *gptr++ = cache->unicharToGlyph(*text32++);
564 }
565 break;
566 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000568 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569 }
570 return gptr - glyphs;
571}
572
reed@android.coma5dcaf62010-02-05 17:12:32 +0000573bool SkPaint::containsText(const void* textData, size_t byteLength) const {
574 if (0 == byteLength) {
575 return true;
576 }
reed@google.com72cf4922011-01-04 19:58:20 +0000577
reed@android.coma5dcaf62010-02-05 17:12:32 +0000578 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000579
reed@android.coma5dcaf62010-02-05 17:12:32 +0000580 // handle this encoding before the setup for the glyphcache
581 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
582 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
583 size_t count = byteLength >> 1;
584 for (size_t i = 0; i < count; i++) {
585 if (0 == glyphID[i]) {
586 return false;
587 }
588 }
589 return true;
590 }
reed@google.com72cf4922011-01-04 19:58:20 +0000591
bungeman@google.com532470f2013-01-22 19:25:14 +0000592 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000593 SkGlyphCache* cache = autoCache.getCache();
594
595 switch (this->getTextEncoding()) {
596 case SkPaint::kUTF8_TextEncoding: {
597 const char* text = static_cast<const char*>(textData);
598 const char* stop = text + byteLength;
599 while (text < stop) {
600 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
601 return false;
602 }
603 }
604 break;
605 }
606 case SkPaint::kUTF16_TextEncoding: {
607 const uint16_t* text = static_cast<const uint16_t*>(textData);
608 const uint16_t* stop = text + (byteLength >> 1);
609 while (text < stop) {
610 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
611 return false;
612 }
613 }
614 break;
615 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000616 case SkPaint::kUTF32_TextEncoding: {
617 const int32_t* text = static_cast<const int32_t*>(textData);
618 const int32_t* stop = text + (byteLength >> 2);
619 while (text < stop) {
620 if (0 == cache->unicharToGlyph(*text++)) {
621 return false;
622 }
623 }
624 break;
625 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000626 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000627 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000628 return false;
629 }
630 return true;
631}
632
reed@android.com9d3a9852010-01-08 14:07:42 +0000633void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
634 SkUnichar textData[]) const {
635 if (count <= 0) {
636 return;
637 }
638
639 SkASSERT(glyphs != NULL);
640 SkASSERT(textData != NULL);
641
bungeman@google.com532470f2013-01-22 19:25:14 +0000642 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000643 SkGlyphCache* cache = autoCache.getCache();
644
645 for (int index = 0; index < count; index++) {
646 textData[index] = cache->glyphToUnichar(glyphs[index]);
647 }
648}
649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650///////////////////////////////////////////////////////////////////////////////
651
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000652static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
653 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 SkASSERT(cache != NULL);
655 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
658}
659
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000660static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
661 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 SkASSERT(cache != NULL);
663 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000664
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
666}
667
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000668static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
669 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 SkASSERT(cache != NULL);
671 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000672
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
674}
675
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000676static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
677 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 SkASSERT(cache != NULL);
679 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000680
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
682}
683
reed@google.com68bc6f72012-03-14 19:41:55 +0000684static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
685 const char** text) {
686 SkASSERT(cache != NULL);
687 SkASSERT(text != NULL);
688
689 const int32_t* ptr = *(const int32_t**)text;
690 SkUnichar uni = *ptr++;
691 *text = (const char*)ptr;
692 return cache->getUnicharMetrics(uni);
693}
694
695static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
696 const char** text) {
697 SkASSERT(cache != NULL);
698 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000699
reed@google.com68bc6f72012-03-14 19:41:55 +0000700 const int32_t* ptr = *(const int32_t**)text;
701 SkUnichar uni = *--ptr;
702 *text = (const char*)ptr;
703 return cache->getUnicharMetrics(uni);
704}
705
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000706static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
707 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 SkASSERT(cache != NULL);
709 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000710
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 const uint16_t* ptr = *(const uint16_t**)text;
712 unsigned glyphID = *ptr;
713 ptr += 1;
714 *text = (const char*)ptr;
715 return cache->getGlyphIDMetrics(glyphID);
716}
717
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000718static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
719 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 SkASSERT(cache != NULL);
721 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 const uint16_t* ptr = *(const uint16_t**)text;
724 ptr -= 1;
725 unsigned glyphID = *ptr;
726 *text = (const char*)ptr;
727 return cache->getGlyphIDMetrics(glyphID);
728}
729
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000730static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
731 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 SkASSERT(cache != NULL);
733 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000734
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
736}
737
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000738static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
739 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 SkASSERT(cache != NULL);
741 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000742
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
744}
745
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000746static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
747 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 SkASSERT(cache != NULL);
749 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000750
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
752}
753
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000754static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
755 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 SkASSERT(cache != NULL);
757 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000758
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
760}
761
reed@google.com68bc6f72012-03-14 19:41:55 +0000762static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
763 const char** text) {
764 SkASSERT(cache != NULL);
765 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000766
reed@google.com68bc6f72012-03-14 19:41:55 +0000767 const int32_t* ptr = *(const int32_t**)text;
768 SkUnichar uni = *ptr++;
769 *text = (const char*)ptr;
770 return cache->getUnicharAdvance(uni);
771}
772
773static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
774 const char** text) {
775 SkASSERT(cache != NULL);
776 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000777
reed@google.com68bc6f72012-03-14 19:41:55 +0000778 const int32_t* ptr = *(const int32_t**)text;
779 SkUnichar uni = *--ptr;
780 *text = (const char*)ptr;
781 return cache->getUnicharAdvance(uni);
782}
783
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000784static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
785 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 SkASSERT(cache != NULL);
787 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 const uint16_t* ptr = *(const uint16_t**)text;
790 unsigned glyphID = *ptr;
791 ptr += 1;
792 *text = (const char*)ptr;
793 return cache->getGlyphIDAdvance(glyphID);
794}
795
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000796static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
797 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkASSERT(cache != NULL);
799 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 const uint16_t* ptr = *(const uint16_t**)text;
802 ptr -= 1;
803 unsigned glyphID = *ptr;
804 *text = (const char*)ptr;
805 return cache->getGlyphIDAdvance(glyphID);
806}
807
808SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000809 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
811 sk_getMetrics_utf8_next,
812 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000813 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 sk_getMetrics_utf8_prev,
817 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000818 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000820
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 sk_getAdvance_utf8_next,
822 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000823 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 sk_getAdvance_utf8_prev,
827 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000828 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 sk_getAdvance_glyph_prev
830 };
reed@google.com72cf4922011-01-04 19:58:20 +0000831
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 unsigned index = this->getTextEncoding();
833
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000834 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000835 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000836 }
837 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000838 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000839 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840
841 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
842 return gMeasureCacheProcs[index];
843}
844
845///////////////////////////////////////////////////////////////////////////////
846
847static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000848 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkASSERT(cache != NULL);
850 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000851
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
853}
854
855static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000856 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 SkASSERT(cache != NULL);
858 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000859
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
861}
862
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000863static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
864 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 SkASSERT(cache != NULL);
866 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000867
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
869}
870
871static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000872 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 SkASSERT(cache != NULL);
874 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000875
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
877 x, y);
878}
879
reed@google.com68bc6f72012-03-14 19:41:55 +0000880static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
881 const char** text, SkFixed, SkFixed) {
882 SkASSERT(cache != NULL);
883 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000884
reed@google.com68bc6f72012-03-14 19:41:55 +0000885 const int32_t* ptr = *(const int32_t**)text;
886 SkUnichar uni = *ptr++;
887 *text = (const char*)ptr;
888 return cache->getUnicharMetrics(uni);
889}
890
891static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
892 const char** text, SkFixed x, SkFixed y) {
893 SkASSERT(cache != NULL);
894 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000895
reed@google.com68bc6f72012-03-14 19:41:55 +0000896 const int32_t* ptr = *(const int32_t**)text;
897 SkUnichar uni = *--ptr;
898 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000899 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000900}
901
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000902static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
903 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 SkASSERT(cache != NULL);
905 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000906
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 const uint16_t* ptr = *(const uint16_t**)text;
908 unsigned glyphID = *ptr;
909 ptr += 1;
910 *text = (const char*)ptr;
911 return cache->getGlyphIDMetrics(glyphID);
912}
913
914static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000915 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 SkASSERT(cache != NULL);
917 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000918
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 const uint16_t* ptr = *(const uint16_t**)text;
920 unsigned glyphID = *ptr;
921 ptr += 1;
922 *text = (const char*)ptr;
923 return cache->getGlyphIDMetrics(glyphID, x, y);
924}
925
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000926SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 static const SkDrawCacheProc gDrawCacheProcs[] = {
928 sk_getMetrics_utf8_00,
929 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000930 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000932
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 sk_getMetrics_utf8_xy,
934 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000935 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 sk_getMetrics_glyph_xy
937 };
reed@google.com72cf4922011-01-04 19:58:20 +0000938
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000940 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000941 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000942 }
reed@google.com72cf4922011-01-04 19:58:20 +0000943
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
945 return gDrawCacheProcs[index];
946}
947
948///////////////////////////////////////////////////////////////////////////////
949
reed@google.comed43dff2013-06-04 16:56:27 +0000950#define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \
951SkPaint::kDevKernText_Flag | \
952SkPaint::kLinearText_Flag | \
953SkPaint::kLCDRenderText_Flag | \
954SkPaint::kEmbeddedBitmapText_Flag | \
955SkPaint::kAutoHinting_Flag | \
956SkPaint::kGenA8FromLCD_Flag )
957
958SkScalar SkPaint::setupForAsPaths() {
959 uint32_t flags = this->getFlags();
960 // clear the flags we don't care about
961 flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE;
962 // set the flags we do care about
963 flags |= SkPaint::kSubpixelText_Flag;
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000964
reed@google.comed43dff2013-06-04 16:56:27 +0000965 this->setFlags(flags);
966 this->setHinting(SkPaint::kNo_Hinting);
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000967
reed@google.comed43dff2013-06-04 16:56:27 +0000968 SkScalar textSize = fTextSize;
969 this->setTextSize(kCanonicalTextSizeForPaths);
970 return textSize / kCanonicalTextSizeForPaths;
971}
972
973class SkCanonicalizePaint {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974public:
reed@google.comed43dff2013-06-04 16:56:27 +0000975 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
976 if (paint.isLinearText() || paint.tooBigToUseCache()) {
977 SkPaint* p = fLazy.set(paint);
978 fScale = p->setupForAsPaths();
979 fPaint = p;
980 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000982
reed@google.comed43dff2013-06-04 16:56:27 +0000983 const SkPaint& getPaint() const { return *fPaint; }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000984
reed@google.comed43dff2013-06-04 16:56:27 +0000985 /**
986 * Returns 0 if the paint was unmodified, or the scale factor need to
987 * the original textSize
988 */
989 SkScalar getScale() const { return fScale; }
reed@google.com72cf4922011-01-04 19:58:20 +0000990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991private:
reed@google.comed43dff2013-06-04 16:56:27 +0000992 const SkPaint* fPaint;
993 SkScalar fScale;
994 SkTLazy<SkPaint> fLazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995};
996
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000997static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 bounds->set(SkIntToScalar(g.fLeft),
999 SkIntToScalar(g.fTop),
1000 SkIntToScalar(g.fLeft + g.fWidth),
1001 SkIntToScalar(g.fTop + g.fHeight));
1002}
1003
reed@android.come88f5512010-03-19 14:42:28 +00001004// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
1005// we don't overflow along the way
1006typedef int64_t Sk48Dot16;
1007
1008#ifdef SK_SCALAR_IS_FLOAT
1009 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +00001010 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +00001011 }
1012#else
1013 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
1014 // just return the low 32bits
1015 return static_cast<SkFixed>(x);
1016 }
1017#endif
1018
reed@google.com44da42e2011-11-10 20:04:47 +00001019static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +00001020 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 bounds->join(SkIntToScalar(g.fLeft) + sx,
1022 SkIntToScalar(g.fTop),
1023 SkIntToScalar(g.fLeft + g.fWidth) + sx,
1024 SkIntToScalar(g.fTop + g.fHeight));
1025}
1026
reed@google.com44da42e2011-11-10 20:04:47 +00001027static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
1028 SkScalar sy = Sk48Dot16ToScalar(dy);
1029 bounds->join(SkIntToScalar(g.fLeft),
1030 SkIntToScalar(g.fTop) + sy,
1031 SkIntToScalar(g.fLeft + g.fWidth),
1032 SkIntToScalar(g.fTop + g.fHeight) + sy);
1033}
1034
1035typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
1036
1037// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
1038static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
1039 SkASSERT(0 == xyIndex || 1 == xyIndex);
1040 return (&glyph.fAdvanceX)[xyIndex];
1041}
1042
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043SkScalar SkPaint::measure_text(SkGlyphCache* cache,
1044 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001045 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001047 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001049 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001051 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 return 0;
1053 }
1054
1055 SkMeasureCacheProc glyphCacheProc;
1056 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1057 NULL != bounds);
1058
reed@google.com44da42e2011-11-10 20:04:47 +00001059 int xyIndex;
1060 JoinBoundsProc joinBoundsProc;
1061 if (this->isVerticalText()) {
1062 xyIndex = 1;
1063 joinBoundsProc = join_bounds_y;
1064 } else {
1065 xyIndex = 0;
1066 joinBoundsProc = join_bounds_x;
1067 }
1068
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 int n = 1;
1070 const char* stop = (const char*)text + byteLength;
1071 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001072 // our accumulated fixed-point advances might overflow 16.16, so we use
1073 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1074 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001075 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076
1077 SkAutoKern autokern;
1078
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001079 if (NULL == bounds) {
1080 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 int rsb;
1082 for (; text < stop; n++) {
1083 rsb = g->fRsbDelta;
1084 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001085 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001087 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001089 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 }
1091 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001092 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001094 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 int rsb;
1096 for (; text < stop; n++) {
1097 rsb = g->fRsbDelta;
1098 g = &glyphCacheProc(cache, &text);
1099 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001100 joinBoundsProc(*g, bounds, x);
1101 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001103 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 for (; text < stop; n++) {
1105 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001106 joinBoundsProc(*g, bounds, x);
1107 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 }
1109 }
1110 }
1111 SkASSERT(text == stop);
1112
1113 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001114 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115}
1116
1117SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001118 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 const char* text = (const char*)textData;
1120 SkASSERT(text != NULL || length == 0);
1121
reed@google.comed43dff2013-06-04 16:56:27 +00001122 SkCanonicalizePaint canon(*this);
1123 const SkPaint& paint = canon.getPaint();
1124 SkScalar scale = canon.getScale();
reed@google.com72cf4922011-01-04 19:58:20 +00001125
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001126 SkMatrix zoomMatrix, *zoomPtr = NULL;
1127 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 zoomMatrix.setScale(zoom, zoom);
1129 zoomPtr = &zoomMatrix;
1130 }
1131
reed@google.comed43dff2013-06-04 16:56:27 +00001132 SkAutoGlyphCache autoCache(paint, NULL, zoomPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 SkGlyphCache* cache = autoCache.getCache();
1134
1135 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001136
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001137 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 int tempCount;
1139
reed@google.comed43dff2013-06-04 16:56:27 +00001140 width = paint.measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001141 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001143 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1145 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1146 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1147 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1148 }
1149 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001150 } else if (bounds) {
1151 // ensure that even if we don't measure_text we still update the bounds
1152 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 }
1154 return width;
1155}
1156
1157typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1158
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001159static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 return text < stop;
1161}
1162
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001163static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164 return text > stop;
1165}
1166
1167static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001168 const char** text, size_t length,
1169 const char** stop) {
1170 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 *stop = *text + length;
1172 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001173 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 // text should point to the end of the buffer, and stop to the beginning
1175 *stop = *text;
1176 *text += length;
1177 return backward_textBufferPred;
1178 }
1179}
1180
1181size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1182 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001183 TextBufferDirection tbd) const {
1184 if (0 == length || 0 >= maxWidth) {
1185 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001187 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 return 0;
1189 }
1190
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001191 if (0 == fTextSize) {
1192 if (measuredWidth) {
1193 *measuredWidth = 0;
1194 }
1195 return length;
1196 }
1197
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 SkASSERT(textD != NULL);
1199 const char* text = (const char*)textD;
1200
reed@google.comed43dff2013-06-04 16:56:27 +00001201 SkCanonicalizePaint canon(*this);
1202 const SkPaint& paint = canon.getPaint();
1203 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204
reed@google.comed43dff2013-06-04 16:56:27 +00001205 // adjust max in case we changed the textSize in paint
1206 if (scale) {
1207 maxWidth /= scale;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 }
reed@google.com72cf4922011-01-04 19:58:20 +00001209
reed@google.comed43dff2013-06-04 16:56:27 +00001210 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 SkGlyphCache* cache = autoCache.getCache();
1212
reed@google.comed43dff2013-06-04 16:56:27 +00001213 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 const char* stop;
1215 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.comed43dff2013-06-04 16:56:27 +00001216 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001217 // use 64bits for our accumulator, to avoid overflowing 16.16
1218 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1219 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220
1221 SkAutoKern autokern;
1222
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001223 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001225 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 const char* curr = text;
1227 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001228 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001229 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 width -= x;
1231 text = curr;
1232 break;
1233 }
1234 rsb = g.fRsbDelta;
1235 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001236 } else {
1237 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001239 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001240 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 width -= x;
1242 text = curr;
1243 break;
1244 }
1245 }
1246 }
reed@google.com72cf4922011-01-04 19:58:20 +00001247
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001248 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001249 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001250 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001252 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 *measuredWidth = scalarWidth;
1254 }
reed@google.com72cf4922011-01-04 19:58:20 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 // return the number of bytes measured
1257 return (kForward_TextBufferDirection == tbd) ?
1258 text - stop + length : stop - text + length;
1259}
1260
1261///////////////////////////////////////////////////////////////////////////////
1262
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001263static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001264 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 return false; // don't detach the cache
1266}
1267
reed@google.com90808e82013-03-19 14:44:54 +00001268static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1269 void* context) {
1270 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271}
1272
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001273SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@google.comed43dff2013-06-04 16:56:27 +00001274 SkCanonicalizePaint canon(*this);
1275 const SkPaint& paint = canon.getPaint();
1276 SkScalar scale = canon.getScale();
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +00001277
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001278 SkMatrix zoomMatrix, *zoomPtr = NULL;
1279 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 zoomMatrix.setScale(zoom, zoom);
1281 zoomPtr = &zoomMatrix;
1282 }
1283
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001285 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001287 }
reed@google.com72cf4922011-01-04 19:58:20 +00001288
reed@google.comed43dff2013-06-04 16:56:27 +00001289 paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001291 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1293 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1294 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1295 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1296 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001297 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1298 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1299 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1300 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 }
1302 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1303}
1304
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001305///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001307static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 bounds->set(g.fLeft * scale,
1309 g.fTop * scale,
1310 (g.fLeft + g.fWidth) * scale,
1311 (g.fTop + g.fHeight) * scale);
1312}
1313
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001314int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1315 SkScalar widths[], SkRect bounds[]) const {
1316 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319
1320 SkASSERT(NULL != textData);
1321
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001322 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325
reed@google.comed43dff2013-06-04 16:56:27 +00001326 SkCanonicalizePaint canon(*this);
1327 const SkPaint& paint = canon.getPaint();
1328 SkScalar scale = canon.getScale();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329
reed@google.comed43dff2013-06-04 16:56:27 +00001330 SkAutoGlyphCache autoCache(paint, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 SkGlyphCache* cache = autoCache.getCache();
1332 SkMeasureCacheProc glyphCacheProc;
reed@google.comed43dff2013-06-04 16:56:27 +00001333 glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 NULL != bounds);
1335
1336 const char* text = (const char*)textData;
1337 const char* stop = text + byteLength;
1338 int count = 0;
reed@google.comed43dff2013-06-04 16:56:27 +00001339 const int xyIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001341 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 // we adjust the widths returned here through auto-kerning
1343 SkAutoKern autokern;
1344 SkFixed prevWidth = 0;
1345
1346 if (scale) {
1347 while (text < stop) {
1348 const SkGlyph& g = glyphCacheProc(cache, &text);
1349 if (widths) {
1350 SkFixed adjust = autokern.adjust(g);
1351
1352 if (count > 0) {
1353 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1354 *widths++ = SkScalarMul(w, scale);
1355 }
reed@google.com44da42e2011-11-10 20:04:47 +00001356 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 }
1358 if (bounds) {
1359 set_bounds(g, bounds++, scale);
1360 }
1361 ++count;
1362 }
1363 if (count > 0 && widths) {
1364 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1365 }
1366 } else {
1367 while (text < stop) {
1368 const SkGlyph& g = glyphCacheProc(cache, &text);
1369 if (widths) {
1370 SkFixed adjust = autokern.adjust(g);
1371
1372 if (count > 0) {
1373 *widths++ = SkFixedToScalar(prevWidth + adjust);
1374 }
reed@google.com44da42e2011-11-10 20:04:47 +00001375 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376 }
1377 if (bounds) {
1378 set_bounds(g, bounds++);
1379 }
1380 ++count;
1381 }
1382 if (count > 0 && widths) {
1383 *widths = SkFixedToScalar(prevWidth);
1384 }
1385 }
1386 } else { // no devkern
1387 if (scale) {
1388 while (text < stop) {
1389 const SkGlyph& g = glyphCacheProc(cache, &text);
1390 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001391 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 scale);
1393 }
1394 if (bounds) {
1395 set_bounds(g, bounds++, scale);
1396 }
1397 ++count;
1398 }
1399 } else {
1400 while (text < stop) {
1401 const SkGlyph& g = glyphCacheProc(cache, &text);
1402 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001403 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 }
1405 if (bounds) {
1406 set_bounds(g, bounds++);
1407 }
1408 ++count;
1409 }
1410 }
1411 }
1412
1413 SkASSERT(text == stop);
1414 return count;
1415}
1416
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001417///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418
1419#include "SkDraw.h"
1420
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001421void SkPaint::getTextPath(const void* textData, size_t length,
1422 SkScalar x, SkScalar y, SkPath* path) const {
1423 SkASSERT(length == 0 || textData != NULL);
1424
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001426 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001428 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429
djsollen@google.com166e6532012-03-20 14:24:38 +00001430 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkMatrix matrix;
1432 SkScalar prevXPos = 0;
1433
1434 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1435 matrix.postTranslate(x, y);
1436 path->reset();
1437
1438 SkScalar xpos;
1439 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001440 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001442 if (iterPath) {
1443 path->addPath(*iterPath, matrix);
1444 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 prevXPos = xpos;
1446 }
1447}
1448
reed@google.comca0062e2012-07-20 11:20:32 +00001449void SkPaint::getPosTextPath(const void* textData, size_t length,
1450 const SkPoint pos[], SkPath* path) const {
1451 SkASSERT(length == 0 || textData != NULL);
1452
1453 const char* text = (const char*)textData;
1454 if (text == NULL || length == 0 || path == NULL) {
1455 return;
1456 }
1457
1458 SkTextToPathIter iter(text, length, *this, false);
1459 SkMatrix matrix;
1460 SkPoint prevPos;
1461 prevPos.set(0, 0);
1462
1463 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1464 path->reset();
1465
1466 unsigned int i = 0;
1467 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001468 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001469 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001470 if (iterPath) {
1471 path->addPath(*iterPath, matrix);
1472 }
reed@google.comca0062e2012-07-20 11:20:32 +00001473 prevPos = pos[i];
1474 i++;
1475 }
1476}
1477
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478static void add_flattenable(SkDescriptor* desc, uint32_t tag,
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001479 SkOrderedWriteBuffer* buffer) {
1480 buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
reed@google.com2739b272011-09-28 17:26:42 +00001483// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001484static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001487 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001488 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001489 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001490 }
1491
reed@google.com65dd8f82011-03-14 13:31:16 +00001492 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001493 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001494 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001495
1496 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497}
1498
reed@android.com1cdcb512009-08-24 19:11:00 +00001499// if linear-text is on, then we force hinting to be off (since that's sort of
1500// the point of linear-text.
1501static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1502 SkPaint::Hinting h = paint.getHinting();
1503 if (paint.isLinearText()) {
1504 h = SkPaint::kNo_Hinting;
1505 }
1506 return h;
1507}
1508
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001509// return true if the paint is just a single color (i.e. not a shader). If its
1510// a shader, then we can't compute a const luminance for it :(
1511static bool justAColor(const SkPaint& paint, SkColor* color) {
1512 if (paint.getShader()) {
1513 return false;
1514 }
1515 SkColor c = paint.getColor();
1516 if (paint.getColorFilter()) {
1517 c = paint.getColorFilter()->filterColor(c);
1518 }
1519 if (color) {
1520 *color = c;
1521 }
1522 return true;
1523}
1524
reed@google.comce6dbb62012-02-10 22:01:45 +00001525static SkColor computeLuminanceColor(const SkPaint& paint) {
1526 SkColor c;
1527 if (!justAColor(paint, &c)) {
1528 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1529 }
1530 return c;
1531}
reed@google.com813d38b2012-02-13 21:37:57 +00001532
reed@google.comdd43df92012-02-15 14:50:29 +00001533#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1534
reed@google.com4f79b9b2011-09-13 13:23:26 +00001535// Beyond this size, LCD doesn't appreciably improve quality, but it always
1536// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001537#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1538 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1539#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001540
1541static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1542 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1543 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1544 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001545 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001546}
1547
reed@google.com72cf4922011-01-04 19:58:20 +00001548/*
1549 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1550 * that vary only slightly when we create our key into the font cache, since the font scaler
1551 * typically returns the same looking resuts for tiny changes in the matrix.
1552 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001553static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001554#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001555 int n = sk_float_round2int(x * 1024);
1556 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001557#else
1558 // round to the nearest 10 fractional bits
1559 return (x + (1 << 5)) & ~(1024 - 1);
1560#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001561}
1562
reed@android.com36a4c2a2009-07-22 19:52:11 +00001563void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001564 const SkDeviceProperties* deviceProperties,
1565 const SkMatrix* deviceMatrix,
1566 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001567 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001569 SkTypeface* typeface = paint.getTypeface();
1570 if (NULL == typeface) {
1571 typeface = SkTypeface::GetDefaultTypeface();
1572 }
1573 rec->fOrigFontID = typeface->uniqueID();
reed@google.com7d26c592011-06-13 13:01:10 +00001574 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 rec->fTextSize = paint.getTextSize();
1576 rec->fPreScaleX = paint.getTextScaleX();
1577 rec->fPreSkewX = paint.getTextSkewX();
reed@google.com1f1543f2012-09-12 21:08:33 +00001578#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
1579 rec->fHintingScaleFactor = paint.getHintingScaleFactor();
1580#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001582 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001583 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1584 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1585 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1586 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001587 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1589 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1590 }
reed@google.com72cf4922011-01-04 19:58:20 +00001591
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 SkPaint::Style style = paint.getStyle();
1593 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001594
reed@google.comffe49f52011-11-22 19:42:41 +00001595 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001596
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001597 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001598#ifdef SK_USE_FREETYPE_EMBOLDEN
1599 flags |= SkScalerContext::kEmbolden_Flag;
1600#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001601 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1602 kStdFakeBoldInterpKeys,
1603 kStdFakeBoldInterpValues,
1604 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001606
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001607 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 style = SkPaint::kStrokeAndFill_Style;
1609 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001610 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001612 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001613#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 }
1615
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001616 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001618 }
reed@google.com72cf4922011-01-04 19:58:20 +00001619
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001620 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 rec->fFrameWidth = strokeWidth;
1622 rec->fMiterLimit = paint.getStrokeMiter();
1623 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1624
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001625 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001627 }
1628 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 rec->fFrameWidth = 0;
1630 rec->fMiterLimit = 0;
1631 rec->fStrokeJoin = 0;
1632 }
1633
reed@google.com02b53312011-05-18 19:00:53 +00001634 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1635
bungeman@google.com532470f2013-01-22 19:25:14 +00001636 SkDeviceProperties::Geometry geometry = deviceProperties
1637 ? deviceProperties->fGeometry
1638 : SkDeviceProperties::Geometry::MakeDefault();
1639 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
1640 if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001641 // eeek, can't support LCD
1642 rec->fMaskFormat = SkMask::kA8_Format;
1643 } else {
bungeman@google.com532470f2013-01-22 19:25:14 +00001644 if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
reed@google.com02b53312011-05-18 19:00:53 +00001645 flags |= SkScalerContext::kLCD_Vertical_Flag;
1646 }
bungeman@google.com532470f2013-01-22 19:25:14 +00001647 if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
reed@google.com02b53312011-05-18 19:00:53 +00001648 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1649 }
1650 }
1651 }
1652
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001653 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001654 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001655 }
1656 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001657 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001658 }
1659 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001660 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001661 }
reed@google.com830a23e2011-11-10 15:20:49 +00001662 if (paint.isVerticalText()) {
1663 flags |= SkScalerContext::kVertical_Flag;
1664 }
reed@google.com8351aab2012-01-18 17:06:35 +00001665 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1666 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1667 }
reed@google.com02b53312011-05-18 19:00:53 +00001668 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001669
reed@google.comffe49f52011-11-22 19:42:41 +00001670 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001671 rec->setHinting(computeHinting(paint));
1672
bungeman@google.com97efada2012-07-30 20:40:50 +00001673 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001674
1675 if (NULL == deviceProperties) {
1676 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1677 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1678 } else {
1679 rec->setDeviceGamma(deviceProperties->fGamma);
1680
1681 //For now always set the paint gamma equal to the device gamma.
1682 //The math in SkMaskGamma can handle them being different,
1683 //but it requires superluminous masks when
1684 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1685 rec->setPaintGamma(deviceProperties->fGamma);
1686 }
1687
1688#ifdef SK_GAMMA_CONTRAST
1689 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001690#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001691 /**
1692 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1693 * With lower values small text appears washed out (though correctly so).
1694 * With higher values lcd fringing is worse and the smoothing effect of
1695 * partial coverage is diminished.
1696 */
1697 rec->setContrast(SkFloatToScalar(0.5f));
bungeman@google.com97efada2012-07-30 20:40:50 +00001698#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001699
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001700 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001701
reed@android.com36a4c2a2009-07-22 19:52:11 +00001702 /* Allow the fonthost to modify our rec before we use it as a key into the
1703 cache. This way if we're asking for something that they will ignore,
1704 they can modify our rec up front, so we don't create duplicate cache
1705 entries.
1706 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001707 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001708
reed@google.com10d2d4d2012-03-01 22:32:51 +00001709 // be sure to call PostMakeRec(rec) before you actually use it!
1710}
1711
1712/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001713 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1714 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1715 * to hold it until the returned pointer is refed or forgotten.
1716 */
1717SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1718
bungeman@google.comae30f562012-09-11 18:44:55 +00001719static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001720static SkMaskGamma* gMaskGamma = NULL;
1721static SkScalar gContrast = SK_ScalarMin;
1722static SkScalar gPaintGamma = SK_ScalarMin;
1723static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001724/**
1725 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1726 * the returned SkMaskGamma pointer is refed or forgotten.
1727 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001728static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001729 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1730 if (NULL == gLinearMaskGamma) {
1731 gLinearMaskGamma = SkNEW(SkMaskGamma);
1732 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001733 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001734 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001735 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1736 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001737 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001738 gContrast = contrast;
1739 gPaintGamma = paintGamma;
1740 gDeviceGamma = deviceGamma;
1741 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001742 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001743}
1744
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001745/*static*/ void SkPaint::Term() {
1746 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1747
bungeman@google.comae30f562012-09-11 18:44:55 +00001748 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001749 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001750 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001751 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001752 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1753 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1754 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001755}
1756
bungeman@google.com97efada2012-07-30 20:40:50 +00001757/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001758 * We ensure that the rec is self-consistent and efficient (where possible)
1759 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001760void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001761 /**
1762 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001763 * limits the number of unique entries, and the scaler will only look at
1764 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001765 */
reed@google.comdd43df92012-02-15 14:50:29 +00001766 switch (rec->fMaskFormat) {
1767 case SkMask::kLCD16_Format:
1768 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001769 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001770 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001771 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001772 break;
1773 }
1774 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001775 // filter down the luminance to a single component, since A8 can't
1776 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001777
bungeman@google.com97efada2012-07-30 20:40:50 +00001778 SkColor color = rec->getLuminanceColor();
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001779 U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001780 //If we are asked to look like LCD, look like LCD.
1781 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1782 // HACK: Prevents green from being pre-blended as white.
1783 lum -= ((255 - lum) * lum) / 255;
1784 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001785
reed@google.comdd43df92012-02-15 14:50:29 +00001786 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001787 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001788 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001789 break;
1790 }
1791 case SkMask::kBW_Format:
1792 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001793 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001794 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001795 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796}
1797
1798#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1799
reed@google.com17fb3872011-05-04 14:31:07 +00001800#ifdef SK_DEBUG
1801 #define TEST_DESC
1802#endif
1803
reed@google.comffe49f52011-11-22 19:42:41 +00001804/*
1805 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1806 * by gamma correction, so we jam the luminance field to 0 (most common value
1807 * for black text) in hopes that we get a cache hit easier. A better solution
1808 * would be for the fontcache lookup to know to ignore the luminance field
1809 * entirely, but not sure how to do that and keep it fast.
1810 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001811void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1812 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001813 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001814 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 SkScalerContext::Rec rec;
1816
bungeman@google.com532470f2013-01-22 19:25:14 +00001817 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001818 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001819 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001820 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821
1822 size_t descSize = sizeof(rec);
1823 int entryCount = 1;
1824 SkPathEffect* pe = this->getPathEffect();
1825 SkMaskFilter* mf = this->getMaskFilter();
1826 SkRasterizer* ra = this->getRasterizer();
1827
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001828 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1829 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1830 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831
1832 if (pe) {
1833 peBuffer.writeFlattenable(pe);
1834 descSize += peBuffer.size();
1835 entryCount += 1;
1836 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1837 // seems like we could support kLCD as well at this point...
1838 }
1839 if (mf) {
1840 mfBuffer.writeFlattenable(mf);
1841 descSize += mfBuffer.size();
1842 entryCount += 1;
1843 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001844 /* Pre-blend is not currently applied to filtered text.
1845 The primary filter is blur, for which contrast makes no sense,
1846 and for which the destination guess error is more visible.
1847 Also, all existing users of blur have calibrated for linear. */
1848 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 }
1850 if (ra) {
1851 raBuffer.writeFlattenable(ra);
1852 descSize += raBuffer.size();
1853 entryCount += 1;
1854 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1855 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001856
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001857#ifdef SK_BUILD_FOR_ANDROID
1858 SkOrderedWriteBuffer androidBuffer(128);
1859 fPaintOptionsAndroid.flatten(androidBuffer);
1860 descSize += androidBuffer.size();
1861 entryCount += 1;
1862#endif
1863
reed@google.com10d2d4d2012-03-01 22:32:51 +00001864 ///////////////////////////////////////////////////////////////////////////
1865 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001866 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001867
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868 descSize += SkDescriptor::ComputeOverhead(entryCount);
1869
1870 SkAutoDescriptor ad(descSize);
1871 SkDescriptor* desc = ad.getDesc();
1872
1873 desc->init();
1874 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1875
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001876#ifdef SK_BUILD_FOR_ANDROID
1877 add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1878#endif
1879
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 if (pe) {
1881 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1882 }
1883 if (mf) {
1884 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1885 }
1886 if (ra) {
1887 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1888 }
1889
1890 SkASSERT(descSize == desc->getLength());
1891 desc->computeChecksum();
1892
reed@google.com17fb3872011-05-04 14:31:07 +00001893#ifdef TEST_DESC
1894 {
1895 // Check that we completely write the bytes in desc (our key), and that
1896 // there are no uninitialized bytes. If there were, then we would get
1897 // false-misses (or worse, false-hits) in our fontcache.
1898 //
1899 // We do this buy filling 2 others, one with 0s and the other with 1s
1900 // and create those, and then check that all 3 are identical.
1901 SkAutoDescriptor ad1(descSize);
1902 SkAutoDescriptor ad2(descSize);
1903 SkDescriptor* desc1 = ad1.getDesc();
1904 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001905
reed@google.com17fb3872011-05-04 14:31:07 +00001906 memset(desc1, 0x00, descSize);
1907 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001908
reed@google.com17fb3872011-05-04 14:31:07 +00001909 desc1->init();
1910 desc2->init();
1911 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1912 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001913
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001914#ifdef SK_BUILD_FOR_ANDROID
1915 add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1916 add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1917#endif
1918
reed@google.com17fb3872011-05-04 14:31:07 +00001919 if (pe) {
1920 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1921 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1922 }
1923 if (mf) {
1924 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1925 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1926 }
1927 if (ra) {
1928 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1929 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1930 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001931
reed@google.com17fb3872011-05-04 14:31:07 +00001932 SkASSERT(descSize == desc1->getLength());
1933 SkASSERT(descSize == desc2->getLength());
1934 desc1->computeChecksum();
1935 desc2->computeChecksum();
1936 SkASSERT(!memcmp(desc, desc1, descSize));
1937 SkASSERT(!memcmp(desc, desc2, descSize));
1938 }
1939#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001940
reed@google.com90808e82013-03-19 14:44:54 +00001941 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942}
1943
bungeman@google.com532470f2013-01-22 19:25:14 +00001944SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
1945 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946 SkGlyphCache* cache;
bungeman@google.com532470f2013-01-22 19:25:14 +00001947 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 return cache;
1949}
1950
bungeman@google.com97efada2012-07-30 20:40:50 +00001951/**
1952 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1953 */
1954//static
1955SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1956 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001957 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1958 rec.getPaintGamma(),
1959 rec.getDeviceGamma());
1960 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001961}
1962
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963///////////////////////////////////////////////////////////////////////////////
1964
1965#include "SkStream.h"
1966
reed@android.comaefd2bc2009-03-30 21:02:14 +00001967static uintptr_t asint(const void* p) {
1968 return reinterpret_cast<uintptr_t>(p);
1969}
1970
1971union Scalar32 {
1972 SkScalar fScalar;
1973 uint32_t f32;
1974};
1975
1976static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1977 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1978 Scalar32 tmp;
1979 tmp.fScalar = value;
1980 *ptr = tmp.f32;
1981 return ptr + 1;
1982}
1983
1984static SkScalar read_scalar(const uint32_t*& ptr) {
1985 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1986 Scalar32 tmp;
1987 tmp.f32 = *ptr++;
1988 return tmp.fScalar;
1989}
1990
1991static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1992 SkASSERT(a == (uint8_t)a);
1993 SkASSERT(b == (uint8_t)b);
1994 SkASSERT(c == (uint8_t)c);
1995 SkASSERT(d == (uint8_t)d);
1996 return (a << 24) | (b << 16) | (c << 8) | d;
1997}
1998
1999enum FlatFlags {
2000 kHasTypeface_FlatFlag = 0x01,
reed@google.comb0a34d82012-07-11 19:57:55 +00002001 kHasEffects_FlatFlag = 0x02,
reed@android.comaefd2bc2009-03-30 21:02:14 +00002002};
2003
2004// The size of a flat paint's POD fields
scroggo@google.com5cd36562012-10-09 20:02:20 +00002005// Include an SkScalar for hinting scale factor whether it is
2006// supported or not so that an SKP is valid whether it was
2007// created with support or not.
reed@google.com1f1543f2012-09-12 21:08:33 +00002008
reed@google.com1f1543f2012-09-12 21:08:33 +00002009static const uint32_t kPODPaintSize = 6 * sizeof(SkScalar) +
2010 1 * sizeof(SkColor) +
2011 1 * sizeof(uint16_t) +
2012 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002013
2014/* To save space/time, we analyze the paint, and write a truncated version of
2015 it if there are not tricky elements like shaders, etc.
2016 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002018 uint8_t flatFlags = 0;
2019 if (this->getTypeface()) {
2020 flatFlags |= kHasTypeface_FlatFlag;
2021 }
2022 if (asint(this->getPathEffect()) |
2023 asint(this->getShader()) |
2024 asint(this->getXfermode()) |
2025 asint(this->getMaskFilter()) |
2026 asint(this->getColorFilter()) |
2027 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002028 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00002029 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002030 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00002031 flatFlags |= kHasEffects_FlatFlag;
2032 }
reed@google.com72cf4922011-01-04 19:58:20 +00002033
reed@android.comaefd2bc2009-03-30 21:02:14 +00002034
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002035 if (buffer.isOrderedBinaryBuffer()) {
2036 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2037 uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
2038
2039 ptr = write_scalar(ptr, this->getTextSize());
2040 ptr = write_scalar(ptr, this->getTextScaleX());
2041 ptr = write_scalar(ptr, this->getTextSkewX());
reed@google.com1f1543f2012-09-12 21:08:33 +00002042#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2043 ptr = write_scalar(ptr, this->getHintingScaleFactor());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002044#else
2045 // Dummy value.
2046 ptr = write_scalar(ptr, SK_Scalar1);
reed@google.com1f1543f2012-09-12 21:08:33 +00002047#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002048 ptr = write_scalar(ptr, this->getStrokeWidth());
2049 ptr = write_scalar(ptr, this->getStrokeMiter());
2050 *ptr++ = this->getColor();
2051 // previously flags:16, textAlign:8, flatFlags:8
2052 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2053 *ptr++ = (this->getFlags() << 16) |
2054 // hinting added later. 0 in this nibble means use the default.
2055 ((this->getHinting()+1) << 12) |
2056 (this->getTextAlign() << 8) |
2057 flatFlags;
2058 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2059 this->getStyle(), this->getTextEncoding());
2060 } else {
2061 buffer.writeScalar(fTextSize);
2062 buffer.writeScalar(fTextScaleX);
2063 buffer.writeScalar(fTextSkewX);
reed@google.com1f1543f2012-09-12 21:08:33 +00002064#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2065 buffer.writeScalar(fHintingScaleFactor);
scroggo@google.com5cd36562012-10-09 20:02:20 +00002066#else
2067 // Dummy value.
2068 buffer.writeScalar(SK_Scalar1);
reed@google.com1f1543f2012-09-12 21:08:33 +00002069#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002070 buffer.writeScalar(fWidth);
2071 buffer.writeScalar(fMiterLimit);
2072 buffer.writeColor(fColor);
2073 buffer.writeUInt(fFlags);
2074 buffer.writeUInt(fHinting);
2075 buffer.writeUInt(fTextAlign);
2076 buffer.writeUInt(flatFlags);
2077
2078 buffer.writeUInt(fCapType);
2079 buffer.writeUInt(fJoinType);
2080 buffer.writeUInt(fStyle);
2081 buffer.writeUInt(fTextEncoding);
2082 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002083
2084 // now we're done with ptr and the (pre)reserved space. If we need to write
2085 // additional fields, use the buffer directly
2086 if (flatFlags & kHasTypeface_FlatFlag) {
2087 buffer.writeTypeface(this->getTypeface());
2088 }
2089 if (flatFlags & kHasEffects_FlatFlag) {
2090 buffer.writeFlattenable(this->getPathEffect());
2091 buffer.writeFlattenable(this->getShader());
2092 buffer.writeFlattenable(this->getXfermode());
2093 buffer.writeFlattenable(this->getMaskFilter());
2094 buffer.writeFlattenable(this->getColorFilter());
2095 buffer.writeFlattenable(this->getRasterizer());
2096 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002097 buffer.writeFlattenable(this->getImageFilter());
reed@google.comb0a34d82012-07-11 19:57:55 +00002098 buffer.writeFlattenable(this->getAnnotation());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002099 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100}
2101
2102void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@google.comb0a34d82012-07-11 19:57:55 +00002103 fPrivFlags = 0;
2104
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002105 uint8_t flatFlags = 0;
2106 if (buffer.isOrderedBinaryBuffer()) {
2107 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2108 const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
2109 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002110
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002111 // the order we read must match the order we wrote in flatten()
2112 this->setTextSize(read_scalar(pod));
2113 this->setTextScaleX(read_scalar(pod));
2114 this->setTextSkewX(read_scalar(pod));
reed@google.com1f1543f2012-09-12 21:08:33 +00002115#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2116 this->setHintingScaleFactor(read_scalar(pod));
scroggo@google.com5cd36562012-10-09 20:02:20 +00002117#else
2118 // Skip the hinting scalar factor, which is not supported.
2119 read_scalar(pod);
reed@google.com1f1543f2012-09-12 21:08:33 +00002120#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002121 this->setStrokeWidth(read_scalar(pod));
2122 this->setStrokeMiter(read_scalar(pod));
2123 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002124
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002125 // previously flags:16, textAlign:8, flatFlags:8
2126 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2127 uint32_t tmp = *pod++;
2128 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00002129
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002130 // hinting added later. 0 in this nibble means use the default.
2131 uint32_t hinting = (tmp >> 12) & 0xF;
2132 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
bungeman@google.com24babf42011-11-07 16:33:40 +00002133
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002134 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002135
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002136 flatFlags = tmp & 0xFF;
2137
2138 tmp = *pod++;
2139 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2140 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2141 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2142 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
2143 } else {
2144 this->setTextSize(buffer.readScalar());
2145 this->setTextScaleX(buffer.readScalar());
2146 this->setTextSkewX(buffer.readScalar());
reed@google.com1f1543f2012-09-12 21:08:33 +00002147#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2148 this->setHintingScaleFactor(buffer.readScalar());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002149#else
2150 // Skip the hinting scalar factor, which is not supported.
2151 buffer.readScalar();
reed@google.com1f1543f2012-09-12 21:08:33 +00002152#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002153 this->setStrokeWidth(buffer.readScalar());
2154 this->setStrokeMiter(buffer.readScalar());
2155 this->setColor(buffer.readColor());
2156 this->setFlags(buffer.readUInt());
2157 this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
2158 this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
2159 flatFlags = buffer.readUInt();
2160
2161 this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
2162 this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
2163 this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
2164 this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
2165 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002166
2167 if (flatFlags & kHasTypeface_FlatFlag) {
2168 this->setTypeface(buffer.readTypeface());
2169 } else {
2170 this->setTypeface(NULL);
2171 }
2172
2173 if (flatFlags & kHasEffects_FlatFlag) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002174 SkSafeUnref(this->setPathEffect(buffer.readFlattenableT<SkPathEffect>()));
2175 SkSafeUnref(this->setShader(buffer.readFlattenableT<SkShader>()));
2176 SkSafeUnref(this->setXfermode(buffer.readFlattenableT<SkXfermode>()));
2177 SkSafeUnref(this->setMaskFilter(buffer.readFlattenableT<SkMaskFilter>()));
2178 SkSafeUnref(this->setColorFilter(buffer.readFlattenableT<SkColorFilter>()));
2179 SkSafeUnref(this->setRasterizer(buffer.readFlattenableT<SkRasterizer>()));
2180 SkSafeUnref(this->setLooper(buffer.readFlattenableT<SkDrawLooper>()));
2181 SkSafeUnref(this->setImageFilter(buffer.readFlattenableT<SkImageFilter>()));
2182 SkSafeUnref(this->setAnnotation(buffer.readFlattenableT<SkAnnotation>()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002183 } else {
2184 this->setPathEffect(NULL);
2185 this->setShader(NULL);
2186 this->setXfermode(NULL);
2187 this->setMaskFilter(NULL);
2188 this->setColorFilter(NULL);
2189 this->setRasterizer(NULL);
2190 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002191 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002192 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193}
2194
2195///////////////////////////////////////////////////////////////////////////////
2196
reed@google.com82065d62011-02-07 15:30:46 +00002197SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002198 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 SkRefCnt_SafeAssign(fShader, shader);
2200 return shader;
2201}
2202
reed@google.com82065d62011-02-07 15:30:46 +00002203SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002204 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 SkRefCnt_SafeAssign(fColorFilter, filter);
2206 return filter;
2207}
2208
reed@google.com82065d62011-02-07 15:30:46 +00002209SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002210 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 SkRefCnt_SafeAssign(fXfermode, mode);
2212 return mode;
2213}
2214
reed@android.com0baf1932009-06-24 12:41:42 +00002215SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002216 SkSafeUnref(fXfermode);
2217 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002218 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002219 return fXfermode;
2220}
2221
reed@google.com82065d62011-02-07 15:30:46 +00002222SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002223 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 SkRefCnt_SafeAssign(fPathEffect, effect);
2225 return effect;
2226}
2227
reed@google.com82065d62011-02-07 15:30:46 +00002228SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002229 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230 SkRefCnt_SafeAssign(fMaskFilter, filter);
2231 return filter;
2232}
2233
reed@google.com82065d62011-02-07 15:30:46 +00002234///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235
reed@google.com4bbdeac2013-01-24 21:03:11 +00002236bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2237 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002238 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239
reed@google.comfd4be262012-05-25 01:04:12 +00002240 const SkPath* srcPtr = &src;
2241 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002242
reed@google.com4bbdeac2013-01-24 21:03:11 +00002243 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002244 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 }
2246
reed@google.comfd4be262012-05-25 01:04:12 +00002247 if (!rec.applyToPath(dst, *srcPtr)) {
2248 if (srcPtr == &tmpPath) {
2249 // If path's were copy-on-write, this trick would not be needed.
2250 // As it is, we want to save making a deep-copy from tmpPath -> dst
2251 // since we know we're just going to delete tmpPath when we return,
2252 // so the swap saves that copy.
2253 dst->swap(tmpPath);
2254 } else {
2255 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 }
2257 }
reed@google.comfd4be262012-05-25 01:04:12 +00002258 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259}
2260
reed@google.come4f10a72012-05-15 20:47:50 +00002261const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002262 SkRect* storage,
2263 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002265
reed@google.come4f10a72012-05-15 20:47:50 +00002266 const SkRect* src = &origSrc;
2267
reed@google.com9efd9a02012-01-30 15:41:43 +00002268 if (this->getLooper()) {
2269 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002270 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002271 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002273
reed@google.come4f10a72012-05-15 20:47:50 +00002274 SkRect tmpSrc;
2275 if (this->getPathEffect()) {
2276 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2277 src = &tmpSrc;
2278 }
2279
reed@google.coma584aed2012-05-16 14:06:02 +00002280 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002281 // since we're stroked, outset the rect by the radius (and join type)
2282 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2283 if (0 == radius) { // hairline
2284 radius = SK_Scalar1;
2285 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2286 SkScalar scale = this->getStrokeMiter();
2287 if (scale > SK_Scalar1) {
2288 radius = SkScalarMul(radius, scale);
2289 }
2290 }
reed@google.come4f10a72012-05-15 20:47:50 +00002291 storage->set(src->fLeft - radius, src->fTop - radius,
2292 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002293 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002294 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002295 }
2296
reed@google.com9efd9a02012-01-30 15:41:43 +00002297 if (this->getMaskFilter()) {
2298 this->getMaskFilter()->computeFastBounds(*storage, storage);
2299 }
2300
reed@android.comd252db02009-04-01 18:31:44 +00002301 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302}
2303
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002304#ifdef SK_DEVELOPER
2305void SkPaint::toString(SkString* str) const {
2306 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2307
2308 SkTypeface* typeface = this->getTypeface();
2309 if (NULL != typeface) {
2310 SkDynamicMemoryWStream ostream;
2311 typeface->serialize(&ostream);
2312 SkAutoTUnref<SkData> data(ostream.copyToData());
2313
2314 SkMemoryStream stream(data);
2315 SkFontDescriptor descriptor(&stream);
2316
2317 str->append("<dt>Font Family Name:</dt><dd>");
2318 str->append(descriptor.getFamilyName());
2319 str->append("</dd><dt>Font Full Name:</dt><dd>");
2320 str->append(descriptor.getFullName());
2321 str->append("</dd><dt>Font PS Name:</dt><dd>");
2322 str->append(descriptor.getPostscriptName());
2323 str->append("</dd><dt>Font File Name:</dt><dd>");
2324 str->append(descriptor.getFontFileName());
2325 str->append("</dd>");
2326 }
2327
2328 str->append("<dt>TextSize:</dt><dd>");
2329 str->appendScalar(this->getTextSize());
2330 str->append("</dd>");
2331
2332 str->append("<dt>TextScaleX:</dt><dd>");
2333 str->appendScalar(this->getTextScaleX());
2334 str->append("</dd>");
2335
2336 str->append("<dt>TextSkewX:</dt><dd>");
2337 str->appendScalar(this->getTextSkewX());
2338 str->append("</dd>");
2339
2340 SkPathEffect* pathEffect = this->getPathEffect();
2341 if (NULL != pathEffect) {
2342 str->append("<dt>PathEffect:</dt><dd>");
2343 str->append("</dd>");
2344 }
2345
2346 SkShader* shader = this->getShader();
2347 if (NULL != shader) {
2348 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002349 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002350 str->append("</dd>");
2351 }
2352
2353 SkXfermode* xfer = this->getXfermode();
2354 if (NULL != xfer) {
2355 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002356 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002357 str->append("</dd>");
2358 }
2359
2360 SkMaskFilter* maskFilter = this->getMaskFilter();
2361 if (NULL != maskFilter) {
2362 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002363 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002364 str->append("</dd>");
2365 }
2366
2367 SkColorFilter* colorFilter = this->getColorFilter();
2368 if (NULL != colorFilter) {
2369 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002370 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002371 str->append("</dd>");
2372 }
2373
2374 SkRasterizer* rasterizer = this->getRasterizer();
2375 if (NULL != rasterizer) {
2376 str->append("<dt>Rasterizer:</dt><dd>");
2377 str->append("</dd>");
2378 }
2379
2380 SkDrawLooper* looper = this->getLooper();
2381 if (NULL != looper) {
2382 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002383 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002384 str->append("</dd>");
2385 }
2386
2387 SkImageFilter* imageFilter = this->getImageFilter();
2388 if (NULL != imageFilter) {
2389 str->append("<dt>ImageFilter:</dt><dd>");
2390 str->append("</dd>");
2391 }
2392
2393 SkAnnotation* annotation = this->getAnnotation();
2394 if (NULL != annotation) {
2395 str->append("<dt>Annotation:</dt><dd>");
2396 str->append("</dd>");
2397 }
2398
2399 str->append("<dt>Color:</dt><dd>0x");
2400 SkColor color = this->getColor();
2401 str->appendHex(color);
2402 str->append("</dd>");
2403
2404 str->append("<dt>Stroke Width:</dt><dd>");
2405 str->appendScalar(this->getStrokeWidth());
2406 str->append("</dd>");
2407
2408 str->append("<dt>Stroke Miter:</dt><dd>");
2409 str->appendScalar(this->getStrokeMiter());
2410 str->append("</dd>");
2411
2412 str->append("<dt>Flags:</dt><dd>(");
2413 if (this->getFlags()) {
2414 bool needSeparator = false;
2415 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
2416 SkAddFlagToString(str, this->isFilterBitmap(), "FilterBitmap", &needSeparator);
2417 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2418 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2419 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2420 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2421 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2422 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2423 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2424 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2425 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2426 "EmbeddedBitmapText", &needSeparator);
2427 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2428 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2429 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2430 "GenA8FromLCD", &needSeparator);
2431 } else {
2432 str->append("None");
2433 }
2434 str->append(")</dd>");
2435
2436 str->append("<dt>TextAlign:</dt><dd>");
2437 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2438 str->append(gTextAlignStrings[this->getTextAlign()]);
2439 str->append("</dd>");
2440
2441 str->append("<dt>CapType:</dt><dd>");
2442 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2443 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2444 str->append("</dd>");
2445
2446 str->append("<dt>JoinType:</dt><dd>");
2447 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2448 str->append(gJoinStrings[this->getStrokeJoin()]);
2449 str->append("</dd>");
2450
2451 str->append("<dt>Style:</dt><dd>");
2452 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2453 str->append(gStyleStrings[this->getStyle()]);
2454 str->append("</dd>");
2455
2456 str->append("<dt>TextEncoding:</dt><dd>");
2457 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2458 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2459 str->append("</dd>");
2460
2461 str->append("<dt>Hinting:</dt><dd>");
2462 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2463 str->append(gHintingStrings[this->getHinting()]);
2464 str->append("</dd>");
2465
2466 str->append("</dd></dl></dl>");
2467}
2468#endif
2469
2470
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002471///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002472
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002473static bool has_thick_frame(const SkPaint& paint) {
2474 return paint.getStrokeWidth() > 0 &&
2475 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002476}
2477
2478SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2479 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002480 bool applyStrokeAndPathEffects)
2481 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2483 true);
2484
djsollen@google.com166e6532012-03-20 14:24:38 +00002485 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2487
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002488 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002489 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002490 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002491
djsollen@google.com166e6532012-03-20 14:24:38 +00002492 // can't use our canonical size if we need to apply patheffects
2493 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2495 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002496 if (has_thick_frame(fPaint)) {
2497 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2498 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002499 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002500 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002501 }
reed@google.com72cf4922011-01-04 19:58:20 +00002502
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002503 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002504 fPaint.setStyle(SkPaint::kFill_Style);
2505 fPaint.setPathEffect(NULL);
2506 }
2507
bungeman@google.com532470f2013-01-22 19:25:14 +00002508 fCache = fPaint.detachCache(NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002509
2510 SkPaint::Style style = SkPaint::kFill_Style;
2511 SkPathEffect* pe = NULL;
2512
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002513 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002514 style = paint.getStyle(); // restore
2515 pe = paint.getPathEffect(); // restore
2516 }
2517 fPaint.setStyle(style);
2518 fPaint.setPathEffect(pe);
2519 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2520
2521 // now compute fXOffset if needed
2522
2523 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002524 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002526 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2527 &count, NULL), fScale);
2528 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002529 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002530 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002531 xOffset = -width;
2532 }
2533 fXPos = xOffset;
2534 fPrevAdvance = 0;
2535
2536 fText = text;
2537 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002538
reed@google.com44da42e2011-11-10 20:04:47 +00002539 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002540}
2541
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002542SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002543 SkGlyphCache::AttachCache(fCache);
2544}
2545
reed@google.com7b4531f2012-08-07 15:53:00 +00002546bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2547 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2549
2550 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002551 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002552
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002553 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002554 if (path) {
2555 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002556 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002557 } else {
2558 if (path) {
2559 *path = NULL;
2560 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002562 if (xpos) {
2563 *xpos = fXPos;
2564 }
2565 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002566 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002567 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002569
2570///////////////////////////////////////////////////////////////////////////////
2571
2572bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002573 if (fLooper) {
2574 return false;
2575 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002576 SkXfermode::Mode mode;
2577 if (SkXfermode::AsMode(fXfermode, &mode)) {
2578 switch (mode) {
2579 case SkXfermode::kSrcOver_Mode:
2580 case SkXfermode::kSrcATop_Mode:
2581 case SkXfermode::kDstOut_Mode:
2582 case SkXfermode::kDstOver_Mode:
2583 case SkXfermode::kPlus_Mode:
2584 return 0 == this->getAlpha();
2585 case SkXfermode::kDst_Mode:
2586 return true;
2587 default:
2588 break;
2589 }
2590 }
2591 return false;
2592}