blob: c7cbce18c77e48f3f864dd91446b8be82cfedd87 [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@android.com8a1c16f2008-12-17 15:59:43 +000033#include "SkTypeface.h"
34#include "SkXfermode.h"
robertphillips@google.com791f12e2013-02-14 13:53:53 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
reed@google.coma3237872011-07-05 19:20:48 +000037// define this to get a printf for out-of-range parameter in setters
38// e.g. setTextSize(-1)
39//#define SK_REPORT_API_RANGE_CHECK
40
djsollen@google.com56c69772011-11-08 19:00:26 +000041#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000042#define GEN_ID_INC fGenerationID++
43#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
44#else
45#define GEN_ID_INC
46#define GEN_ID_INC_EVAL(expression)
47#endif
48
reed@android.coma3122b92009-08-13 20:38:25 +000049SkPaint::SkPaint() {
50 // since we may have padding, we zero everything so that our memcmp() call
51 // in operator== will work correctly.
52 // with this, we can skip 0 and null individual initializations
53 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000054
reed@android.coma3122b92009-08-13 20:38:25 +000055#if 0 // not needed with the bzero call above
56 fTypeface = NULL;
57 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 fPathEffect = NULL;
59 fShader = NULL;
60 fXfermode = NULL;
61 fMaskFilter = NULL;
62 fColorFilter = NULL;
63 fRasterizer = NULL;
64 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000065 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000066 fAnnotation = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000068#endif
69
reed@google.comaefdd062012-02-29 13:03:00 +000070 fTextSize = SkPaintDefaults_TextSize;
reed@android.coma3122b92009-08-13 20:38:25 +000071 fTextScaleX = SK_Scalar1;
reed@google.com1f1543f2012-09-12 21:08:33 +000072#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
73 fHintingScaleFactor = SK_Scalar1;
74#endif
reed@android.coma3122b92009-08-13 20:38:25 +000075 fColor = SK_ColorBLACK;
reed@google.comaefdd062012-02-29 13:03:00 +000076 fMiterLimit = SkPaintDefaults_MiterLimit;
77 fFlags = SkPaintDefaults_Flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 fCapType = kDefault_Cap;
79 fJoinType = kDefault_Join;
80 fTextAlign = kLeft_Align;
81 fStyle = kFill_Style;
82 fTextEncoding = kUTF8_TextEncoding;
reed@google.comaefdd062012-02-29 13:03:00 +000083 fHinting = SkPaintDefaults_Hinting;
reed@google.comb0a34d82012-07-11 19:57:55 +000084 fPrivFlags = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +000085#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +000086 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000087 fGenerationID = 0;
88#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000089}
90
reed@google.com6fb7e2e2011-02-08 22:22:52 +000091SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 memcpy(this, &src, sizeof(src));
93
reed@google.com82065d62011-02-07 15:30:46 +000094 SkSafeRef(fTypeface);
95 SkSafeRef(fPathEffect);
96 SkSafeRef(fShader);
97 SkSafeRef(fXfermode);
98 SkSafeRef(fMaskFilter);
99 SkSafeRef(fColorFilter);
100 SkSafeRef(fRasterizer);
101 SkSafeRef(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000102 SkSafeRef(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000103 SkSafeRef(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104}
105
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000106SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +0000107 SkSafeUnref(fTypeface);
108 SkSafeUnref(fPathEffect);
109 SkSafeUnref(fShader);
110 SkSafeUnref(fXfermode);
111 SkSafeUnref(fMaskFilter);
112 SkSafeUnref(fColorFilter);
113 SkSafeUnref(fRasterizer);
114 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000115 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000116 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117}
118
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000119SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 SkASSERT(&src);
121
reed@google.com82065d62011-02-07 15:30:46 +0000122 SkSafeRef(src.fTypeface);
123 SkSafeRef(src.fPathEffect);
124 SkSafeRef(src.fShader);
125 SkSafeRef(src.fXfermode);
126 SkSafeRef(src.fMaskFilter);
127 SkSafeRef(src.fColorFilter);
128 SkSafeRef(src.fRasterizer);
129 SkSafeRef(src.fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000130 SkSafeRef(src.fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000131 SkSafeRef(src.fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132
reed@google.com82065d62011-02-07 15:30:46 +0000133 SkSafeUnref(fTypeface);
134 SkSafeUnref(fPathEffect);
135 SkSafeUnref(fShader);
136 SkSafeUnref(fXfermode);
137 SkSafeUnref(fMaskFilter);
138 SkSafeUnref(fColorFilter);
139 SkSafeUnref(fRasterizer);
140 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000141 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000142 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143
djsollen@google.com56c69772011-11-08 19:00:26 +0000144#ifdef SK_BUILD_FOR_ANDROID
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000145 fPaintOptionsAndroid.~SkPaintOptionsAndroid();
146
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000147 uint32_t oldGenerationID = fGenerationID;
148#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 memcpy(this, &src, sizeof(src));
djsollen@google.com56c69772011-11-08 19:00:26 +0000150#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000151 fGenerationID = oldGenerationID + 1;
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000152
153 new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000154#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
156 return *this;
157}
158
reed@google.comb530ef52011-07-20 19:55:42 +0000159bool operator==(const SkPaint& a, const SkPaint& b) {
djsollen@google.comb44cd652011-12-01 17:09:21 +0000160#ifdef SK_BUILD_FOR_ANDROID
161 //assumes that fGenerationID is the last field in the struct
162 return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
163#else
reed@google.comb530ef52011-07-20 19:55:42 +0000164 return !memcmp(&a, &b, sizeof(a));
djsollen@google.comb44cd652011-12-01 17:09:21 +0000165#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166}
167
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000168void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 SkPaint init;
170
djsollen@google.com56c69772011-11-08 19:00:26 +0000171#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000172 uint32_t oldGenerationID = fGenerationID;
173#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000175#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000176 fGenerationID = oldGenerationID + 1;
177#endif
178}
179
djsollen@google.com56c69772011-11-08 19:00:26 +0000180#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000181uint32_t SkPaint::getGenerationID() const {
182 return fGenerationID;
183}
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000184
185void SkPaint::setGenerationID(uint32_t generationID) {
186 fGenerationID = generationID;
187}
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000188
djsollen@google.com60abb072012-02-15 18:49:15 +0000189unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
bungeman@google.comba3284e2013-01-22 19:49:33 +0000190 SkAutoGlyphCache autoCache(*this, NULL, NULL);
djsollen@google.com60abb072012-02-15 18:49:15 +0000191 SkGlyphCache* cache = autoCache.getCache();
192 return cache->getBaseGlyphCount(text);
193}
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000194
195void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) {
196 if (options != fPaintOptionsAndroid) {
197 fPaintOptionsAndroid = options;
198 GEN_ID_INC;
199 }
200}
djsollen@google.com60abb072012-02-15 18:49:15 +0000201#endif
202
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000203void SkPaint::setHinting(Hinting hintingLevel) {
204 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
205 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206}
207
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000208void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000209 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 fFlags = flags;
211}
212
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000213void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
215}
216
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000217void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
219}
220
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000221void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
223}
224
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000225void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000226 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
227}
228
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000229void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000230 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
231}
232
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000233void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000234 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
235}
236
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000237void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
239}
240
reed@google.com830a23e2011-11-10 15:20:49 +0000241void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000242 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
243}
244
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000245void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
247}
248
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000249void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
251}
252
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000253void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
255}
256
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000257void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
259}
260
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000261void SkPaint::setFilterBitmap(bool doFilter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
263}
264
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000265void SkPaint::setStyle(Style style) {
266 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000267 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000269 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000270#ifdef SK_REPORT_API_RANGE_CHECK
271 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
272#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274}
275
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000276void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000277 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 fColor = color;
279}
280
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000281void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000282 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
283 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284}
285
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000286void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000287 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288}
289
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000290void SkPaint::setStrokeWidth(SkScalar width) {
291 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000292 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000294 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000295#ifdef SK_REPORT_API_RANGE_CHECK
296 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
297#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299}
300
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000301void SkPaint::setStrokeMiter(SkScalar limit) {
302 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000303 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000305 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000306#ifdef SK_REPORT_API_RANGE_CHECK
307 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
308#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310}
311
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000312void SkPaint::setStrokeCap(Cap ct) {
313 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000314 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000316 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000317#ifdef SK_REPORT_API_RANGE_CHECK
318 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
319#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321}
322
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323void SkPaint::setStrokeJoin(Join jt) {
324 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000325 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000327 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000328#ifdef SK_REPORT_API_RANGE_CHECK
329 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
330#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332}
333
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000334///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000336void SkPaint::setTextAlign(Align align) {
337 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000338 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000340 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000341#ifdef SK_REPORT_API_RANGE_CHECK
342 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
343#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345}
346
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000347void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000348 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000349 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000351 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000352#ifdef SK_REPORT_API_RANGE_CHECK
353 SkDebugf("SkPaint::setTextSize() called with negative value\n");
354#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356}
357
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000358void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000359 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fTextScaleX = scaleX;
361}
362
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000363void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000364 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 fTextSkewX = skewX;
366}
367
reed@google.com1f1543f2012-09-12 21:08:33 +0000368#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
369void SkPaint::setHintingScaleFactor(SkScalar hintingScaleFactor) {
370 GEN_ID_INC_EVAL(hintingScaleFactor != fHintingScaleFactor);
371 fHintingScaleFactor = hintingScaleFactor;
372}
373#endif
374
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000375void SkPaint::setTextEncoding(TextEncoding encoding) {
376 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000377 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000379 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000380#ifdef SK_REPORT_API_RANGE_CHECK
381 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
382#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000383 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384}
385
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000386///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000388SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000390 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 return font;
392}
393
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000394SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000396 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 return r;
398}
399
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000400SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000402 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 return looper;
404}
405
reed@google.com15356a62011-11-03 19:29:08 +0000406SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
407 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
408 GEN_ID_INC;
409 return imageFilter;
410}
411
reed@google.comb0a34d82012-07-11 19:57:55 +0000412SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
413 SkRefCnt_SafeAssign(fAnnotation, annotation);
414 GEN_ID_INC;
415
416 bool isNoDraw = annotation && annotation->isNoDraw();
417 fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag);
418
419 return annotation;
420}
421
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422///////////////////////////////////////////////////////////////////////////////
423
424#include "SkGlyphCache.h"
425#include "SkUtils.h"
426
reed@google.com90808e82013-03-19 14:44:54 +0000427static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc,
428 void* context) {
429 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000430}
431
djsollen@google.com56c69772011-11-08 19:00:26 +0000432#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000433const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text,
434 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000435 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000436 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000437
438 const SkGlyph& glyph = cache->getUnicharMetrics(text);
439
440 SkGlyphCache::AttachCache(cache);
441 return glyph;
442}
443
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000444const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId,
445 const SkMatrix* deviceMatrix) {
djsollen@google.com60abb072012-02-15 18:49:15 +0000446 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000447 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.com60abb072012-02-15 18:49:15 +0000448
449 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
450
451 SkGlyphCache::AttachCache(cache);
452 return glyph;
453}
454
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000455const void* SkPaint::findImage(const SkGlyph& glyph,
456 const SkMatrix* deviceMatrix) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000457 // See ::detachCache()
458 SkGlyphCache* cache;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000459 descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000460
461 const void* image = cache->findImage(glyph);
462
463 SkGlyphCache::AttachCache(cache);
464 return image;
465}
466#endif
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
469 uint16_t glyphs[]) const {
470 if (byteLength == 0) {
471 return 0;
472 }
reed@google.com72cf4922011-01-04 19:58:20 +0000473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 SkASSERT(textData != NULL);
475
476 if (NULL == glyphs) {
477 switch (this->getTextEncoding()) {
478 case kUTF8_TextEncoding:
479 return SkUTF8_CountUnichars((const char*)textData, byteLength);
480 case kUTF16_TextEncoding:
481 return SkUTF16_CountUnichars((const uint16_t*)textData,
482 byteLength >> 1);
reed@google.com68bc6f72012-03-14 19:41:55 +0000483 case kUTF32_TextEncoding:
484 return byteLength >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 case kGlyphID_TextEncoding:
486 return byteLength >> 1;
487 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000488 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 }
490 return 0;
491 }
reed@google.com72cf4922011-01-04 19:58:20 +0000492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 // handle this encoding before the setup for the glyphcache
496 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
497 // we want to ignore the low bit of byteLength
498 memcpy(glyphs, textData, byteLength >> 1 << 1);
499 return byteLength >> 1;
500 }
reed@google.com72cf4922011-01-04 19:58:20 +0000501
bungeman@google.com532470f2013-01-22 19:25:14 +0000502 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 SkGlyphCache* cache = autoCache.getCache();
504
505 const char* text = (const char*)textData;
506 const char* stop = text + byteLength;
507 uint16_t* gptr = glyphs;
508
509 switch (this->getTextEncoding()) {
510 case SkPaint::kUTF8_TextEncoding:
511 while (text < stop) {
512 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
513 }
514 break;
515 case SkPaint::kUTF16_TextEncoding: {
516 const uint16_t* text16 = (const uint16_t*)text;
517 const uint16_t* stop16 = (const uint16_t*)stop;
518 while (text16 < stop16) {
519 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
520 }
521 break;
522 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000523 case kUTF32_TextEncoding: {
524 const int32_t* text32 = (const int32_t*)text;
525 const int32_t* stop32 = (const int32_t*)stop;
526 while (text32 < stop32) {
527 *gptr++ = cache->unicharToGlyph(*text32++);
528 }
529 break;
530 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000532 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 }
534 return gptr - glyphs;
535}
536
reed@android.coma5dcaf62010-02-05 17:12:32 +0000537bool SkPaint::containsText(const void* textData, size_t byteLength) const {
538 if (0 == byteLength) {
539 return true;
540 }
reed@google.com72cf4922011-01-04 19:58:20 +0000541
reed@android.coma5dcaf62010-02-05 17:12:32 +0000542 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000543
reed@android.coma5dcaf62010-02-05 17:12:32 +0000544 // handle this encoding before the setup for the glyphcache
545 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
546 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
547 size_t count = byteLength >> 1;
548 for (size_t i = 0; i < count; i++) {
549 if (0 == glyphID[i]) {
550 return false;
551 }
552 }
553 return true;
554 }
reed@google.com72cf4922011-01-04 19:58:20 +0000555
bungeman@google.com532470f2013-01-22 19:25:14 +0000556 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.coma5dcaf62010-02-05 17:12:32 +0000557 SkGlyphCache* cache = autoCache.getCache();
558
559 switch (this->getTextEncoding()) {
560 case SkPaint::kUTF8_TextEncoding: {
561 const char* text = static_cast<const char*>(textData);
562 const char* stop = text + byteLength;
563 while (text < stop) {
564 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
565 return false;
566 }
567 }
568 break;
569 }
570 case SkPaint::kUTF16_TextEncoding: {
571 const uint16_t* text = static_cast<const uint16_t*>(textData);
572 const uint16_t* stop = text + (byteLength >> 1);
573 while (text < stop) {
574 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
575 return false;
576 }
577 }
578 break;
579 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000580 case SkPaint::kUTF32_TextEncoding: {
581 const int32_t* text = static_cast<const int32_t*>(textData);
582 const int32_t* stop = text + (byteLength >> 2);
583 while (text < stop) {
584 if (0 == cache->unicharToGlyph(*text++)) {
585 return false;
586 }
587 }
588 break;
589 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000590 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000591 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000592 return false;
593 }
594 return true;
595}
596
reed@android.com9d3a9852010-01-08 14:07:42 +0000597void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
598 SkUnichar textData[]) const {
599 if (count <= 0) {
600 return;
601 }
602
603 SkASSERT(glyphs != NULL);
604 SkASSERT(textData != NULL);
605
bungeman@google.com532470f2013-01-22 19:25:14 +0000606 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com9d3a9852010-01-08 14:07:42 +0000607 SkGlyphCache* cache = autoCache.getCache();
608
609 for (int index = 0; index < count; index++) {
610 textData[index] = cache->glyphToUnichar(glyphs[index]);
611 }
612}
613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614///////////////////////////////////////////////////////////////////////////////
615
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000616static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
617 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 SkASSERT(cache != NULL);
619 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
622}
623
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000624static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
625 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 SkASSERT(cache != NULL);
627 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
630}
631
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000632static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
633 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 SkASSERT(cache != NULL);
635 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
638}
639
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000640static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
641 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkASSERT(cache != NULL);
643 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
646}
647
reed@google.com68bc6f72012-03-14 19:41:55 +0000648static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
649 const char** text) {
650 SkASSERT(cache != NULL);
651 SkASSERT(text != NULL);
652
653 const int32_t* ptr = *(const int32_t**)text;
654 SkUnichar uni = *ptr++;
655 *text = (const char*)ptr;
656 return cache->getUnicharMetrics(uni);
657}
658
659static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
660 const char** text) {
661 SkASSERT(cache != NULL);
662 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000663
reed@google.com68bc6f72012-03-14 19:41:55 +0000664 const int32_t* ptr = *(const int32_t**)text;
665 SkUnichar uni = *--ptr;
666 *text = (const char*)ptr;
667 return cache->getUnicharMetrics(uni);
668}
669
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000670static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
671 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 SkASSERT(cache != NULL);
673 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000674
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 const uint16_t* ptr = *(const uint16_t**)text;
676 unsigned glyphID = *ptr;
677 ptr += 1;
678 *text = (const char*)ptr;
679 return cache->getGlyphIDMetrics(glyphID);
680}
681
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000682static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
683 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 SkASSERT(cache != NULL);
685 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000686
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 const uint16_t* ptr = *(const uint16_t**)text;
688 ptr -= 1;
689 unsigned glyphID = *ptr;
690 *text = (const char*)ptr;
691 return cache->getGlyphIDMetrics(glyphID);
692}
693
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000694static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
695 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 SkASSERT(cache != NULL);
697 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
700}
701
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000702static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
703 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 SkASSERT(cache != NULL);
705 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000706
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
708}
709
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000710static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
711 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 SkASSERT(cache != NULL);
713 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
716}
717
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000718static const SkGlyph& sk_getAdvance_utf16_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 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
724}
725
reed@google.com68bc6f72012-03-14 19:41:55 +0000726static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
727 const char** text) {
728 SkASSERT(cache != NULL);
729 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000730
reed@google.com68bc6f72012-03-14 19:41:55 +0000731 const int32_t* ptr = *(const int32_t**)text;
732 SkUnichar uni = *ptr++;
733 *text = (const char*)ptr;
734 return cache->getUnicharAdvance(uni);
735}
736
737static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
738 const char** text) {
739 SkASSERT(cache != NULL);
740 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000741
reed@google.com68bc6f72012-03-14 19:41:55 +0000742 const int32_t* ptr = *(const int32_t**)text;
743 SkUnichar uni = *--ptr;
744 *text = (const char*)ptr;
745 return cache->getUnicharAdvance(uni);
746}
747
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000748static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
749 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 SkASSERT(cache != NULL);
751 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000752
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 const uint16_t* ptr = *(const uint16_t**)text;
754 unsigned glyphID = *ptr;
755 ptr += 1;
756 *text = (const char*)ptr;
757 return cache->getGlyphIDAdvance(glyphID);
758}
759
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000760static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
761 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 SkASSERT(cache != NULL);
763 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000764
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 const uint16_t* ptr = *(const uint16_t**)text;
766 ptr -= 1;
767 unsigned glyphID = *ptr;
768 *text = (const char*)ptr;
769 return cache->getGlyphIDAdvance(glyphID);
770}
771
772SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000773 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
775 sk_getMetrics_utf8_next,
776 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000777 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 sk_getMetrics_utf8_prev,
781 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000782 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 sk_getAdvance_utf8_next,
786 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000787 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 sk_getAdvance_utf8_prev,
791 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000792 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 sk_getAdvance_glyph_prev
794 };
reed@google.com72cf4922011-01-04 19:58:20 +0000795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 unsigned index = this->getTextEncoding();
797
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000798 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000799 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000800 }
801 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000802 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000803 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804
805 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
806 return gMeasureCacheProcs[index];
807}
808
809///////////////////////////////////////////////////////////////////////////////
810
811static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000812 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 SkASSERT(cache != NULL);
814 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
817}
818
819static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000820 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 SkASSERT(cache != NULL);
822 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
825}
826
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000827static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
828 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 SkASSERT(cache != NULL);
830 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000831
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
833}
834
835static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000836 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 SkASSERT(cache != NULL);
838 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
841 x, y);
842}
843
reed@google.com68bc6f72012-03-14 19:41:55 +0000844static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
845 const char** text, SkFixed, SkFixed) {
846 SkASSERT(cache != NULL);
847 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000848
reed@google.com68bc6f72012-03-14 19:41:55 +0000849 const int32_t* ptr = *(const int32_t**)text;
850 SkUnichar uni = *ptr++;
851 *text = (const char*)ptr;
852 return cache->getUnicharMetrics(uni);
853}
854
855static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
856 const char** text, SkFixed x, SkFixed y) {
857 SkASSERT(cache != NULL);
858 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000859
reed@google.com68bc6f72012-03-14 19:41:55 +0000860 const int32_t* ptr = *(const int32_t**)text;
861 SkUnichar uni = *--ptr;
862 *text = (const char*)ptr;
sugoi@google.com8d38d512013-02-26 21:56:19 +0000863 return cache->getUnicharMetrics(uni, x, y);
reed@google.com68bc6f72012-03-14 19:41:55 +0000864}
865
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000866static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
867 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 SkASSERT(cache != NULL);
869 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000870
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 const uint16_t* ptr = *(const uint16_t**)text;
872 unsigned glyphID = *ptr;
873 ptr += 1;
874 *text = (const char*)ptr;
875 return cache->getGlyphIDMetrics(glyphID);
876}
877
878static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000879 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 SkASSERT(cache != NULL);
881 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000882
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 const uint16_t* ptr = *(const uint16_t**)text;
884 unsigned glyphID = *ptr;
885 ptr += 1;
886 *text = (const char*)ptr;
887 return cache->getGlyphIDMetrics(glyphID, x, y);
888}
889
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000890SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 static const SkDrawCacheProc gDrawCacheProcs[] = {
892 sk_getMetrics_utf8_00,
893 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000894 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000896
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 sk_getMetrics_utf8_xy,
898 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000899 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 sk_getMetrics_glyph_xy
901 };
reed@google.com72cf4922011-01-04 19:58:20 +0000902
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000904 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000905 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000906 }
reed@google.com72cf4922011-01-04 19:58:20 +0000907
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
909 return gDrawCacheProcs[index];
910}
911
912///////////////////////////////////////////////////////////////////////////////
913
914class SkAutoRestorePaintTextSizeAndFrame {
915public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000916 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
917 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 fTextSize = paint->getTextSize();
919 fStyle = paint->getStyle();
920 fPaint->setStyle(SkPaint::kFill_Style);
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000921#ifdef SK_BUILD_FOR_ANDROID
922 fGenerationID = fPaint->getGenerationID();
923#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000925
926 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 fPaint->setStyle(fStyle);
928 fPaint->setTextSize(fTextSize);
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000929#ifdef SK_BUILD_FOR_ANDROID
930 fPaint->setGenerationID(fGenerationID);
931#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
reed@google.com72cf4922011-01-04 19:58:20 +0000933
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934private:
935 SkPaint* fPaint;
936 SkScalar fTextSize;
937 SkPaint::Style fStyle;
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000938#ifdef SK_BUILD_FOR_ANDROID
939 uint32_t fGenerationID;
940#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941};
942
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000943static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 bounds->set(SkIntToScalar(g.fLeft),
945 SkIntToScalar(g.fTop),
946 SkIntToScalar(g.fLeft + g.fWidth),
947 SkIntToScalar(g.fTop + g.fHeight));
948}
949
reed@android.come88f5512010-03-19 14:42:28 +0000950// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
951// we don't overflow along the way
952typedef int64_t Sk48Dot16;
953
954#ifdef SK_SCALAR_IS_FLOAT
955 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000956 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000957 }
958#else
959 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
960 // just return the low 32bits
961 return static_cast<SkFixed>(x);
962 }
963#endif
964
reed@google.com44da42e2011-11-10 20:04:47 +0000965static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000966 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 bounds->join(SkIntToScalar(g.fLeft) + sx,
968 SkIntToScalar(g.fTop),
969 SkIntToScalar(g.fLeft + g.fWidth) + sx,
970 SkIntToScalar(g.fTop + g.fHeight));
971}
972
reed@google.com44da42e2011-11-10 20:04:47 +0000973static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
974 SkScalar sy = Sk48Dot16ToScalar(dy);
975 bounds->join(SkIntToScalar(g.fLeft),
976 SkIntToScalar(g.fTop) + sy,
977 SkIntToScalar(g.fLeft + g.fWidth),
978 SkIntToScalar(g.fTop + g.fHeight) + sy);
979}
980
981typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
982
983// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
984static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
985 SkASSERT(0 == xyIndex || 1 == xyIndex);
986 return (&glyph.fAdvanceX)[xyIndex];
987}
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989SkScalar SkPaint::measure_text(SkGlyphCache* cache,
990 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000991 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000993 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000995 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000997 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 return 0;
999 }
1000
1001 SkMeasureCacheProc glyphCacheProc;
1002 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1003 NULL != bounds);
1004
reed@google.com44da42e2011-11-10 20:04:47 +00001005 int xyIndex;
1006 JoinBoundsProc joinBoundsProc;
1007 if (this->isVerticalText()) {
1008 xyIndex = 1;
1009 joinBoundsProc = join_bounds_y;
1010 } else {
1011 xyIndex = 0;
1012 joinBoundsProc = join_bounds_x;
1013 }
1014
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 int n = 1;
1016 const char* stop = (const char*)text + byteLength;
1017 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +00001018 // our accumulated fixed-point advances might overflow 16.16, so we use
1019 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
1020 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +00001021 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022
1023 SkAutoKern autokern;
1024
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001025 if (NULL == bounds) {
1026 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 int rsb;
1028 for (; text < stop; n++) {
1029 rsb = g->fRsbDelta;
1030 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001031 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001033 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +00001035 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 }
1037 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001038 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001040 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 int rsb;
1042 for (; text < stop; n++) {
1043 rsb = g->fRsbDelta;
1044 g = &glyphCacheProc(cache, &text);
1045 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001046 joinBoundsProc(*g, bounds, x);
1047 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001049 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 for (; text < stop; n++) {
1051 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001052 joinBoundsProc(*g, bounds, x);
1053 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 }
1055 }
1056 }
1057 SkASSERT(text == stop);
1058
1059 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001060 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
1063SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001064 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 const char* text = (const char*)textData;
1066 SkASSERT(text != NULL || length == 0);
1067
1068 SkScalar scale = 0;
1069 SkAutoRestorePaintTextSizeAndFrame restore(this);
1070
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001071 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 scale = fTextSize / kCanonicalTextSizeForPaths;
1073 // this gets restored by restore
1074 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1075 }
reed@google.com72cf4922011-01-04 19:58:20 +00001076
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001077 SkMatrix zoomMatrix, *zoomPtr = NULL;
1078 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 zoomMatrix.setScale(zoom, zoom);
1080 zoomPtr = &zoomMatrix;
1081 }
1082
bungeman@google.com532470f2013-01-22 19:25:14 +00001083 SkAutoGlyphCache autoCache(*this, NULL, zoomPtr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 SkGlyphCache* cache = autoCache.getCache();
1085
1086 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001087
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001088 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 int tempCount;
1090
1091 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001092 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001094 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1096 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1097 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1098 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1099 }
1100 }
djsollen@google.com46348e22013-03-04 19:47:42 +00001101 } else if (bounds) {
1102 // ensure that even if we don't measure_text we still update the bounds
1103 bounds->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 }
1105 return width;
1106}
1107
1108typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1109
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001110static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 return text < stop;
1112}
1113
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001114static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 return text > stop;
1116}
1117
1118static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001119 const char** text, size_t length,
1120 const char** stop) {
1121 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 *stop = *text + length;
1123 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001124 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 // text should point to the end of the buffer, and stop to the beginning
1126 *stop = *text;
1127 *text += length;
1128 return backward_textBufferPred;
1129 }
1130}
1131
1132size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1133 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001134 TextBufferDirection tbd) const {
1135 if (0 == length || 0 >= maxWidth) {
1136 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001138 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 return 0;
1140 }
1141
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001142 if (0 == fTextSize) {
1143 if (measuredWidth) {
1144 *measuredWidth = 0;
1145 }
1146 return length;
1147 }
1148
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 SkASSERT(textD != NULL);
1150 const char* text = (const char*)textD;
1151
1152 SkScalar scale = 0;
1153 SkAutoRestorePaintTextSizeAndFrame restore(this);
1154
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001155 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +00001157 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 // this gets restored by restore
1159 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1160 }
reed@google.com72cf4922011-01-04 19:58:20 +00001161
bungeman@google.com532470f2013-01-22 19:25:14 +00001162 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 SkGlyphCache* cache = autoCache.getCache();
1164
1165 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
1166 const char* stop;
1167 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.com44da42e2011-11-10 20:04:47 +00001168 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001169 // use 64bits for our accumulator, to avoid overflowing 16.16
1170 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1171 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172
1173 SkAutoKern autokern;
1174
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001175 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001177 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 const char* curr = text;
1179 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001180 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001181 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 width -= x;
1183 text = curr;
1184 break;
1185 }
1186 rsb = g.fRsbDelta;
1187 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001188 } else {
1189 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001191 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001192 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 width -= x;
1194 text = curr;
1195 break;
1196 }
1197 }
1198 }
reed@google.com72cf4922011-01-04 19:58:20 +00001199
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001200 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001201 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001202 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001204 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 *measuredWidth = scalarWidth;
1206 }
reed@google.com72cf4922011-01-04 19:58:20 +00001207
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 // return the number of bytes measured
1209 return (kForward_TextBufferDirection == tbd) ?
1210 text - stop + length : stop - text + length;
1211}
1212
1213///////////////////////////////////////////////////////////////////////////////
1214
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001215static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@google.com0a01f5a2013-05-08 14:19:08 +00001216 *(SkPaint::FontMetrics*)context = cache->getFontMetrics();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 return false; // don't detach the cache
1218}
1219
reed@google.com90808e82013-03-19 14:44:54 +00001220static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc,
1221 void* context) {
1222 SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223}
1224
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001225SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 SkScalar scale = 0;
1227 SkAutoRestorePaintTextSizeAndFrame restore(this);
1228
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001229 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 scale = fTextSize / kCanonicalTextSizeForPaths;
1231 // this gets restored by restore
1232 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1233 }
reed@google.com72cf4922011-01-04 19:58:20 +00001234
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001235 SkMatrix zoomMatrix, *zoomPtr = NULL;
1236 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 zoomMatrix.setScale(zoom, zoom);
1238 zoomPtr = &zoomMatrix;
1239 }
1240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001242 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001244 }
reed@google.com72cf4922011-01-04 19:58:20 +00001245
bungeman@google.com532470f2013-01-22 19:25:14 +00001246 this->descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001248 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1250 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1251 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1252 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1253 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
reed@google.com0a01f5a2013-05-08 14:19:08 +00001254 metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale);
1255 metrics->fXMin = SkScalarMul(metrics->fXMin, scale);
1256 metrics->fXMax = SkScalarMul(metrics->fXMax, scale);
1257 metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 }
1259 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1260}
1261
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001262///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001264static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 bounds->set(g.fLeft * scale,
1266 g.fTop * scale,
1267 (g.fLeft + g.fWidth) * scale,
1268 (g.fTop + g.fHeight) * scale);
1269}
1270
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001271int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1272 SkScalar widths[], SkRect bounds[]) const {
1273 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
1277 SkASSERT(NULL != textData);
1278
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001279 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001281 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282
1283 SkAutoRestorePaintTextSizeAndFrame restore(this);
1284 SkScalar scale = 0;
1285
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001286 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 scale = fTextSize / kCanonicalTextSizeForPaths;
1288 // this gets restored by restore
1289 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1290 }
1291
bungeman@google.com532470f2013-01-22 19:25:14 +00001292 SkAutoGlyphCache autoCache(*this, NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 SkGlyphCache* cache = autoCache.getCache();
1294 SkMeasureCacheProc glyphCacheProc;
1295 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1296 NULL != bounds);
1297
1298 const char* text = (const char*)textData;
1299 const char* stop = text + byteLength;
1300 int count = 0;
reed@google.com44da42e2011-11-10 20:04:47 +00001301 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001303 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 // we adjust the widths returned here through auto-kerning
1305 SkAutoKern autokern;
1306 SkFixed prevWidth = 0;
1307
1308 if (scale) {
1309 while (text < stop) {
1310 const SkGlyph& g = glyphCacheProc(cache, &text);
1311 if (widths) {
1312 SkFixed adjust = autokern.adjust(g);
1313
1314 if (count > 0) {
1315 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1316 *widths++ = SkScalarMul(w, scale);
1317 }
reed@google.com44da42e2011-11-10 20:04:47 +00001318 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 }
1320 if (bounds) {
1321 set_bounds(g, bounds++, scale);
1322 }
1323 ++count;
1324 }
1325 if (count > 0 && widths) {
1326 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1327 }
1328 } else {
1329 while (text < stop) {
1330 const SkGlyph& g = glyphCacheProc(cache, &text);
1331 if (widths) {
1332 SkFixed adjust = autokern.adjust(g);
1333
1334 if (count > 0) {
1335 *widths++ = SkFixedToScalar(prevWidth + adjust);
1336 }
reed@google.com44da42e2011-11-10 20:04:47 +00001337 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 }
1339 if (bounds) {
1340 set_bounds(g, bounds++);
1341 }
1342 ++count;
1343 }
1344 if (count > 0 && widths) {
1345 *widths = SkFixedToScalar(prevWidth);
1346 }
1347 }
1348 } else { // no devkern
1349 if (scale) {
1350 while (text < stop) {
1351 const SkGlyph& g = glyphCacheProc(cache, &text);
1352 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001353 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 scale);
1355 }
1356 if (bounds) {
1357 set_bounds(g, bounds++, scale);
1358 }
1359 ++count;
1360 }
1361 } else {
1362 while (text < stop) {
1363 const SkGlyph& g = glyphCacheProc(cache, &text);
1364 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001365 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 }
1367 if (bounds) {
1368 set_bounds(g, bounds++);
1369 }
1370 ++count;
1371 }
1372 }
1373 }
1374
1375 SkASSERT(text == stop);
1376 return count;
1377}
1378
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001379///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380
1381#include "SkDraw.h"
1382
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001383void SkPaint::getTextPath(const void* textData, size_t length,
1384 SkScalar x, SkScalar y, SkPath* path) const {
1385 SkASSERT(length == 0 || textData != NULL);
1386
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001388 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001390 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
djsollen@google.com166e6532012-03-20 14:24:38 +00001392 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkMatrix matrix;
1394 SkScalar prevXPos = 0;
1395
1396 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1397 matrix.postTranslate(x, y);
1398 path->reset();
1399
1400 SkScalar xpos;
1401 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001402 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001404 if (iterPath) {
1405 path->addPath(*iterPath, matrix);
1406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 prevXPos = xpos;
1408 }
1409}
1410
reed@google.comca0062e2012-07-20 11:20:32 +00001411void SkPaint::getPosTextPath(const void* textData, size_t length,
1412 const SkPoint pos[], SkPath* path) const {
1413 SkASSERT(length == 0 || textData != NULL);
1414
1415 const char* text = (const char*)textData;
1416 if (text == NULL || length == 0 || path == NULL) {
1417 return;
1418 }
1419
1420 SkTextToPathIter iter(text, length, *this, false);
1421 SkMatrix matrix;
1422 SkPoint prevPos;
1423 prevPos.set(0, 0);
1424
1425 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1426 path->reset();
1427
1428 unsigned int i = 0;
1429 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001430 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001431 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001432 if (iterPath) {
1433 path->addPath(*iterPath, matrix);
1434 }
reed@google.comca0062e2012-07-20 11:20:32 +00001435 prevPos = pos[i];
1436 i++;
1437 }
1438}
1439
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440static void add_flattenable(SkDescriptor* desc, uint32_t tag,
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001441 SkOrderedWriteBuffer* buffer) {
1442 buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
reed@google.com2739b272011-09-28 17:26:42 +00001445// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001446static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001449 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001450 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001451 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001452 }
1453
reed@google.com65dd8f82011-03-14 13:31:16 +00001454 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001455 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001456 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001457
1458 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
reed@android.com1cdcb512009-08-24 19:11:00 +00001461// if linear-text is on, then we force hinting to be off (since that's sort of
1462// the point of linear-text.
1463static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1464 SkPaint::Hinting h = paint.getHinting();
1465 if (paint.isLinearText()) {
1466 h = SkPaint::kNo_Hinting;
1467 }
1468 return h;
1469}
1470
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001471// return true if the paint is just a single color (i.e. not a shader). If its
1472// a shader, then we can't compute a const luminance for it :(
1473static bool justAColor(const SkPaint& paint, SkColor* color) {
1474 if (paint.getShader()) {
1475 return false;
1476 }
1477 SkColor c = paint.getColor();
1478 if (paint.getColorFilter()) {
1479 c = paint.getColorFilter()->filterColor(c);
1480 }
1481 if (color) {
1482 *color = c;
1483 }
1484 return true;
1485}
1486
reed@google.comce6dbb62012-02-10 22:01:45 +00001487static SkColor computeLuminanceColor(const SkPaint& paint) {
1488 SkColor c;
1489 if (!justAColor(paint, &c)) {
1490 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1491 }
1492 return c;
1493}
reed@google.com813d38b2012-02-13 21:37:57 +00001494
reed@google.comdd43df92012-02-15 14:50:29 +00001495#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1496
reed@google.com4f79b9b2011-09-13 13:23:26 +00001497// Beyond this size, LCD doesn't appreciably improve quality, but it always
1498// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001499#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1500 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1501#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001502
1503static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1504 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1505 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1506 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001507 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001508}
1509
reed@google.com72cf4922011-01-04 19:58:20 +00001510/*
1511 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1512 * that vary only slightly when we create our key into the font cache, since the font scaler
1513 * typically returns the same looking resuts for tiny changes in the matrix.
1514 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001515static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001516#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001517 int n = sk_float_round2int(x * 1024);
1518 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001519#else
1520 // round to the nearest 10 fractional bits
1521 return (x + (1 << 5)) & ~(1024 - 1);
1522#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001523}
1524
reed@android.com36a4c2a2009-07-22 19:52:11 +00001525void SkScalerContext::MakeRec(const SkPaint& paint,
bungeman@google.com532470f2013-01-22 19:25:14 +00001526 const SkDeviceProperties* deviceProperties,
1527 const SkMatrix* deviceMatrix,
1528 Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001529 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530
bungeman@google.comcb1bbb32012-10-12 18:48:35 +00001531 SkTypeface* typeface = paint.getTypeface();
1532 if (NULL == typeface) {
1533 typeface = SkTypeface::GetDefaultTypeface();
1534 }
1535 rec->fOrigFontID = typeface->uniqueID();
reed@google.com7d26c592011-06-13 13:01:10 +00001536 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 rec->fTextSize = paint.getTextSize();
1538 rec->fPreScaleX = paint.getTextScaleX();
1539 rec->fPreSkewX = paint.getTextSkewX();
reed@google.com1f1543f2012-09-12 21:08:33 +00001540#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
1541 rec->fHintingScaleFactor = paint.getHintingScaleFactor();
1542#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001544 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001545 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1546 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1547 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1548 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001549 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1551 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1552 }
reed@google.com72cf4922011-01-04 19:58:20 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 SkPaint::Style style = paint.getStyle();
1555 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001556
reed@google.comffe49f52011-11-22 19:42:41 +00001557 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001558
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001559 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001560#ifdef SK_USE_FREETYPE_EMBOLDEN
1561 flags |= SkScalerContext::kEmbolden_Flag;
1562#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001563 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1564 kStdFakeBoldInterpKeys,
1565 kStdFakeBoldInterpValues,
1566 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001568
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001569 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 style = SkPaint::kStrokeAndFill_Style;
1571 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001572 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001574 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001575#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576 }
1577
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001578 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001580 }
reed@google.com72cf4922011-01-04 19:58:20 +00001581
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001582 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 rec->fFrameWidth = strokeWidth;
1584 rec->fMiterLimit = paint.getStrokeMiter();
1585 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1586
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001587 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001589 }
1590 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591 rec->fFrameWidth = 0;
1592 rec->fMiterLimit = 0;
1593 rec->fStrokeJoin = 0;
1594 }
1595
reed@google.com02b53312011-05-18 19:00:53 +00001596 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1597
bungeman@google.com532470f2013-01-22 19:25:14 +00001598 SkDeviceProperties::Geometry geometry = deviceProperties
1599 ? deviceProperties->fGeometry
1600 : SkDeviceProperties::Geometry::MakeDefault();
1601 if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
1602 if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001603 // eeek, can't support LCD
1604 rec->fMaskFormat = SkMask::kA8_Format;
1605 } else {
bungeman@google.com532470f2013-01-22 19:25:14 +00001606 if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
reed@google.com02b53312011-05-18 19:00:53 +00001607 flags |= SkScalerContext::kLCD_Vertical_Flag;
1608 }
bungeman@google.com532470f2013-01-22 19:25:14 +00001609 if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
reed@google.com02b53312011-05-18 19:00:53 +00001610 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1611 }
1612 }
1613 }
1614
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001615 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001616 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001617 }
1618 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001619 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001620 }
1621 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001622 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001623 }
reed@google.com830a23e2011-11-10 15:20:49 +00001624 if (paint.isVerticalText()) {
1625 flags |= SkScalerContext::kVertical_Flag;
1626 }
reed@google.com8351aab2012-01-18 17:06:35 +00001627 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1628 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1629 }
reed@google.com02b53312011-05-18 19:00:53 +00001630 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001631
reed@google.comffe49f52011-11-22 19:42:41 +00001632 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001633 rec->setHinting(computeHinting(paint));
1634
bungeman@google.com97efada2012-07-30 20:40:50 +00001635 rec->setLuminanceColor(computeLuminanceColor(paint));
bungeman@google.com532470f2013-01-22 19:25:14 +00001636
1637 if (NULL == deviceProperties) {
1638 rec->setDeviceGamma(SK_GAMMA_EXPONENT);
1639 rec->setPaintGamma(SK_GAMMA_EXPONENT);
1640 } else {
1641 rec->setDeviceGamma(deviceProperties->fGamma);
1642
1643 //For now always set the paint gamma equal to the device gamma.
1644 //The math in SkMaskGamma can handle them being different,
1645 //but it requires superluminous masks when
1646 //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1647 rec->setPaintGamma(deviceProperties->fGamma);
1648 }
1649
1650#ifdef SK_GAMMA_CONTRAST
1651 rec->setContrast(SK_GAMMA_CONTRAST);
bungeman@google.com97efada2012-07-30 20:40:50 +00001652#else
bungeman@google.com532470f2013-01-22 19:25:14 +00001653 /**
1654 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1655 * With lower values small text appears washed out (though correctly so).
1656 * With higher values lcd fringing is worse and the smoothing effect of
1657 * partial coverage is diminished.
1658 */
1659 rec->setContrast(SkFloatToScalar(0.5f));
bungeman@google.com97efada2012-07-30 20:40:50 +00001660#endif
bungeman@google.com532470f2013-01-22 19:25:14 +00001661
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001662 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001663
reed@android.com36a4c2a2009-07-22 19:52:11 +00001664 /* Allow the fonthost to modify our rec before we use it as a key into the
1665 cache. This way if we're asking for something that they will ignore,
1666 they can modify our rec up front, so we don't create duplicate cache
1667 entries.
1668 */
reed@google.comfed86bd2013-03-14 15:04:57 +00001669 typeface->onFilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001670
reed@google.com10d2d4d2012-03-01 22:32:51 +00001671 // be sure to call PostMakeRec(rec) before you actually use it!
1672}
1673
1674/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001675 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1676 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1677 * to hold it until the returned pointer is refed or forgotten.
1678 */
1679SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1680
bungeman@google.comae30f562012-09-11 18:44:55 +00001681static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001682static SkMaskGamma* gMaskGamma = NULL;
1683static SkScalar gContrast = SK_ScalarMin;
1684static SkScalar gPaintGamma = SK_ScalarMin;
1685static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001686/**
1687 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1688 * the returned SkMaskGamma pointer is refed or forgotten.
1689 */
bungeman@google.coma76de722012-10-26 19:35:54 +00001690static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001691 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1692 if (NULL == gLinearMaskGamma) {
1693 gLinearMaskGamma = SkNEW(SkMaskGamma);
1694 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001695 return *gLinearMaskGamma;
bungeman@google.comae30f562012-09-11 18:44:55 +00001696 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001697 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1698 SkSafeUnref(gMaskGamma);
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001699 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
bungeman@google.com97efada2012-07-30 20:40:50 +00001700 gContrast = contrast;
1701 gPaintGamma = paintGamma;
1702 gDeviceGamma = deviceGamma;
1703 }
bungeman@google.coma76de722012-10-26 19:35:54 +00001704 return *gMaskGamma;
bungeman@google.com97efada2012-07-30 20:40:50 +00001705}
1706
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001707/*static*/ void SkPaint::Term() {
1708 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1709
bungeman@google.comae30f562012-09-11 18:44:55 +00001710 SkSafeUnref(gLinearMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001711 gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001712 SkSafeUnref(gMaskGamma);
caryclark@google.com5987f582012-10-02 18:33:14 +00001713 gMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001714 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1715 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1716 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001717}
1718
bungeman@google.com97efada2012-07-30 20:40:50 +00001719/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001720 * We ensure that the rec is self-consistent and efficient (where possible)
1721 */
sugoi@google.com8d38d512013-02-26 21:56:19 +00001722void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001723 /**
1724 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001725 * limits the number of unique entries, and the scaler will only look at
1726 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001727 */
reed@google.comdd43df92012-02-15 14:50:29 +00001728 switch (rec->fMaskFormat) {
1729 case SkMask::kLCD16_Format:
1730 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001731 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001732 SkColor color = rec->getLuminanceColor();
reed@google.comb5715a12013-01-08 13:07:25 +00001733 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001734 break;
1735 }
1736 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001737 // filter down the luminance to a single component, since A8 can't
1738 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001739
bungeman@google.com97efada2012-07-30 20:40:50 +00001740 SkColor color = rec->getLuminanceColor();
bungeman@google.comb1a72cb2012-10-04 22:03:41 +00001741 U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001742 //If we are asked to look like LCD, look like LCD.
1743 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1744 // HACK: Prevents green from being pre-blended as white.
1745 lum -= ((255 - lum) * lum) / 255;
1746 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001747
reed@google.comdd43df92012-02-15 14:50:29 +00001748 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001749 color = SkColorSetRGB(lum, lum, lum);
reed@google.comb5715a12013-01-08 13:07:25 +00001750 rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001751 break;
1752 }
1753 case SkMask::kBW_Format:
1754 // No need to differentiate gamma if we're BW
bungeman@google.com532470f2013-01-22 19:25:14 +00001755 rec->ignorePreBlend();
reed@google.comdd43df92012-02-15 14:50:29 +00001756 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001757 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758}
1759
1760#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1761
reed@google.com17fb3872011-05-04 14:31:07 +00001762#ifdef SK_DEBUG
1763 #define TEST_DESC
1764#endif
1765
reed@google.comffe49f52011-11-22 19:42:41 +00001766/*
1767 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1768 * by gamma correction, so we jam the luminance field to 0 (most common value
1769 * for black text) in hopes that we get a cache hit easier. A better solution
1770 * would be for the fontcache lookup to know to ignore the luminance field
1771 * entirely, but not sure how to do that and keep it fast.
1772 */
bungeman@google.com532470f2013-01-22 19:25:14 +00001773void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
1774 const SkMatrix* deviceMatrix,
reed@google.com90808e82013-03-19 14:44:54 +00001775 void (*proc)(SkTypeface*, const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001776 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 SkScalerContext::Rec rec;
1778
bungeman@google.com532470f2013-01-22 19:25:14 +00001779 SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001780 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001781 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001782 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783
1784 size_t descSize = sizeof(rec);
1785 int entryCount = 1;
1786 SkPathEffect* pe = this->getPathEffect();
1787 SkMaskFilter* mf = this->getMaskFilter();
1788 SkRasterizer* ra = this->getRasterizer();
1789
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001790 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1791 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1792 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793
1794 if (pe) {
1795 peBuffer.writeFlattenable(pe);
1796 descSize += peBuffer.size();
1797 entryCount += 1;
1798 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1799 // seems like we could support kLCD as well at this point...
1800 }
1801 if (mf) {
1802 mfBuffer.writeFlattenable(mf);
1803 descSize += mfBuffer.size();
1804 entryCount += 1;
1805 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
bungeman@google.coma76de722012-10-26 19:35:54 +00001806 /* Pre-blend is not currently applied to filtered text.
1807 The primary filter is blur, for which contrast makes no sense,
1808 and for which the destination guess error is more visible.
1809 Also, all existing users of blur have calibrated for linear. */
1810 rec.ignorePreBlend();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 }
1812 if (ra) {
1813 raBuffer.writeFlattenable(ra);
1814 descSize += raBuffer.size();
1815 entryCount += 1;
1816 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1817 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001818
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001819#ifdef SK_BUILD_FOR_ANDROID
1820 SkOrderedWriteBuffer androidBuffer(128);
1821 fPaintOptionsAndroid.flatten(androidBuffer);
1822 descSize += androidBuffer.size();
1823 entryCount += 1;
1824#endif
1825
reed@google.com10d2d4d2012-03-01 22:32:51 +00001826 ///////////////////////////////////////////////////////////////////////////
1827 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001828 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001829
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830 descSize += SkDescriptor::ComputeOverhead(entryCount);
1831
1832 SkAutoDescriptor ad(descSize);
1833 SkDescriptor* desc = ad.getDesc();
1834
1835 desc->init();
1836 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1837
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001838#ifdef SK_BUILD_FOR_ANDROID
1839 add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1840#endif
1841
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 if (pe) {
1843 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1844 }
1845 if (mf) {
1846 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1847 }
1848 if (ra) {
1849 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1850 }
1851
1852 SkASSERT(descSize == desc->getLength());
1853 desc->computeChecksum();
1854
reed@google.com17fb3872011-05-04 14:31:07 +00001855#ifdef TEST_DESC
1856 {
1857 // Check that we completely write the bytes in desc (our key), and that
1858 // there are no uninitialized bytes. If there were, then we would get
1859 // false-misses (or worse, false-hits) in our fontcache.
1860 //
1861 // We do this buy filling 2 others, one with 0s and the other with 1s
1862 // and create those, and then check that all 3 are identical.
1863 SkAutoDescriptor ad1(descSize);
1864 SkAutoDescriptor ad2(descSize);
1865 SkDescriptor* desc1 = ad1.getDesc();
1866 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001867
reed@google.com17fb3872011-05-04 14:31:07 +00001868 memset(desc1, 0x00, descSize);
1869 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001870
reed@google.com17fb3872011-05-04 14:31:07 +00001871 desc1->init();
1872 desc2->init();
1873 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1874 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001875
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +00001876#ifdef SK_BUILD_FOR_ANDROID
1877 add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1878 add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer);
1879#endif
1880
reed@google.com17fb3872011-05-04 14:31:07 +00001881 if (pe) {
1882 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1883 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1884 }
1885 if (mf) {
1886 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1887 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1888 }
1889 if (ra) {
1890 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1891 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1892 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001893
reed@google.com17fb3872011-05-04 14:31:07 +00001894 SkASSERT(descSize == desc1->getLength());
1895 SkASSERT(descSize == desc2->getLength());
1896 desc1->computeChecksum();
1897 desc2->computeChecksum();
1898 SkASSERT(!memcmp(desc, desc1, descSize));
1899 SkASSERT(!memcmp(desc, desc2, descSize));
1900 }
1901#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001902
reed@google.com90808e82013-03-19 14:44:54 +00001903 proc(fTypeface, desc, context);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904}
1905
bungeman@google.com532470f2013-01-22 19:25:14 +00001906SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
1907 const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 SkGlyphCache* cache;
bungeman@google.com532470f2013-01-22 19:25:14 +00001909 this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 return cache;
1911}
1912
bungeman@google.com97efada2012-07-30 20:40:50 +00001913/**
1914 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1915 */
1916//static
1917SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1918 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
bungeman@google.coma76de722012-10-26 19:35:54 +00001919 const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
1920 rec.getPaintGamma(),
1921 rec.getDeviceGamma());
1922 return maskGamma.preBlend(rec.getLuminanceColor());
bungeman@google.com97efada2012-07-30 20:40:50 +00001923}
1924
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925///////////////////////////////////////////////////////////////////////////////
1926
1927#include "SkStream.h"
1928
reed@android.comaefd2bc2009-03-30 21:02:14 +00001929static uintptr_t asint(const void* p) {
1930 return reinterpret_cast<uintptr_t>(p);
1931}
1932
1933union Scalar32 {
1934 SkScalar fScalar;
1935 uint32_t f32;
1936};
1937
1938static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1939 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1940 Scalar32 tmp;
1941 tmp.fScalar = value;
1942 *ptr = tmp.f32;
1943 return ptr + 1;
1944}
1945
1946static SkScalar read_scalar(const uint32_t*& ptr) {
1947 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1948 Scalar32 tmp;
1949 tmp.f32 = *ptr++;
1950 return tmp.fScalar;
1951}
1952
1953static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1954 SkASSERT(a == (uint8_t)a);
1955 SkASSERT(b == (uint8_t)b);
1956 SkASSERT(c == (uint8_t)c);
1957 SkASSERT(d == (uint8_t)d);
1958 return (a << 24) | (b << 16) | (c << 8) | d;
1959}
1960
1961enum FlatFlags {
1962 kHasTypeface_FlatFlag = 0x01,
reed@google.comb0a34d82012-07-11 19:57:55 +00001963 kHasEffects_FlatFlag = 0x02,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001964};
1965
1966// The size of a flat paint's POD fields
scroggo@google.com5cd36562012-10-09 20:02:20 +00001967// Include an SkScalar for hinting scale factor whether it is
1968// supported or not so that an SKP is valid whether it was
1969// created with support or not.
reed@google.com1f1543f2012-09-12 21:08:33 +00001970
reed@google.com1f1543f2012-09-12 21:08:33 +00001971static const uint32_t kPODPaintSize = 6 * sizeof(SkScalar) +
1972 1 * sizeof(SkColor) +
1973 1 * sizeof(uint16_t) +
1974 6 * sizeof(uint8_t);
reed@android.comaefd2bc2009-03-30 21:02:14 +00001975
1976/* To save space/time, we analyze the paint, and write a truncated version of
1977 it if there are not tricky elements like shaders, etc.
1978 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001980 uint8_t flatFlags = 0;
1981 if (this->getTypeface()) {
1982 flatFlags |= kHasTypeface_FlatFlag;
1983 }
1984 if (asint(this->getPathEffect()) |
1985 asint(this->getShader()) |
1986 asint(this->getXfermode()) |
1987 asint(this->getMaskFilter()) |
1988 asint(this->getColorFilter()) |
1989 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001990 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001991 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001992 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001993 flatFlags |= kHasEffects_FlatFlag;
1994 }
reed@google.com72cf4922011-01-04 19:58:20 +00001995
reed@android.comaefd2bc2009-03-30 21:02:14 +00001996
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001997 if (buffer.isOrderedBinaryBuffer()) {
1998 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1999 uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
2000
2001 ptr = write_scalar(ptr, this->getTextSize());
2002 ptr = write_scalar(ptr, this->getTextScaleX());
2003 ptr = write_scalar(ptr, this->getTextSkewX());
reed@google.com1f1543f2012-09-12 21:08:33 +00002004#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2005 ptr = write_scalar(ptr, this->getHintingScaleFactor());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002006#else
2007 // Dummy value.
2008 ptr = write_scalar(ptr, SK_Scalar1);
reed@google.com1f1543f2012-09-12 21:08:33 +00002009#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002010 ptr = write_scalar(ptr, this->getStrokeWidth());
2011 ptr = write_scalar(ptr, this->getStrokeMiter());
2012 *ptr++ = this->getColor();
2013 // previously flags:16, textAlign:8, flatFlags:8
2014 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2015 *ptr++ = (this->getFlags() << 16) |
2016 // hinting added later. 0 in this nibble means use the default.
2017 ((this->getHinting()+1) << 12) |
2018 (this->getTextAlign() << 8) |
2019 flatFlags;
2020 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
2021 this->getStyle(), this->getTextEncoding());
2022 } else {
2023 buffer.writeScalar(fTextSize);
2024 buffer.writeScalar(fTextScaleX);
2025 buffer.writeScalar(fTextSkewX);
reed@google.com1f1543f2012-09-12 21:08:33 +00002026#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2027 buffer.writeScalar(fHintingScaleFactor);
scroggo@google.com5cd36562012-10-09 20:02:20 +00002028#else
2029 // Dummy value.
2030 buffer.writeScalar(SK_Scalar1);
reed@google.com1f1543f2012-09-12 21:08:33 +00002031#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002032 buffer.writeScalar(fWidth);
2033 buffer.writeScalar(fMiterLimit);
2034 buffer.writeColor(fColor);
2035 buffer.writeUInt(fFlags);
2036 buffer.writeUInt(fHinting);
2037 buffer.writeUInt(fTextAlign);
2038 buffer.writeUInt(flatFlags);
2039
2040 buffer.writeUInt(fCapType);
2041 buffer.writeUInt(fJoinType);
2042 buffer.writeUInt(fStyle);
2043 buffer.writeUInt(fTextEncoding);
2044 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002045
2046 // now we're done with ptr and the (pre)reserved space. If we need to write
2047 // additional fields, use the buffer directly
2048 if (flatFlags & kHasTypeface_FlatFlag) {
2049 buffer.writeTypeface(this->getTypeface());
2050 }
2051 if (flatFlags & kHasEffects_FlatFlag) {
2052 buffer.writeFlattenable(this->getPathEffect());
2053 buffer.writeFlattenable(this->getShader());
2054 buffer.writeFlattenable(this->getXfermode());
2055 buffer.writeFlattenable(this->getMaskFilter());
2056 buffer.writeFlattenable(this->getColorFilter());
2057 buffer.writeFlattenable(this->getRasterizer());
2058 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002059 buffer.writeFlattenable(this->getImageFilter());
reed@google.comb0a34d82012-07-11 19:57:55 +00002060 buffer.writeFlattenable(this->getAnnotation());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002061 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062}
2063
2064void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@google.comb0a34d82012-07-11 19:57:55 +00002065 fPrivFlags = 0;
2066
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002067 uint8_t flatFlags = 0;
2068 if (buffer.isOrderedBinaryBuffer()) {
2069 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2070 const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
2071 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002072
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002073 // the order we read must match the order we wrote in flatten()
2074 this->setTextSize(read_scalar(pod));
2075 this->setTextScaleX(read_scalar(pod));
2076 this->setTextSkewX(read_scalar(pod));
reed@google.com1f1543f2012-09-12 21:08:33 +00002077#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2078 this->setHintingScaleFactor(read_scalar(pod));
scroggo@google.com5cd36562012-10-09 20:02:20 +00002079#else
2080 // Skip the hinting scalar factor, which is not supported.
2081 read_scalar(pod);
reed@google.com1f1543f2012-09-12 21:08:33 +00002082#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002083 this->setStrokeWidth(read_scalar(pod));
2084 this->setStrokeMiter(read_scalar(pod));
2085 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002086
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002087 // previously flags:16, textAlign:8, flatFlags:8
2088 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2089 uint32_t tmp = *pod++;
2090 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00002091
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002092 // hinting added later. 0 in this nibble means use the default.
2093 uint32_t hinting = (tmp >> 12) & 0xF;
2094 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
bungeman@google.com24babf42011-11-07 16:33:40 +00002095
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002096 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002097
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002098 flatFlags = tmp & 0xFF;
2099
2100 tmp = *pod++;
2101 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2102 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2103 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2104 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
2105 } else {
2106 this->setTextSize(buffer.readScalar());
2107 this->setTextScaleX(buffer.readScalar());
2108 this->setTextSkewX(buffer.readScalar());
reed@google.com1f1543f2012-09-12 21:08:33 +00002109#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
2110 this->setHintingScaleFactor(buffer.readScalar());
scroggo@google.com5cd36562012-10-09 20:02:20 +00002111#else
2112 // Skip the hinting scalar factor, which is not supported.
2113 buffer.readScalar();
reed@google.com1f1543f2012-09-12 21:08:33 +00002114#endif
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002115 this->setStrokeWidth(buffer.readScalar());
2116 this->setStrokeMiter(buffer.readScalar());
2117 this->setColor(buffer.readColor());
2118 this->setFlags(buffer.readUInt());
2119 this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
2120 this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
2121 flatFlags = buffer.readUInt();
2122
2123 this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
2124 this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
2125 this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
2126 this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
2127 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002128
2129 if (flatFlags & kHasTypeface_FlatFlag) {
2130 this->setTypeface(buffer.readTypeface());
2131 } else {
2132 this->setTypeface(NULL);
2133 }
2134
2135 if (flatFlags & kHasEffects_FlatFlag) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002136 SkSafeUnref(this->setPathEffect(buffer.readFlattenableT<SkPathEffect>()));
2137 SkSafeUnref(this->setShader(buffer.readFlattenableT<SkShader>()));
2138 SkSafeUnref(this->setXfermode(buffer.readFlattenableT<SkXfermode>()));
2139 SkSafeUnref(this->setMaskFilter(buffer.readFlattenableT<SkMaskFilter>()));
2140 SkSafeUnref(this->setColorFilter(buffer.readFlattenableT<SkColorFilter>()));
2141 SkSafeUnref(this->setRasterizer(buffer.readFlattenableT<SkRasterizer>()));
2142 SkSafeUnref(this->setLooper(buffer.readFlattenableT<SkDrawLooper>()));
2143 SkSafeUnref(this->setImageFilter(buffer.readFlattenableT<SkImageFilter>()));
2144 SkSafeUnref(this->setAnnotation(buffer.readFlattenableT<SkAnnotation>()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002145 } else {
2146 this->setPathEffect(NULL);
2147 this->setShader(NULL);
2148 this->setXfermode(NULL);
2149 this->setMaskFilter(NULL);
2150 this->setColorFilter(NULL);
2151 this->setRasterizer(NULL);
2152 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002153 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155}
2156
2157///////////////////////////////////////////////////////////////////////////////
2158
reed@google.com82065d62011-02-07 15:30:46 +00002159SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002160 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 SkRefCnt_SafeAssign(fShader, shader);
2162 return shader;
2163}
2164
reed@google.com82065d62011-02-07 15:30:46 +00002165SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002166 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 SkRefCnt_SafeAssign(fColorFilter, filter);
2168 return filter;
2169}
2170
reed@google.com82065d62011-02-07 15:30:46 +00002171SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002172 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 SkRefCnt_SafeAssign(fXfermode, mode);
2174 return mode;
2175}
2176
reed@android.com0baf1932009-06-24 12:41:42 +00002177SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002178 SkSafeUnref(fXfermode);
2179 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002180 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002181 return fXfermode;
2182}
2183
reed@google.com82065d62011-02-07 15:30:46 +00002184SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002185 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 SkRefCnt_SafeAssign(fPathEffect, effect);
2187 return effect;
2188}
2189
reed@google.com82065d62011-02-07 15:30:46 +00002190SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002191 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192 SkRefCnt_SafeAssign(fMaskFilter, filter);
2193 return filter;
2194}
2195
reed@google.com82065d62011-02-07 15:30:46 +00002196///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197
reed@google.com4bbdeac2013-01-24 21:03:11 +00002198bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
2199 const SkRect* cullRect) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002200 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201
reed@google.comfd4be262012-05-25 01:04:12 +00002202 const SkPath* srcPtr = &src;
2203 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002204
reed@google.com4bbdeac2013-01-24 21:03:11 +00002205 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
reed@google.comfd4be262012-05-25 01:04:12 +00002206 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 }
2208
reed@google.comfd4be262012-05-25 01:04:12 +00002209 if (!rec.applyToPath(dst, *srcPtr)) {
2210 if (srcPtr == &tmpPath) {
2211 // If path's were copy-on-write, this trick would not be needed.
2212 // As it is, we want to save making a deep-copy from tmpPath -> dst
2213 // since we know we're just going to delete tmpPath when we return,
2214 // so the swap saves that copy.
2215 dst->swap(tmpPath);
2216 } else {
2217 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 }
2219 }
reed@google.comfd4be262012-05-25 01:04:12 +00002220 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221}
2222
reed@google.come4f10a72012-05-15 20:47:50 +00002223const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002224 SkRect* storage,
2225 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002227
reed@google.come4f10a72012-05-15 20:47:50 +00002228 const SkRect* src = &origSrc;
2229
reed@google.com9efd9a02012-01-30 15:41:43 +00002230 if (this->getLooper()) {
2231 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002232 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002233 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002234 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002235
reed@google.come4f10a72012-05-15 20:47:50 +00002236 SkRect tmpSrc;
2237 if (this->getPathEffect()) {
2238 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2239 src = &tmpSrc;
2240 }
2241
reed@google.coma584aed2012-05-16 14:06:02 +00002242 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002243 // since we're stroked, outset the rect by the radius (and join type)
2244 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2245 if (0 == radius) { // hairline
2246 radius = SK_Scalar1;
2247 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2248 SkScalar scale = this->getStrokeMiter();
2249 if (scale > SK_Scalar1) {
2250 radius = SkScalarMul(radius, scale);
2251 }
2252 }
reed@google.come4f10a72012-05-15 20:47:50 +00002253 storage->set(src->fLeft - radius, src->fTop - radius,
2254 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002255 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002256 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002257 }
2258
reed@google.com9efd9a02012-01-30 15:41:43 +00002259 if (this->getMaskFilter()) {
2260 this->getMaskFilter()->computeFastBounds(*storage, storage);
2261 }
2262
reed@android.comd252db02009-04-01 18:31:44 +00002263 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264}
2265
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002266#ifdef SK_DEVELOPER
2267void SkPaint::toString(SkString* str) const {
2268 str->append("<dl><dt>SkPaint:</dt><dd><dl>");
2269
2270 SkTypeface* typeface = this->getTypeface();
2271 if (NULL != typeface) {
2272 SkDynamicMemoryWStream ostream;
2273 typeface->serialize(&ostream);
2274 SkAutoTUnref<SkData> data(ostream.copyToData());
2275
2276 SkMemoryStream stream(data);
2277 SkFontDescriptor descriptor(&stream);
2278
2279 str->append("<dt>Font Family Name:</dt><dd>");
2280 str->append(descriptor.getFamilyName());
2281 str->append("</dd><dt>Font Full Name:</dt><dd>");
2282 str->append(descriptor.getFullName());
2283 str->append("</dd><dt>Font PS Name:</dt><dd>");
2284 str->append(descriptor.getPostscriptName());
2285 str->append("</dd><dt>Font File Name:</dt><dd>");
2286 str->append(descriptor.getFontFileName());
2287 str->append("</dd>");
2288 }
2289
2290 str->append("<dt>TextSize:</dt><dd>");
2291 str->appendScalar(this->getTextSize());
2292 str->append("</dd>");
2293
2294 str->append("<dt>TextScaleX:</dt><dd>");
2295 str->appendScalar(this->getTextScaleX());
2296 str->append("</dd>");
2297
2298 str->append("<dt>TextSkewX:</dt><dd>");
2299 str->appendScalar(this->getTextSkewX());
2300 str->append("</dd>");
2301
2302 SkPathEffect* pathEffect = this->getPathEffect();
2303 if (NULL != pathEffect) {
2304 str->append("<dt>PathEffect:</dt><dd>");
2305 str->append("</dd>");
2306 }
2307
2308 SkShader* shader = this->getShader();
2309 if (NULL != shader) {
2310 str->append("<dt>Shader:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002311 shader->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002312 str->append("</dd>");
2313 }
2314
2315 SkXfermode* xfer = this->getXfermode();
2316 if (NULL != xfer) {
2317 str->append("<dt>Xfermode:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002318 xfer->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002319 str->append("</dd>");
2320 }
2321
2322 SkMaskFilter* maskFilter = this->getMaskFilter();
2323 if (NULL != maskFilter) {
2324 str->append("<dt>MaskFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002325 maskFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002326 str->append("</dd>");
2327 }
2328
2329 SkColorFilter* colorFilter = this->getColorFilter();
2330 if (NULL != colorFilter) {
2331 str->append("<dt>ColorFilter:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002332 colorFilter->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002333 str->append("</dd>");
2334 }
2335
2336 SkRasterizer* rasterizer = this->getRasterizer();
2337 if (NULL != rasterizer) {
2338 str->append("<dt>Rasterizer:</dt><dd>");
2339 str->append("</dd>");
2340 }
2341
2342 SkDrawLooper* looper = this->getLooper();
2343 if (NULL != looper) {
2344 str->append("<dt>DrawLooper:</dt><dd>");
robertphillips@google.com1202c2a2013-05-23 14:00:17 +00002345 looper->toString(str);
robertphillips@google.com791f12e2013-02-14 13:53:53 +00002346 str->append("</dd>");
2347 }
2348
2349 SkImageFilter* imageFilter = this->getImageFilter();
2350 if (NULL != imageFilter) {
2351 str->append("<dt>ImageFilter:</dt><dd>");
2352 str->append("</dd>");
2353 }
2354
2355 SkAnnotation* annotation = this->getAnnotation();
2356 if (NULL != annotation) {
2357 str->append("<dt>Annotation:</dt><dd>");
2358 str->append("</dd>");
2359 }
2360
2361 str->append("<dt>Color:</dt><dd>0x");
2362 SkColor color = this->getColor();
2363 str->appendHex(color);
2364 str->append("</dd>");
2365
2366 str->append("<dt>Stroke Width:</dt><dd>");
2367 str->appendScalar(this->getStrokeWidth());
2368 str->append("</dd>");
2369
2370 str->append("<dt>Stroke Miter:</dt><dd>");
2371 str->appendScalar(this->getStrokeMiter());
2372 str->append("</dd>");
2373
2374 str->append("<dt>Flags:</dt><dd>(");
2375 if (this->getFlags()) {
2376 bool needSeparator = false;
2377 SkAddFlagToString(str, this->isAntiAlias(), "AntiAlias", &needSeparator);
2378 SkAddFlagToString(str, this->isFilterBitmap(), "FilterBitmap", &needSeparator);
2379 SkAddFlagToString(str, this->isDither(), "Dither", &needSeparator);
2380 SkAddFlagToString(str, this->isUnderlineText(), "UnderlineText", &needSeparator);
2381 SkAddFlagToString(str, this->isStrikeThruText(), "StrikeThruText", &needSeparator);
2382 SkAddFlagToString(str, this->isFakeBoldText(), "FakeBoldText", &needSeparator);
2383 SkAddFlagToString(str, this->isLinearText(), "LinearText", &needSeparator);
2384 SkAddFlagToString(str, this->isSubpixelText(), "SubpixelText", &needSeparator);
2385 SkAddFlagToString(str, this->isDevKernText(), "DevKernText", &needSeparator);
2386 SkAddFlagToString(str, this->isLCDRenderText(), "LCDRenderText", &needSeparator);
2387 SkAddFlagToString(str, this->isEmbeddedBitmapText(),
2388 "EmbeddedBitmapText", &needSeparator);
2389 SkAddFlagToString(str, this->isAutohinted(), "Autohinted", &needSeparator);
2390 SkAddFlagToString(str, this->isVerticalText(), "VerticalText", &needSeparator);
2391 SkAddFlagToString(str, SkToBool(this->getFlags() & SkPaint::kGenA8FromLCD_Flag),
2392 "GenA8FromLCD", &needSeparator);
2393 } else {
2394 str->append("None");
2395 }
2396 str->append(")</dd>");
2397
2398 str->append("<dt>TextAlign:</dt><dd>");
2399 static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
2400 str->append(gTextAlignStrings[this->getTextAlign()]);
2401 str->append("</dd>");
2402
2403 str->append("<dt>CapType:</dt><dd>");
2404 static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
2405 str->append(gStrokeCapStrings[this->getStrokeCap()]);
2406 str->append("</dd>");
2407
2408 str->append("<dt>JoinType:</dt><dd>");
2409 static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
2410 str->append(gJoinStrings[this->getStrokeJoin()]);
2411 str->append("</dd>");
2412
2413 str->append("<dt>Style:</dt><dd>");
2414 static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
2415 str->append(gStyleStrings[this->getStyle()]);
2416 str->append("</dd>");
2417
2418 str->append("<dt>TextEncoding:</dt><dd>");
2419 static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
2420 str->append(gTextEncodingStrings[this->getTextEncoding()]);
2421 str->append("</dd>");
2422
2423 str->append("<dt>Hinting:</dt><dd>");
2424 static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
2425 str->append(gHintingStrings[this->getHinting()]);
2426 str->append("</dd>");
2427
2428 str->append("</dd></dl></dl>");
2429}
2430#endif
2431
2432
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002433///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002435static bool has_thick_frame(const SkPaint& paint) {
2436 return paint.getStrokeWidth() > 0 &&
2437 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002438}
2439
2440SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2441 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002442 bool applyStrokeAndPathEffects)
2443 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2445 true);
2446
djsollen@google.com166e6532012-03-20 14:24:38 +00002447 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2449
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002450 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453
djsollen@google.com166e6532012-03-20 14:24:38 +00002454 // can't use our canonical size if we need to apply patheffects
2455 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2457 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002458 if (has_thick_frame(fPaint)) {
2459 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2460 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002461 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002463 }
reed@google.com72cf4922011-01-04 19:58:20 +00002464
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002465 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466 fPaint.setStyle(SkPaint::kFill_Style);
2467 fPaint.setPathEffect(NULL);
2468 }
2469
bungeman@google.com532470f2013-01-22 19:25:14 +00002470 fCache = fPaint.detachCache(NULL, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471
2472 SkPaint::Style style = SkPaint::kFill_Style;
2473 SkPathEffect* pe = NULL;
2474
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002475 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002476 style = paint.getStyle(); // restore
2477 pe = paint.getPathEffect(); // restore
2478 }
2479 fPaint.setStyle(style);
2480 fPaint.setPathEffect(pe);
2481 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2482
2483 // now compute fXOffset if needed
2484
2485 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002486 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002487 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002488 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2489 &count, NULL), fScale);
2490 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002491 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002492 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002493 xOffset = -width;
2494 }
2495 fXPos = xOffset;
2496 fPrevAdvance = 0;
2497
2498 fText = text;
2499 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002500
reed@google.com44da42e2011-11-10 20:04:47 +00002501 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002502}
2503
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002504SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 SkGlyphCache::AttachCache(fCache);
2506}
2507
reed@google.com7b4531f2012-08-07 15:53:00 +00002508bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2509 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2511
2512 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002513 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002514
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002515 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002516 if (path) {
2517 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002518 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002519 } else {
2520 if (path) {
2521 *path = NULL;
2522 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002523 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002524 if (xpos) {
2525 *xpos = fXPos;
2526 }
2527 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002529 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002530}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002531
2532///////////////////////////////////////////////////////////////////////////////
2533
2534bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002535 if (fLooper) {
2536 return false;
2537 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002538 SkXfermode::Mode mode;
2539 if (SkXfermode::AsMode(fXfermode, &mode)) {
2540 switch (mode) {
2541 case SkXfermode::kSrcOver_Mode:
2542 case SkXfermode::kSrcATop_Mode:
2543 case SkXfermode::kDstOut_Mode:
2544 case SkXfermode::kDstOver_Mode:
2545 case SkXfermode::kPlus_Mode:
2546 return 0 == this->getAlpha();
2547 case SkXfermode::kDst_Mode:
2548 return true;
2549 default:
2550 break;
2551 }
2552 }
2553 return false;
2554}
2555
2556
reed@google.com15356a62011-11-03 19:29:08 +00002557//////////// Move these to their own file soon.
2558
robertphillips@google.com0456e0b2012-06-27 14:03:26 +00002559SK_DEFINE_INST_COUNT(SkDrawLooper)
2560
reed@google.com9efd9a02012-01-30 15:41:43 +00002561bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
2562 SkCanvas canvas;
2563
2564 this->init(&canvas);
2565 for (;;) {
2566 SkPaint p(paint);
2567 if (this->next(&canvas, &p)) {
2568 p.setLooper(NULL);
2569 if (!p.canComputeFastBounds()) {
2570 return false;
2571 }
2572 } else {
2573 break;
2574 }
2575 }
2576 return true;
2577}
2578
2579void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
2580 SkRect* dst) {
2581 SkCanvas canvas;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002582
reed@google.com9efd9a02012-01-30 15:41:43 +00002583 this->init(&canvas);
2584 for (bool firstTime = true;; firstTime = false) {
2585 SkPaint p(paint);
2586 if (this->next(&canvas, &p)) {
2587 SkRect r(src);
2588
2589 p.setLooper(NULL);
2590 p.computeFastBounds(r, &r);
2591 canvas.getTotalMatrix().mapRect(&r);
2592
2593 if (firstTime) {
2594 *dst = r;
2595 } else {
2596 dst->join(r);
2597 }
2598 } else {
2599 break;
2600 }
2601 }
2602}