blob: 7e0ea97cdc8edb5fc3b164548ae5a398f29d65fe [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkFontHost.h"
reed@google.com15356a62011-11-03 19:29:08 +000013#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000015#include "SkMaskGamma.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkPathEffect.h"
17#include "SkRasterizer.h"
18#include "SkShader.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000019#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkScalerContext.h"
21#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000022#include "SkTextFormatParams.h"
reed@google.come6913762012-08-07 15:19:47 +000023#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTypeface.h"
25#include "SkXfermode.h"
26#include "SkAutoKern.h"
djsollen@google.com60abb072012-02-15 18:49:15 +000027#include "SkGlyphCache.h"
reed@google.comaefdd062012-02-29 13:03:00 +000028#include "SkPaintDefaults.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000029#include "SkOrderedReadBuffer.h"
djsollen@google.com2b2ede32012-04-12 13:24:04 +000030#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
reed@google.coma3237872011-07-05 19:20:48 +000032// define this to get a printf for out-of-range parameter in setters
33// e.g. setTextSize(-1)
34//#define SK_REPORT_API_RANGE_CHECK
35
djsollen@google.com56c69772011-11-08 19:00:26 +000036#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000037#define GEN_ID_INC fGenerationID++
38#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
39#else
40#define GEN_ID_INC
41#define GEN_ID_INC_EVAL(expression)
42#endif
43
reed@android.coma3122b92009-08-13 20:38:25 +000044SkPaint::SkPaint() {
45 // since we may have padding, we zero everything so that our memcmp() call
46 // in operator== will work correctly.
47 // with this, we can skip 0 and null individual initializations
48 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
reed@android.coma3122b92009-08-13 20:38:25 +000050#if 0 // not needed with the bzero call above
51 fTypeface = NULL;
52 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 fPathEffect = NULL;
54 fShader = NULL;
55 fXfermode = NULL;
56 fMaskFilter = NULL;
57 fColorFilter = NULL;
58 fRasterizer = NULL;
59 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000060 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000061 fAnnotation = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000063#endif
64
reed@google.comaefdd062012-02-29 13:03:00 +000065 fTextSize = SkPaintDefaults_TextSize;
reed@android.coma3122b92009-08-13 20:38:25 +000066 fTextScaleX = SK_Scalar1;
67 fColor = SK_ColorBLACK;
reed@google.comaefdd062012-02-29 13:03:00 +000068 fMiterLimit = SkPaintDefaults_MiterLimit;
69 fFlags = SkPaintDefaults_Flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 fCapType = kDefault_Cap;
71 fJoinType = kDefault_Join;
72 fTextAlign = kLeft_Align;
73 fStyle = kFill_Style;
74 fTextEncoding = kUTF8_TextEncoding;
reed@google.comaefdd062012-02-29 13:03:00 +000075 fHinting = SkPaintDefaults_Hinting;
reed@google.comb0a34d82012-07-11 19:57:55 +000076 fPrivFlags = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +000077#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000078 fGenerationID = 0;
79#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000080}
81
reed@google.com6fb7e2e2011-02-08 22:22:52 +000082SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 memcpy(this, &src, sizeof(src));
84
reed@google.com82065d62011-02-07 15:30:46 +000085 SkSafeRef(fTypeface);
86 SkSafeRef(fPathEffect);
87 SkSafeRef(fShader);
88 SkSafeRef(fXfermode);
89 SkSafeRef(fMaskFilter);
90 SkSafeRef(fColorFilter);
91 SkSafeRef(fRasterizer);
92 SkSafeRef(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +000093 SkSafeRef(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +000094 SkSafeRef(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +000095}
96
reed@google.com6fb7e2e2011-02-08 22:22:52 +000097SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000098 SkSafeUnref(fTypeface);
99 SkSafeUnref(fPathEffect);
100 SkSafeUnref(fShader);
101 SkSafeUnref(fXfermode);
102 SkSafeUnref(fMaskFilter);
103 SkSafeUnref(fColorFilter);
104 SkSafeUnref(fRasterizer);
105 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000106 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000107 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108}
109
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000110SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 SkASSERT(&src);
112
reed@google.com82065d62011-02-07 15:30:46 +0000113 SkSafeRef(src.fTypeface);
114 SkSafeRef(src.fPathEffect);
115 SkSafeRef(src.fShader);
116 SkSafeRef(src.fXfermode);
117 SkSafeRef(src.fMaskFilter);
118 SkSafeRef(src.fColorFilter);
119 SkSafeRef(src.fRasterizer);
120 SkSafeRef(src.fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000121 SkSafeRef(src.fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000122 SkSafeRef(src.fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
reed@google.com82065d62011-02-07 15:30:46 +0000124 SkSafeUnref(fTypeface);
125 SkSafeUnref(fPathEffect);
126 SkSafeUnref(fShader);
127 SkSafeUnref(fXfermode);
128 SkSafeUnref(fMaskFilter);
129 SkSafeUnref(fColorFilter);
130 SkSafeUnref(fRasterizer);
131 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000132 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000133 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134
djsollen@google.com56c69772011-11-08 19:00:26 +0000135#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000136 uint32_t oldGenerationID = fGenerationID;
137#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 memcpy(this, &src, sizeof(src));
djsollen@google.com56c69772011-11-08 19:00:26 +0000139#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000140 fGenerationID = oldGenerationID + 1;
141#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142
143 return *this;
144}
145
reed@google.comb530ef52011-07-20 19:55:42 +0000146bool operator==(const SkPaint& a, const SkPaint& b) {
djsollen@google.comb44cd652011-12-01 17:09:21 +0000147#ifdef SK_BUILD_FOR_ANDROID
148 //assumes that fGenerationID is the last field in the struct
149 return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
150#else
reed@google.comb530ef52011-07-20 19:55:42 +0000151 return !memcmp(&a, &b, sizeof(a));
djsollen@google.comb44cd652011-12-01 17:09:21 +0000152#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153}
154
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000155void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 SkPaint init;
157
djsollen@google.com56c69772011-11-08 19:00:26 +0000158#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000159 uint32_t oldGenerationID = fGenerationID;
160#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000162#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000163 fGenerationID = oldGenerationID + 1;
164#endif
165}
166
djsollen@google.com56c69772011-11-08 19:00:26 +0000167#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000168uint32_t SkPaint::getGenerationID() const {
169 return fGenerationID;
170}
171#endif
172
djsollen@google.com60abb072012-02-15 18:49:15 +0000173#ifdef SK_BUILD_FOR_ANDROID
174unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
175 SkAutoGlyphCache autoCache(*this, NULL);
176 SkGlyphCache* cache = autoCache.getCache();
177 return cache->getBaseGlyphCount(text);
178}
179#endif
180
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000181void SkPaint::setHinting(Hinting hintingLevel) {
182 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
183 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184}
185
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000186void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000187 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 fFlags = flags;
189}
190
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000191void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
193}
194
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000195void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
197}
198
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000199void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
201}
202
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000203void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000204 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
205}
206
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000207void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000208 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
209}
210
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000211void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000212 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
213}
214
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000215void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
217}
218
reed@google.com830a23e2011-11-10 15:20:49 +0000219void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000220 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
221}
222
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000223void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
225}
226
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000227void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
229}
230
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000231void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
233}
234
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000235void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
237}
238
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000239void SkPaint::setFilterBitmap(bool doFilter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
241}
242
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000243void SkPaint::setStyle(Style style) {
244 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000245 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000247 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000248#ifdef SK_REPORT_API_RANGE_CHECK
249 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
250#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252}
253
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000254void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000255 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fColor = color;
257}
258
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000259void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000260 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
261 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262}
263
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000264void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000265 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266}
267
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000268void SkPaint::setStrokeWidth(SkScalar width) {
269 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000270 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000272 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000273#ifdef SK_REPORT_API_RANGE_CHECK
274 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
275#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277}
278
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000279void SkPaint::setStrokeMiter(SkScalar limit) {
280 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000281 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000283 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000284#ifdef SK_REPORT_API_RANGE_CHECK
285 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
286#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288}
289
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000290void SkPaint::setStrokeCap(Cap ct) {
291 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000292 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000295#ifdef SK_REPORT_API_RANGE_CHECK
296 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
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::setStrokeJoin(Join jt) {
302 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000303 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000305 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000306#ifdef SK_REPORT_API_RANGE_CHECK
307 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
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 +0000312///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000314void SkPaint::setTextAlign(Align align) {
315 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000316 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000318 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000319#ifdef SK_REPORT_API_RANGE_CHECK
320 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
321#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323}
324
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000325void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000326 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000327 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000329 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000330#ifdef SK_REPORT_API_RANGE_CHECK
331 SkDebugf("SkPaint::setTextSize() called with negative value\n");
332#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334}
335
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000336void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000337 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 fTextScaleX = scaleX;
339}
340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000342 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 fTextSkewX = skewX;
344}
345
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000346void SkPaint::setTextEncoding(TextEncoding encoding) {
347 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000348 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000350 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000351#ifdef SK_REPORT_API_RANGE_CHECK
352 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
353#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355}
356
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000357///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000359SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000361 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 return font;
363}
364
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000365SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000367 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 return r;
369}
370
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000371SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000373 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 return looper;
375}
376
reed@google.com15356a62011-11-03 19:29:08 +0000377SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
378 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
379 GEN_ID_INC;
380 return imageFilter;
381}
382
reed@google.comb0a34d82012-07-11 19:57:55 +0000383SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
384 SkRefCnt_SafeAssign(fAnnotation, annotation);
385 GEN_ID_INC;
386
387 bool isNoDraw = annotation && annotation->isNoDraw();
388 fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag);
389
390 return annotation;
391}
392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393///////////////////////////////////////////////////////////////////////////////
394
395#include "SkGlyphCache.h"
396#include "SkUtils.h"
397
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000398static void DetachDescProc(const SkDescriptor* desc, void* context) {
399 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
400}
401
djsollen@google.com56c69772011-11-08 19:00:26 +0000402#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000403const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
404 SkGlyphCache* cache;
405 descriptorProc(NULL, DetachDescProc, &cache, true);
406
407 const SkGlyph& glyph = cache->getUnicharMetrics(text);
408
409 SkGlyphCache::AttachCache(cache);
410 return glyph;
411}
412
djsollen@google.com60abb072012-02-15 18:49:15 +0000413const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId) {
414 SkGlyphCache* cache;
415 descriptorProc(NULL, DetachDescProc, &cache, true);
416
417 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
418
419 SkGlyphCache::AttachCache(cache);
420 return glyph;
421}
422
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000423const void* SkPaint::findImage(const SkGlyph& glyph) {
424 // See ::detachCache()
425 SkGlyphCache* cache;
426 descriptorProc(NULL, DetachDescProc, &cache, true);
427
428 const void* image = cache->findImage(glyph);
429
430 SkGlyphCache::AttachCache(cache);
431 return image;
432}
433#endif
434
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
436 uint16_t glyphs[]) const {
437 if (byteLength == 0) {
438 return 0;
439 }
reed@google.com72cf4922011-01-04 19:58:20 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 SkASSERT(textData != NULL);
442
443 if (NULL == glyphs) {
444 switch (this->getTextEncoding()) {
445 case kUTF8_TextEncoding:
446 return SkUTF8_CountUnichars((const char*)textData, byteLength);
447 case kUTF16_TextEncoding:
448 return SkUTF16_CountUnichars((const uint16_t*)textData,
449 byteLength >> 1);
reed@google.com68bc6f72012-03-14 19:41:55 +0000450 case kUTF32_TextEncoding:
451 return byteLength >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 case kGlyphID_TextEncoding:
453 return byteLength >> 1;
454 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000455 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 }
457 return 0;
458 }
reed@google.com72cf4922011-01-04 19:58:20 +0000459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000461
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 // handle this encoding before the setup for the glyphcache
463 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
464 // we want to ignore the low bit of byteLength
465 memcpy(glyphs, textData, byteLength >> 1 << 1);
466 return byteLength >> 1;
467 }
reed@google.com72cf4922011-01-04 19:58:20 +0000468
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 SkAutoGlyphCache autoCache(*this, NULL);
470 SkGlyphCache* cache = autoCache.getCache();
471
472 const char* text = (const char*)textData;
473 const char* stop = text + byteLength;
474 uint16_t* gptr = glyphs;
475
476 switch (this->getTextEncoding()) {
477 case SkPaint::kUTF8_TextEncoding:
478 while (text < stop) {
479 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
480 }
481 break;
482 case SkPaint::kUTF16_TextEncoding: {
483 const uint16_t* text16 = (const uint16_t*)text;
484 const uint16_t* stop16 = (const uint16_t*)stop;
485 while (text16 < stop16) {
486 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
487 }
488 break;
489 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000490 case kUTF32_TextEncoding: {
491 const int32_t* text32 = (const int32_t*)text;
492 const int32_t* stop32 = (const int32_t*)stop;
493 while (text32 < stop32) {
494 *gptr++ = cache->unicharToGlyph(*text32++);
495 }
496 break;
497 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000499 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 }
501 return gptr - glyphs;
502}
503
reed@android.coma5dcaf62010-02-05 17:12:32 +0000504bool SkPaint::containsText(const void* textData, size_t byteLength) const {
505 if (0 == byteLength) {
506 return true;
507 }
reed@google.com72cf4922011-01-04 19:58:20 +0000508
reed@android.coma5dcaf62010-02-05 17:12:32 +0000509 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000510
reed@android.coma5dcaf62010-02-05 17:12:32 +0000511 // handle this encoding before the setup for the glyphcache
512 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
513 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
514 size_t count = byteLength >> 1;
515 for (size_t i = 0; i < count; i++) {
516 if (0 == glyphID[i]) {
517 return false;
518 }
519 }
520 return true;
521 }
reed@google.com72cf4922011-01-04 19:58:20 +0000522
reed@android.coma5dcaf62010-02-05 17:12:32 +0000523 SkAutoGlyphCache autoCache(*this, NULL);
524 SkGlyphCache* cache = autoCache.getCache();
525
526 switch (this->getTextEncoding()) {
527 case SkPaint::kUTF8_TextEncoding: {
528 const char* text = static_cast<const char*>(textData);
529 const char* stop = text + byteLength;
530 while (text < stop) {
531 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
532 return false;
533 }
534 }
535 break;
536 }
537 case SkPaint::kUTF16_TextEncoding: {
538 const uint16_t* text = static_cast<const uint16_t*>(textData);
539 const uint16_t* stop = text + (byteLength >> 1);
540 while (text < stop) {
541 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
542 return false;
543 }
544 }
545 break;
546 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000547 case SkPaint::kUTF32_TextEncoding: {
548 const int32_t* text = static_cast<const int32_t*>(textData);
549 const int32_t* stop = text + (byteLength >> 2);
550 while (text < stop) {
551 if (0 == cache->unicharToGlyph(*text++)) {
552 return false;
553 }
554 }
555 break;
556 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000557 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000558 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000559 return false;
560 }
561 return true;
562}
563
reed@android.com9d3a9852010-01-08 14:07:42 +0000564void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
565 SkUnichar textData[]) const {
566 if (count <= 0) {
567 return;
568 }
569
570 SkASSERT(glyphs != NULL);
571 SkASSERT(textData != NULL);
572
573 SkAutoGlyphCache autoCache(*this, NULL);
574 SkGlyphCache* cache = autoCache.getCache();
575
576 for (int index = 0; index < count; index++) {
577 textData[index] = cache->glyphToUnichar(glyphs[index]);
578 }
579}
580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581///////////////////////////////////////////////////////////////////////////////
582
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000583static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
584 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 SkASSERT(cache != NULL);
586 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000587
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
589}
590
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000591static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
592 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkASSERT(cache != NULL);
594 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
597}
598
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000599static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
600 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkASSERT(cache != NULL);
602 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
605}
606
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000607static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
608 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkASSERT(cache != NULL);
610 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
613}
614
reed@google.com68bc6f72012-03-14 19:41:55 +0000615static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
616 const char** text) {
617 SkASSERT(cache != NULL);
618 SkASSERT(text != NULL);
619
620 const int32_t* ptr = *(const int32_t**)text;
621 SkUnichar uni = *ptr++;
622 *text = (const char*)ptr;
623 return cache->getUnicharMetrics(uni);
624}
625
626static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
627 const char** text) {
628 SkASSERT(cache != NULL);
629 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000630
reed@google.com68bc6f72012-03-14 19:41:55 +0000631 const int32_t* ptr = *(const int32_t**)text;
632 SkUnichar uni = *--ptr;
633 *text = (const char*)ptr;
634 return cache->getUnicharMetrics(uni);
635}
636
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000637static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
638 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 SkASSERT(cache != NULL);
640 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 const uint16_t* ptr = *(const uint16_t**)text;
643 unsigned glyphID = *ptr;
644 ptr += 1;
645 *text = (const char*)ptr;
646 return cache->getGlyphIDMetrics(glyphID);
647}
648
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000649static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
650 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 SkASSERT(cache != NULL);
652 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 const uint16_t* ptr = *(const uint16_t**)text;
655 ptr -= 1;
656 unsigned glyphID = *ptr;
657 *text = (const char*)ptr;
658 return cache->getGlyphIDMetrics(glyphID);
659}
660
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000661static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
662 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 SkASSERT(cache != NULL);
664 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
667}
668
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000669static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
670 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkASSERT(cache != NULL);
672 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
675}
676
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000677static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
678 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 SkASSERT(cache != NULL);
680 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000681
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
683}
684
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000685static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
686 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 SkASSERT(cache != NULL);
688 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000689
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
691}
692
reed@google.com68bc6f72012-03-14 19:41:55 +0000693static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
694 const char** text) {
695 SkASSERT(cache != NULL);
696 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000697
reed@google.com68bc6f72012-03-14 19:41:55 +0000698 const int32_t* ptr = *(const int32_t**)text;
699 SkUnichar uni = *ptr++;
700 *text = (const char*)ptr;
701 return cache->getUnicharAdvance(uni);
702}
703
704static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
705 const char** text) {
706 SkASSERT(cache != NULL);
707 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000708
reed@google.com68bc6f72012-03-14 19:41:55 +0000709 const int32_t* ptr = *(const int32_t**)text;
710 SkUnichar uni = *--ptr;
711 *text = (const char*)ptr;
712 return cache->getUnicharAdvance(uni);
713}
714
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000715static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
716 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 SkASSERT(cache != NULL);
718 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 const uint16_t* ptr = *(const uint16_t**)text;
721 unsigned glyphID = *ptr;
722 ptr += 1;
723 *text = (const char*)ptr;
724 return cache->getGlyphIDAdvance(glyphID);
725}
726
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000727static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
728 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 SkASSERT(cache != NULL);
730 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 const uint16_t* ptr = *(const uint16_t**)text;
733 ptr -= 1;
734 unsigned glyphID = *ptr;
735 *text = (const char*)ptr;
736 return cache->getGlyphIDAdvance(glyphID);
737}
738
739SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000740 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
742 sk_getMetrics_utf8_next,
743 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000744 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747 sk_getMetrics_utf8_prev,
748 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000749 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000751
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 sk_getAdvance_utf8_next,
753 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000754 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 sk_getAdvance_utf8_prev,
758 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000759 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 sk_getAdvance_glyph_prev
761 };
reed@google.com72cf4922011-01-04 19:58:20 +0000762
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763 unsigned index = this->getTextEncoding();
764
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000765 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000766 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000767 }
768 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000769 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000770 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771
772 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
773 return gMeasureCacheProcs[index];
774}
775
776///////////////////////////////////////////////////////////////////////////////
777
778static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000779 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 SkASSERT(cache != NULL);
781 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
784}
785
786static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000787 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 SkASSERT(cache != NULL);
789 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
792}
793
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000794static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
795 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 SkASSERT(cache != NULL);
797 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
800}
801
802static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000803 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 SkASSERT(cache != NULL);
805 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000806
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
808 x, y);
809}
810
reed@google.com68bc6f72012-03-14 19:41:55 +0000811static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
812 const char** text, SkFixed, SkFixed) {
813 SkASSERT(cache != NULL);
814 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000815
reed@google.com68bc6f72012-03-14 19:41:55 +0000816 const int32_t* ptr = *(const int32_t**)text;
817 SkUnichar uni = *ptr++;
818 *text = (const char*)ptr;
819 return cache->getUnicharMetrics(uni);
820}
821
822static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
823 const char** text, SkFixed x, SkFixed y) {
824 SkASSERT(cache != NULL);
825 SkASSERT(text != NULL);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000826
reed@google.com68bc6f72012-03-14 19:41:55 +0000827 const int32_t* ptr = *(const int32_t**)text;
828 SkUnichar uni = *--ptr;
829 *text = (const char*)ptr;
830 return cache->getUnicharMetrics(uni);
831}
832
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000833static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
834 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 SkASSERT(cache != NULL);
836 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000837
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 const uint16_t* ptr = *(const uint16_t**)text;
839 unsigned glyphID = *ptr;
840 ptr += 1;
841 *text = (const char*)ptr;
842 return cache->getGlyphIDMetrics(glyphID);
843}
844
845static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000846 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 SkASSERT(cache != NULL);
848 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 const uint16_t* ptr = *(const uint16_t**)text;
851 unsigned glyphID = *ptr;
852 ptr += 1;
853 *text = (const char*)ptr;
854 return cache->getGlyphIDMetrics(glyphID, x, y);
855}
856
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000857SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 static const SkDrawCacheProc gDrawCacheProcs[] = {
859 sk_getMetrics_utf8_00,
860 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000861 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000863
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 sk_getMetrics_utf8_xy,
865 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000866 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 sk_getMetrics_glyph_xy
868 };
reed@google.com72cf4922011-01-04 19:58:20 +0000869
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000871 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000872 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000873 }
reed@google.com72cf4922011-01-04 19:58:20 +0000874
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
876 return gDrawCacheProcs[index];
877}
878
879///////////////////////////////////////////////////////////////////////////////
880
881class SkAutoRestorePaintTextSizeAndFrame {
882public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000883 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
884 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 fTextSize = paint->getTextSize();
886 fStyle = paint->getStyle();
887 fPaint->setStyle(SkPaint::kFill_Style);
888 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000889
890 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 fPaint->setStyle(fStyle);
892 fPaint->setTextSize(fTextSize);
893 }
reed@google.com72cf4922011-01-04 19:58:20 +0000894
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895private:
896 SkPaint* fPaint;
897 SkScalar fTextSize;
898 SkPaint::Style fStyle;
899};
900
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000901static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 bounds->set(SkIntToScalar(g.fLeft),
903 SkIntToScalar(g.fTop),
904 SkIntToScalar(g.fLeft + g.fWidth),
905 SkIntToScalar(g.fTop + g.fHeight));
906}
907
reed@android.come88f5512010-03-19 14:42:28 +0000908// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
909// we don't overflow along the way
910typedef int64_t Sk48Dot16;
911
912#ifdef SK_SCALAR_IS_FLOAT
913 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000914 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000915 }
916#else
917 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
918 // just return the low 32bits
919 return static_cast<SkFixed>(x);
920 }
921#endif
922
reed@google.com44da42e2011-11-10 20:04:47 +0000923static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000924 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 bounds->join(SkIntToScalar(g.fLeft) + sx,
926 SkIntToScalar(g.fTop),
927 SkIntToScalar(g.fLeft + g.fWidth) + sx,
928 SkIntToScalar(g.fTop + g.fHeight));
929}
930
reed@google.com44da42e2011-11-10 20:04:47 +0000931static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
932 SkScalar sy = Sk48Dot16ToScalar(dy);
933 bounds->join(SkIntToScalar(g.fLeft),
934 SkIntToScalar(g.fTop) + sy,
935 SkIntToScalar(g.fLeft + g.fWidth),
936 SkIntToScalar(g.fTop + g.fHeight) + sy);
937}
938
939typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
940
941// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
942static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
943 SkASSERT(0 == xyIndex || 1 == xyIndex);
944 return (&glyph.fAdvanceX)[xyIndex];
945}
946
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947SkScalar SkPaint::measure_text(SkGlyphCache* cache,
948 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000949 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000951 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000953 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000955 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 return 0;
957 }
958
959 SkMeasureCacheProc glyphCacheProc;
960 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
961 NULL != bounds);
962
reed@google.com44da42e2011-11-10 20:04:47 +0000963 int xyIndex;
964 JoinBoundsProc joinBoundsProc;
965 if (this->isVerticalText()) {
966 xyIndex = 1;
967 joinBoundsProc = join_bounds_y;
968 } else {
969 xyIndex = 0;
970 joinBoundsProc = join_bounds_x;
971 }
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 int n = 1;
974 const char* stop = (const char*)text + byteLength;
975 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000976 // our accumulated fixed-point advances might overflow 16.16, so we use
977 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
978 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000979 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980
981 SkAutoKern autokern;
982
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000983 if (NULL == bounds) {
984 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 int rsb;
986 for (; text < stop; n++) {
987 rsb = g->fRsbDelta;
988 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000989 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000991 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000993 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 }
995 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000996 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000998 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 int rsb;
1000 for (; text < stop; n++) {
1001 rsb = g->fRsbDelta;
1002 g = &glyphCacheProc(cache, &text);
1003 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001004 joinBoundsProc(*g, bounds, x);
1005 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001007 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 for (; text < stop; n++) {
1009 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001010 joinBoundsProc(*g, bounds, x);
1011 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
1013 }
1014 }
1015 SkASSERT(text == stop);
1016
1017 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001018 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019}
1020
1021SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001022 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 const char* text = (const char*)textData;
1024 SkASSERT(text != NULL || length == 0);
1025
1026 SkScalar scale = 0;
1027 SkAutoRestorePaintTextSizeAndFrame restore(this);
1028
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001029 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 scale = fTextSize / kCanonicalTextSizeForPaths;
1031 // this gets restored by restore
1032 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1033 }
reed@google.com72cf4922011-01-04 19:58:20 +00001034
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001035 SkMatrix zoomMatrix, *zoomPtr = NULL;
1036 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 zoomMatrix.setScale(zoom, zoom);
1038 zoomPtr = &zoomMatrix;
1039 }
1040
1041 SkAutoGlyphCache autoCache(*this, zoomPtr);
1042 SkGlyphCache* cache = autoCache.getCache();
1043
1044 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001045
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001046 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 int tempCount;
1048
1049 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001050 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001052 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1054 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1055 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1056 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1057 }
1058 }
1059 }
1060 return width;
1061}
1062
1063typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1064
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001065static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 return text < stop;
1067}
1068
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001069static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 return text > stop;
1071}
1072
1073static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001074 const char** text, size_t length,
1075 const char** stop) {
1076 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 *stop = *text + length;
1078 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001079 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 // text should point to the end of the buffer, and stop to the beginning
1081 *stop = *text;
1082 *text += length;
1083 return backward_textBufferPred;
1084 }
1085}
1086
1087size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1088 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001089 TextBufferDirection tbd) const {
1090 if (0 == length || 0 >= maxWidth) {
1091 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001093 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 return 0;
1095 }
1096
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001097 if (0 == fTextSize) {
1098 if (measuredWidth) {
1099 *measuredWidth = 0;
1100 }
1101 return length;
1102 }
1103
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 SkASSERT(textD != NULL);
1105 const char* text = (const char*)textD;
1106
1107 SkScalar scale = 0;
1108 SkAutoRestorePaintTextSizeAndFrame restore(this);
1109
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001110 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +00001112 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 // this gets restored by restore
1114 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1115 }
reed@google.com72cf4922011-01-04 19:58:20 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 SkAutoGlyphCache autoCache(*this, NULL);
1118 SkGlyphCache* cache = autoCache.getCache();
1119
1120 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
1121 const char* stop;
1122 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.com44da42e2011-11-10 20:04:47 +00001123 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001124 // use 64bits for our accumulator, to avoid overflowing 16.16
1125 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1126 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127
1128 SkAutoKern autokern;
1129
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001130 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001132 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 const char* curr = text;
1134 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001135 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001136 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 width -= x;
1138 text = curr;
1139 break;
1140 }
1141 rsb = g.fRsbDelta;
1142 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001143 } else {
1144 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001146 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001147 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 width -= x;
1149 text = curr;
1150 break;
1151 }
1152 }
1153 }
reed@google.com72cf4922011-01-04 19:58:20 +00001154
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001155 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001156 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001157 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001159 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 *measuredWidth = scalarWidth;
1161 }
reed@google.com72cf4922011-01-04 19:58:20 +00001162
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 // return the number of bytes measured
1164 return (kForward_TextBufferDirection == tbd) ?
1165 text - stop + length : stop - text + length;
1166}
1167
1168///////////////////////////////////////////////////////////////////////////////
1169
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001170static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
1172 return false; // don't detach the cache
1173}
1174
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001175static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
1177}
1178
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001179SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 SkScalar scale = 0;
1181 SkAutoRestorePaintTextSizeAndFrame restore(this);
1182
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001183 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 scale = fTextSize / kCanonicalTextSizeForPaths;
1185 // this gets restored by restore
1186 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1187 }
reed@google.com72cf4922011-01-04 19:58:20 +00001188
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001189 SkMatrix zoomMatrix, *zoomPtr = NULL;
1190 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 zoomMatrix.setScale(zoom, zoom);
1192 zoomPtr = &zoomMatrix;
1193 }
1194
1195#if 0
1196 SkAutoGlyphCache autoCache(*this, zoomPtr);
1197 SkGlyphCache* cache = autoCache.getCache();
1198 const FontMetrics& my = cache->getFontMetricsY();
1199#endif
1200 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001201 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001203 }
reed@google.com72cf4922011-01-04 19:58:20 +00001204
reed@google.comffe49f52011-11-22 19:42:41 +00001205 this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001207 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1209 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1210 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1211 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1212 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
1213 }
1214 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1215}
1216
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001217///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001219static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 bounds->set(g.fLeft * scale,
1221 g.fTop * scale,
1222 (g.fLeft + g.fWidth) * scale,
1223 (g.fTop + g.fHeight) * scale);
1224}
1225
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001226int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1227 SkScalar widths[], SkRect bounds[]) const {
1228 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001230 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231
1232 SkASSERT(NULL != textData);
1233
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001234 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001236 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237
1238 SkAutoRestorePaintTextSizeAndFrame restore(this);
1239 SkScalar scale = 0;
1240
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001241 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 scale = fTextSize / kCanonicalTextSizeForPaths;
1243 // this gets restored by restore
1244 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1245 }
1246
1247 SkAutoGlyphCache autoCache(*this, NULL);
1248 SkGlyphCache* cache = autoCache.getCache();
1249 SkMeasureCacheProc glyphCacheProc;
1250 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1251 NULL != bounds);
1252
1253 const char* text = (const char*)textData;
1254 const char* stop = text + byteLength;
1255 int count = 0;
reed@google.com44da42e2011-11-10 20:04:47 +00001256 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001258 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259 // we adjust the widths returned here through auto-kerning
1260 SkAutoKern autokern;
1261 SkFixed prevWidth = 0;
1262
1263 if (scale) {
1264 while (text < stop) {
1265 const SkGlyph& g = glyphCacheProc(cache, &text);
1266 if (widths) {
1267 SkFixed adjust = autokern.adjust(g);
1268
1269 if (count > 0) {
1270 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1271 *widths++ = SkScalarMul(w, scale);
1272 }
reed@google.com44da42e2011-11-10 20:04:47 +00001273 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 }
1275 if (bounds) {
1276 set_bounds(g, bounds++, scale);
1277 }
1278 ++count;
1279 }
1280 if (count > 0 && widths) {
1281 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1282 }
1283 } else {
1284 while (text < stop) {
1285 const SkGlyph& g = glyphCacheProc(cache, &text);
1286 if (widths) {
1287 SkFixed adjust = autokern.adjust(g);
1288
1289 if (count > 0) {
1290 *widths++ = SkFixedToScalar(prevWidth + adjust);
1291 }
reed@google.com44da42e2011-11-10 20:04:47 +00001292 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 }
1294 if (bounds) {
1295 set_bounds(g, bounds++);
1296 }
1297 ++count;
1298 }
1299 if (count > 0 && widths) {
1300 *widths = SkFixedToScalar(prevWidth);
1301 }
1302 }
1303 } else { // no devkern
1304 if (scale) {
1305 while (text < stop) {
1306 const SkGlyph& g = glyphCacheProc(cache, &text);
1307 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001308 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 scale);
1310 }
1311 if (bounds) {
1312 set_bounds(g, bounds++, scale);
1313 }
1314 ++count;
1315 }
1316 } else {
1317 while (text < stop) {
1318 const SkGlyph& g = glyphCacheProc(cache, &text);
1319 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001320 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 }
1322 if (bounds) {
1323 set_bounds(g, bounds++);
1324 }
1325 ++count;
1326 }
1327 }
1328 }
1329
1330 SkASSERT(text == stop);
1331 return count;
1332}
1333
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001334///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335
1336#include "SkDraw.h"
1337
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001338void SkPaint::getTextPath(const void* textData, size_t length,
1339 SkScalar x, SkScalar y, SkPath* path) const {
1340 SkASSERT(length == 0 || textData != NULL);
1341
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001343 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001345 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346
djsollen@google.com166e6532012-03-20 14:24:38 +00001347 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 SkMatrix matrix;
1349 SkScalar prevXPos = 0;
1350
1351 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1352 matrix.postTranslate(x, y);
1353 path->reset();
1354
1355 SkScalar xpos;
1356 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001357 while (iter.next(&iterPath, &xpos)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 matrix.postTranslate(xpos - prevXPos, 0);
reed@google.com7b4531f2012-08-07 15:53:00 +00001359 if (iterPath) {
1360 path->addPath(*iterPath, matrix);
1361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 prevXPos = xpos;
1363 }
1364}
1365
reed@google.comca0062e2012-07-20 11:20:32 +00001366void SkPaint::getPosTextPath(const void* textData, size_t length,
1367 const SkPoint pos[], SkPath* path) const {
1368 SkASSERT(length == 0 || textData != NULL);
1369
1370 const char* text = (const char*)textData;
1371 if (text == NULL || length == 0 || path == NULL) {
1372 return;
1373 }
1374
1375 SkTextToPathIter iter(text, length, *this, false);
1376 SkMatrix matrix;
1377 SkPoint prevPos;
1378 prevPos.set(0, 0);
1379
1380 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1381 path->reset();
1382
1383 unsigned int i = 0;
1384 const SkPath* iterPath;
reed@google.com7b4531f2012-08-07 15:53:00 +00001385 while (iter.next(&iterPath, NULL)) {
reed@google.comca0062e2012-07-20 11:20:32 +00001386 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
reed@google.com7b4531f2012-08-07 15:53:00 +00001387 if (iterPath) {
1388 path->addPath(*iterPath, matrix);
1389 }
reed@google.comca0062e2012-07-20 11:20:32 +00001390 prevPos = pos[i];
1391 i++;
1392 }
1393}
1394
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395static void add_flattenable(SkDescriptor* desc, uint32_t tag,
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001396 SkOrderedWriteBuffer* buffer) {
1397 buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398}
1399
reed@google.com2739b272011-09-28 17:26:42 +00001400// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001401static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001404 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001405 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001406 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001407 }
1408
reed@google.com65dd8f82011-03-14 13:31:16 +00001409 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001410 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001411 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001412
1413 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414}
1415
reed@android.com1cdcb512009-08-24 19:11:00 +00001416// if linear-text is on, then we force hinting to be off (since that's sort of
1417// the point of linear-text.
1418static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1419 SkPaint::Hinting h = paint.getHinting();
1420 if (paint.isLinearText()) {
1421 h = SkPaint::kNo_Hinting;
1422 }
1423 return h;
1424}
1425
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001426// return true if the paint is just a single color (i.e. not a shader). If its
1427// a shader, then we can't compute a const luminance for it :(
1428static bool justAColor(const SkPaint& paint, SkColor* color) {
1429 if (paint.getShader()) {
1430 return false;
1431 }
1432 SkColor c = paint.getColor();
1433 if (paint.getColorFilter()) {
1434 c = paint.getColorFilter()->filterColor(c);
1435 }
1436 if (color) {
1437 *color = c;
1438 }
1439 return true;
1440}
1441
reed@google.comce6dbb62012-02-10 22:01:45 +00001442static SkColor computeLuminanceColor(const SkPaint& paint) {
1443 SkColor c;
1444 if (!justAColor(paint, &c)) {
1445 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1446 }
1447 return c;
1448}
reed@google.com813d38b2012-02-13 21:37:57 +00001449
reed@google.comdd43df92012-02-15 14:50:29 +00001450#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1451
reed@google.com4f79b9b2011-09-13 13:23:26 +00001452// Beyond this size, LCD doesn't appreciably improve quality, but it always
1453// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001454#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1455 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1456#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001457
1458static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1459 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1460 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1461 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001462 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001463}
1464
reed@google.com72cf4922011-01-04 19:58:20 +00001465/*
1466 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1467 * that vary only slightly when we create our key into the font cache, since the font scaler
1468 * typically returns the same looking resuts for tiny changes in the matrix.
1469 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001470static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001471#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001472 int n = sk_float_round2int(x * 1024);
1473 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001474#else
1475 // round to the nearest 10 fractional bits
1476 return (x + (1 << 5)) & ~(1024 - 1);
1477#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001478}
1479
bungeman@google.com97efada2012-07-30 20:40:50 +00001480//#define SK_GAMMA_SRGB
1481#ifndef SK_GAMMA_CONTRAST
1482 /**
1483 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1484 * With lower values small text appears washed out (though correctly so).
1485 * With higher values lcd fringing is worse and the smoothing effect of
1486 * partial coverage is diminished.
1487 */
1488 #define SK_GAMMA_CONTRAST (0.5f)
1489#endif
1490#ifndef SK_GAMMA_EXPONENT
1491 #define SK_GAMMA_EXPONENT (2.2f)
1492#endif
1493
reed@android.com36a4c2a2009-07-22 19:52:11 +00001494void SkScalerContext::MakeRec(const SkPaint& paint,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001495 const SkMatrix* deviceMatrix, Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001496 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed@google.com7d26c592011-06-13 13:01:10 +00001498 rec->fOrigFontID = SkTypeface::UniqueID(paint.getTypeface());
1499 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 rec->fTextSize = paint.getTextSize();
1501 rec->fPreScaleX = paint.getTextScaleX();
1502 rec->fPreSkewX = paint.getTextSkewX();
1503
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001504 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001505 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1506 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1507 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1508 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001509 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1511 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1512 }
reed@google.com72cf4922011-01-04 19:58:20 +00001513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 SkPaint::Style style = paint.getStyle();
1515 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001516
reed@google.comffe49f52011-11-22 19:42:41 +00001517 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001518
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001519 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001520#ifdef SK_USE_FREETYPE_EMBOLDEN
1521 flags |= SkScalerContext::kEmbolden_Flag;
1522#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001523 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1524 kStdFakeBoldInterpKeys,
1525 kStdFakeBoldInterpValues,
1526 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001528
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001529 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 style = SkPaint::kStrokeAndFill_Style;
1531 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001532 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001534 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001535#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536 }
1537
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001538 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001540 }
reed@google.com72cf4922011-01-04 19:58:20 +00001541
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001542 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543 rec->fFrameWidth = strokeWidth;
1544 rec->fMiterLimit = paint.getStrokeMiter();
1545 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1546
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001547 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001549 }
1550 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551 rec->fFrameWidth = 0;
1552 rec->fMiterLimit = 0;
1553 rec->fStrokeJoin = 0;
1554 }
1555
reed@google.com02b53312011-05-18 19:00:53 +00001556 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1557
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001558 if (SkMask::kLCD16_Format == rec->fMaskFormat ||
1559 SkMask::kLCD32_Format == rec->fMaskFormat)
1560 {
reed@google.com02b53312011-05-18 19:00:53 +00001561 SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
1562 SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
reed@google.com4f79b9b2011-09-13 13:23:26 +00001563 if (SkFontHost::kNONE_LCDOrder == order || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001564 // eeek, can't support LCD
1565 rec->fMaskFormat = SkMask::kA8_Format;
1566 } else {
1567 if (SkFontHost::kVertical_LCDOrientation == orient) {
1568 flags |= SkScalerContext::kLCD_Vertical_Flag;
1569 }
1570 if (SkFontHost::kBGR_LCDOrder == order) {
1571 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1572 }
1573 }
1574 }
1575
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001576 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001577 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001578 }
1579 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001580 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001581 }
1582 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001583 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001584 }
reed@google.com830a23e2011-11-10 15:20:49 +00001585 if (paint.isVerticalText()) {
1586 flags |= SkScalerContext::kVertical_Flag;
1587 }
reed@google.com8351aab2012-01-18 17:06:35 +00001588 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1589 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1590 }
reed@google.com02b53312011-05-18 19:00:53 +00001591 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001592
reed@google.comffe49f52011-11-22 19:42:41 +00001593 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001594 rec->setHinting(computeHinting(paint));
1595
bungeman@google.com97efada2012-07-30 20:40:50 +00001596 rec->setLuminanceColor(computeLuminanceColor(paint));
1597#ifdef SK_GAMMA_SRGB
1598 rec->setDeviceGamma(0);
1599 rec->setPaintGamma(0);
1600#else
1601 rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
1602 rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
1603#endif
1604 rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST));
bungeman@google.com2bf82d82012-08-02 10:06:19 +00001605 rec->fReservedAlign = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001606
reed@android.com36a4c2a2009-07-22 19:52:11 +00001607 /* Allow the fonthost to modify our rec before we use it as a key into the
1608 cache. This way if we're asking for something that they will ignore,
1609 they can modify our rec up front, so we don't create duplicate cache
1610 entries.
1611 */
1612 SkFontHost::FilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001613
reed@google.com10d2d4d2012-03-01 22:32:51 +00001614 // be sure to call PostMakeRec(rec) before you actually use it!
1615}
1616
1617/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001618 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1619 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1620 * to hold it until the returned pointer is refed or forgotten.
1621 */
1622SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1623
bungeman@google.comae30f562012-09-11 18:44:55 +00001624static SkColorSpaceLuminance* gLinearLuminance = NULL;
1625
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001626static SkColorSpaceLuminance* gDeviceLuminance = NULL;
1627static SkScalar gDeviceGammaExponent = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001628/**
1629 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001630 * the returned SkColorSpaceLuminance pointer is forgotten.
bungeman@google.com97efada2012-07-30 20:40:50 +00001631 */
1632static SkColorSpaceLuminance* cachedDeviceLuminance(SkScalar gammaExponent) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001633 if (SK_Scalar1 == gammaExponent) {
1634 if (NULL == gLinearLuminance) {
1635 gLinearLuminance = SkNEW(SkLinearLuminance);
1636 }
1637 return gLinearLuminance;
1638 }
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001639 if (gDeviceGammaExponent != gammaExponent) {
1640 SkDELETE(gDeviceLuminance);
bungeman@google.com97efada2012-07-30 20:40:50 +00001641 if (0 == gammaExponent) {
1642 gDeviceLuminance = SkNEW(SkSRGBLuminance);
1643 } else {
1644 gDeviceLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
1645 }
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001646 gDeviceGammaExponent = gammaExponent;
bungeman@google.com97efada2012-07-30 20:40:50 +00001647 }
1648 return gDeviceLuminance;
1649}
1650
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001651static SkColorSpaceLuminance* gPaintLuminance = NULL;
1652static SkScalar gPaintGammaExponent = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001653/**
1654 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001655 * the returned SkColorSpaceLuminance pointer is forgotten.
bungeman@google.com97efada2012-07-30 20:40:50 +00001656 */
1657static SkColorSpaceLuminance* cachedPaintLuminance(SkScalar gammaExponent) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001658 if (SK_Scalar1 == gammaExponent) {
1659 if (NULL == gLinearLuminance) {
1660 gLinearLuminance = SkNEW(SkLinearLuminance);
1661 }
1662 return gLinearLuminance;
1663 }
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001664 if (gPaintGammaExponent != gammaExponent) {
1665 SkDELETE(gPaintLuminance);
bungeman@google.com97efada2012-07-30 20:40:50 +00001666 if (0 == gammaExponent) {
1667 gPaintLuminance = SkNEW(SkSRGBLuminance);
1668 } else {
1669 gPaintLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
1670 }
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001671 gPaintGammaExponent = gammaExponent;
bungeman@google.com97efada2012-07-30 20:40:50 +00001672 }
1673 return gPaintLuminance;
1674}
1675
bungeman@google.comae30f562012-09-11 18:44:55 +00001676static SkMaskGamma* gLinearMaskGamma = NULL;
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001677static SkMaskGamma* gMaskGamma = NULL;
1678static SkScalar gContrast = SK_ScalarMin;
1679static SkScalar gPaintGamma = SK_ScalarMin;
1680static SkScalar gDeviceGamma = SK_ScalarMin;
bungeman@google.com97efada2012-07-30 20:40:50 +00001681/**
1682 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1683 * the returned SkMaskGamma pointer is refed or forgotten.
1684 */
1685static SkMaskGamma* cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
bungeman@google.comae30f562012-09-11 18:44:55 +00001686 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
1687 if (NULL == gLinearMaskGamma) {
1688 gLinearMaskGamma = SkNEW(SkMaskGamma);
1689 }
1690 return gLinearMaskGamma;
1691 }
bungeman@google.com97efada2012-07-30 20:40:50 +00001692 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1693 SkSafeUnref(gMaskGamma);
1694 SkColorSpaceLuminance* paintLuminance = cachedPaintLuminance(paintGamma);
1695 SkColorSpaceLuminance* deviceLuminance = cachedDeviceLuminance(deviceGamma);
1696 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, *paintLuminance, *deviceLuminance));
1697 gContrast = contrast;
1698 gPaintGamma = paintGamma;
1699 gDeviceGamma = deviceGamma;
1700 }
1701 return gMaskGamma;
1702}
1703
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001704/*static*/ void SkPaint::Term() {
1705 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1706
bungeman@google.comae30f562012-09-11 18:44:55 +00001707 SkSafeUnref(gLinearMaskGamma);
1708
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001709 SkSafeUnref(gMaskGamma);
1710 SkDEBUGCODE(gContrast = SK_ScalarMin;)
1711 SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
1712 SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
1713
bungeman@google.comae30f562012-09-11 18:44:55 +00001714 SkDELETE(gLinearLuminance);
1715
bungeman@google.comb24b4fa2012-09-04 13:49:59 +00001716 SkDELETE(gPaintLuminance);
1717 SkDEBUGCODE(gPaintLuminance = NULL;)
1718 SkDEBUGCODE(gPaintGammaExponent = SK_ScalarMin;)
1719
1720 SkDELETE(gDeviceLuminance);
1721 SkDEBUGCODE(gDeviceLuminance = NULL;)
1722 SkDEBUGCODE(gDeviceGammaExponent = SK_ScalarMin;)
1723}
1724
bungeman@google.com97efada2012-07-30 20:40:50 +00001725/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001726 * We ensure that the rec is self-consistent and efficient (where possible)
1727 */
bungeman@google.com97efada2012-07-30 20:40:50 +00001728void SkScalerContext::PostMakeRec(const SkPaint& paint, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001729 /**
1730 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001731 * limits the number of unique entries, and the scaler will only look at
1732 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001733 */
reed@google.comdd43df92012-02-15 14:50:29 +00001734 switch (rec->fMaskFormat) {
1735 case SkMask::kLCD16_Format:
1736 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001737 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001738 SkColor color = rec->getLuminanceColor();
bungeman@google.comae30f562012-09-11 18:44:55 +00001739 rec->setLuminanceColor(SkMaskGamma::cannonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001740 break;
1741 }
1742 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001743 // filter down the luminance to a single component, since A8 can't
1744 // use per-component information
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001745
bungeman@google.com97efada2012-07-30 20:40:50 +00001746 SkColor color = rec->getLuminanceColor();
1747 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1748 U8CPU lum = cachedPaintLuminance(rec->getPaintGamma())->computeLuminance(color);
bungeman@google.comfd668cf2012-08-24 17:46:11 +00001749 //If we are asked to look like LCD, look like LCD.
1750 if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
1751 // HACK: Prevents green from being pre-blended as white.
1752 lum -= ((255 - lum) * lum) / 255;
1753 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001754
reed@google.comdd43df92012-02-15 14:50:29 +00001755 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001756 color = SkColorSetRGB(lum, lum, lum);
bungeman@google.comae30f562012-09-11 18:44:55 +00001757 rec->setLuminanceColor(SkMaskGamma::cannonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001758 break;
1759 }
1760 case SkMask::kBW_Format:
1761 // No need to differentiate gamma if we're BW
reed@google.comdd43df92012-02-15 14:50:29 +00001762 rec->setLuminanceColor(0);
reed@google.comdd43df92012-02-15 14:50:29 +00001763 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001764 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
1767#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1768
reed@google.com17fb3872011-05-04 14:31:07 +00001769#ifdef SK_DEBUG
1770 #define TEST_DESC
1771#endif
1772
reed@google.comffe49f52011-11-22 19:42:41 +00001773/*
1774 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1775 * by gamma correction, so we jam the luminance field to 0 (most common value
1776 * for black text) in hopes that we get a cache hit easier. A better solution
1777 * would be for the fontcache lookup to know to ignore the luminance field
1778 * entirely, but not sure how to do that and keep it fast.
1779 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
1781 void (*proc)(const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001782 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 SkScalerContext::Rec rec;
1784
1785 SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001786 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001787 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001788 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789
1790 size_t descSize = sizeof(rec);
1791 int entryCount = 1;
1792 SkPathEffect* pe = this->getPathEffect();
1793 SkMaskFilter* mf = this->getMaskFilter();
1794 SkRasterizer* ra = this->getRasterizer();
1795
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001796 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1797 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1798 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799
1800 if (pe) {
1801 peBuffer.writeFlattenable(pe);
1802 descSize += peBuffer.size();
1803 entryCount += 1;
1804 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1805 // seems like we could support kLCD as well at this point...
1806 }
1807 if (mf) {
1808 mfBuffer.writeFlattenable(mf);
1809 descSize += mfBuffer.size();
1810 entryCount += 1;
1811 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1812 }
1813 if (ra) {
1814 raBuffer.writeFlattenable(ra);
1815 descSize += raBuffer.size();
1816 entryCount += 1;
1817 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1818 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001819
1820 ///////////////////////////////////////////////////////////////////////////
1821 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001822 SkScalerContext::PostMakeRec(*this, &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001823
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 descSize += SkDescriptor::ComputeOverhead(entryCount);
1825
1826 SkAutoDescriptor ad(descSize);
1827 SkDescriptor* desc = ad.getDesc();
1828
1829 desc->init();
1830 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1831
1832 if (pe) {
1833 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1834 }
1835 if (mf) {
1836 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1837 }
1838 if (ra) {
1839 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1840 }
1841
1842 SkASSERT(descSize == desc->getLength());
1843 desc->computeChecksum();
1844
reed@google.com17fb3872011-05-04 14:31:07 +00001845#ifdef TEST_DESC
1846 {
1847 // Check that we completely write the bytes in desc (our key), and that
1848 // there are no uninitialized bytes. If there were, then we would get
1849 // false-misses (or worse, false-hits) in our fontcache.
1850 //
1851 // We do this buy filling 2 others, one with 0s and the other with 1s
1852 // and create those, and then check that all 3 are identical.
1853 SkAutoDescriptor ad1(descSize);
1854 SkAutoDescriptor ad2(descSize);
1855 SkDescriptor* desc1 = ad1.getDesc();
1856 SkDescriptor* desc2 = ad2.getDesc();
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001857
reed@google.com17fb3872011-05-04 14:31:07 +00001858 memset(desc1, 0x00, descSize);
1859 memset(desc2, 0xFF, descSize);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001860
reed@google.com17fb3872011-05-04 14:31:07 +00001861 desc1->init();
1862 desc2->init();
1863 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1864 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001865
reed@google.com17fb3872011-05-04 14:31:07 +00001866 if (pe) {
1867 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1868 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1869 }
1870 if (mf) {
1871 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1872 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1873 }
1874 if (ra) {
1875 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1876 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1877 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001878
reed@google.com17fb3872011-05-04 14:31:07 +00001879 SkASSERT(descSize == desc1->getLength());
1880 SkASSERT(descSize == desc2->getLength());
1881 desc1->computeChecksum();
1882 desc2->computeChecksum();
1883 SkASSERT(!memcmp(desc, desc1, descSize));
1884 SkASSERT(!memcmp(desc, desc2, descSize));
1885 }
1886#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001887
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 proc(desc, context);
1889}
1890
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001891SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892 SkGlyphCache* cache;
1893 this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
1894 return cache;
1895}
1896
bungeman@google.com97efada2012-07-30 20:40:50 +00001897/**
1898 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1899 */
1900//static
1901SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1902 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1903 SkMaskGamma* maskGamma = cachedMaskGamma(rec.getContrast(),
1904 rec.getPaintGamma(),
1905 rec.getDeviceGamma());
1906 return maskGamma->preBlend(rec.getLuminanceColor());
1907}
1908
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909///////////////////////////////////////////////////////////////////////////////
1910
1911#include "SkStream.h"
1912
reed@android.comaefd2bc2009-03-30 21:02:14 +00001913static uintptr_t asint(const void* p) {
1914 return reinterpret_cast<uintptr_t>(p);
1915}
1916
1917union Scalar32 {
1918 SkScalar fScalar;
1919 uint32_t f32;
1920};
1921
1922static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1923 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1924 Scalar32 tmp;
1925 tmp.fScalar = value;
1926 *ptr = tmp.f32;
1927 return ptr + 1;
1928}
1929
1930static SkScalar read_scalar(const uint32_t*& ptr) {
1931 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1932 Scalar32 tmp;
1933 tmp.f32 = *ptr++;
1934 return tmp.fScalar;
1935}
1936
1937static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1938 SkASSERT(a == (uint8_t)a);
1939 SkASSERT(b == (uint8_t)b);
1940 SkASSERT(c == (uint8_t)c);
1941 SkASSERT(d == (uint8_t)d);
1942 return (a << 24) | (b << 16) | (c << 8) | d;
1943}
1944
1945enum FlatFlags {
1946 kHasTypeface_FlatFlag = 0x01,
reed@google.comb0a34d82012-07-11 19:57:55 +00001947 kHasEffects_FlatFlag = 0x02,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001948};
1949
1950// The size of a flat paint's POD fields
1951static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
1952 1 * sizeof(SkColor) +
1953 1 * sizeof(uint16_t) +
1954 6 * sizeof(uint8_t);
1955
1956/* To save space/time, we analyze the paint, and write a truncated version of
1957 it if there are not tricky elements like shaders, etc.
1958 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001960 uint8_t flatFlags = 0;
1961 if (this->getTypeface()) {
1962 flatFlags |= kHasTypeface_FlatFlag;
1963 }
1964 if (asint(this->getPathEffect()) |
1965 asint(this->getShader()) |
1966 asint(this->getXfermode()) |
1967 asint(this->getMaskFilter()) |
1968 asint(this->getColorFilter()) |
1969 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001970 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001971 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001972 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001973 flatFlags |= kHasEffects_FlatFlag;
1974 }
reed@google.com72cf4922011-01-04 19:58:20 +00001975
reed@android.comaefd2bc2009-03-30 21:02:14 +00001976
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001977 if (buffer.isOrderedBinaryBuffer()) {
1978 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1979 uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
1980
1981 ptr = write_scalar(ptr, this->getTextSize());
1982 ptr = write_scalar(ptr, this->getTextScaleX());
1983 ptr = write_scalar(ptr, this->getTextSkewX());
1984 ptr = write_scalar(ptr, this->getStrokeWidth());
1985 ptr = write_scalar(ptr, this->getStrokeMiter());
1986 *ptr++ = this->getColor();
1987 // previously flags:16, textAlign:8, flatFlags:8
1988 // now flags:16, hinting:4, textAlign:4, flatFlags:8
1989 *ptr++ = (this->getFlags() << 16) |
1990 // hinting added later. 0 in this nibble means use the default.
1991 ((this->getHinting()+1) << 12) |
1992 (this->getTextAlign() << 8) |
1993 flatFlags;
1994 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1995 this->getStyle(), this->getTextEncoding());
1996 } else {
1997 buffer.writeScalar(fTextSize);
1998 buffer.writeScalar(fTextScaleX);
1999 buffer.writeScalar(fTextSkewX);
2000 buffer.writeScalar(fWidth);
2001 buffer.writeScalar(fMiterLimit);
2002 buffer.writeColor(fColor);
2003 buffer.writeUInt(fFlags);
2004 buffer.writeUInt(fHinting);
2005 buffer.writeUInt(fTextAlign);
2006 buffer.writeUInt(flatFlags);
2007
2008 buffer.writeUInt(fCapType);
2009 buffer.writeUInt(fJoinType);
2010 buffer.writeUInt(fStyle);
2011 buffer.writeUInt(fTextEncoding);
2012 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002013
2014 // now we're done with ptr and the (pre)reserved space. If we need to write
2015 // additional fields, use the buffer directly
2016 if (flatFlags & kHasTypeface_FlatFlag) {
2017 buffer.writeTypeface(this->getTypeface());
2018 }
2019 if (flatFlags & kHasEffects_FlatFlag) {
2020 buffer.writeFlattenable(this->getPathEffect());
2021 buffer.writeFlattenable(this->getShader());
2022 buffer.writeFlattenable(this->getXfermode());
2023 buffer.writeFlattenable(this->getMaskFilter());
2024 buffer.writeFlattenable(this->getColorFilter());
2025 buffer.writeFlattenable(this->getRasterizer());
2026 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002027 buffer.writeFlattenable(this->getImageFilter());
reed@google.comb0a34d82012-07-11 19:57:55 +00002028 buffer.writeFlattenable(this->getAnnotation());
reed@android.comaefd2bc2009-03-30 21:02:14 +00002029 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030}
2031
2032void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@google.comb0a34d82012-07-11 19:57:55 +00002033 fPrivFlags = 0;
2034
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002035 uint8_t flatFlags = 0;
2036 if (buffer.isOrderedBinaryBuffer()) {
2037 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
2038 const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
2039 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002040
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002041 // the order we read must match the order we wrote in flatten()
2042 this->setTextSize(read_scalar(pod));
2043 this->setTextScaleX(read_scalar(pod));
2044 this->setTextSkewX(read_scalar(pod));
2045 this->setStrokeWidth(read_scalar(pod));
2046 this->setStrokeMiter(read_scalar(pod));
2047 this->setColor(*pod++);
bungeman@google.com24babf42011-11-07 16:33:40 +00002048
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002049 // previously flags:16, textAlign:8, flatFlags:8
2050 // now flags:16, hinting:4, textAlign:4, flatFlags:8
2051 uint32_t tmp = *pod++;
2052 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00002053
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002054 // hinting added later. 0 in this nibble means use the default.
2055 uint32_t hinting = (tmp >> 12) & 0xF;
2056 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
bungeman@google.com24babf42011-11-07 16:33:40 +00002057
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002058 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002059
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002060 flatFlags = tmp & 0xFF;
2061
2062 tmp = *pod++;
2063 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
2064 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
2065 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
2066 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
2067 } else {
2068 this->setTextSize(buffer.readScalar());
2069 this->setTextScaleX(buffer.readScalar());
2070 this->setTextSkewX(buffer.readScalar());
2071 this->setStrokeWidth(buffer.readScalar());
2072 this->setStrokeMiter(buffer.readScalar());
2073 this->setColor(buffer.readColor());
2074 this->setFlags(buffer.readUInt());
2075 this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
2076 this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
2077 flatFlags = buffer.readUInt();
2078
2079 this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
2080 this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
2081 this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
2082 this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
2083 }
reed@android.comaefd2bc2009-03-30 21:02:14 +00002084
2085 if (flatFlags & kHasTypeface_FlatFlag) {
2086 this->setTypeface(buffer.readTypeface());
2087 } else {
2088 this->setTypeface(NULL);
2089 }
2090
2091 if (flatFlags & kHasEffects_FlatFlag) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00002092 SkSafeUnref(this->setPathEffect(buffer.readFlattenableT<SkPathEffect>()));
2093 SkSafeUnref(this->setShader(buffer.readFlattenableT<SkShader>()));
2094 SkSafeUnref(this->setXfermode(buffer.readFlattenableT<SkXfermode>()));
2095 SkSafeUnref(this->setMaskFilter(buffer.readFlattenableT<SkMaskFilter>()));
2096 SkSafeUnref(this->setColorFilter(buffer.readFlattenableT<SkColorFilter>()));
2097 SkSafeUnref(this->setRasterizer(buffer.readFlattenableT<SkRasterizer>()));
2098 SkSafeUnref(this->setLooper(buffer.readFlattenableT<SkDrawLooper>()));
2099 SkSafeUnref(this->setImageFilter(buffer.readFlattenableT<SkImageFilter>()));
2100 SkSafeUnref(this->setAnnotation(buffer.readFlattenableT<SkAnnotation>()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002101 } else {
2102 this->setPathEffect(NULL);
2103 this->setShader(NULL);
2104 this->setXfermode(NULL);
2105 this->setMaskFilter(NULL);
2106 this->setColorFilter(NULL);
2107 this->setRasterizer(NULL);
2108 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002109 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111}
2112
2113///////////////////////////////////////////////////////////////////////////////
2114
reed@google.com82065d62011-02-07 15:30:46 +00002115SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002116 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 SkRefCnt_SafeAssign(fShader, shader);
2118 return shader;
2119}
2120
reed@google.com82065d62011-02-07 15:30:46 +00002121SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002122 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 SkRefCnt_SafeAssign(fColorFilter, filter);
2124 return filter;
2125}
2126
reed@google.com82065d62011-02-07 15:30:46 +00002127SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002128 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002129 SkRefCnt_SafeAssign(fXfermode, mode);
2130 return mode;
2131}
2132
reed@android.com0baf1932009-06-24 12:41:42 +00002133SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002134 SkSafeUnref(fXfermode);
2135 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002136 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002137 return fXfermode;
2138}
2139
reed@google.com82065d62011-02-07 15:30:46 +00002140SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002141 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 SkRefCnt_SafeAssign(fPathEffect, effect);
2143 return effect;
2144}
2145
reed@google.com82065d62011-02-07 15:30:46 +00002146SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002147 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 SkRefCnt_SafeAssign(fMaskFilter, filter);
2149 return filter;
2150}
2151
reed@google.com82065d62011-02-07 15:30:46 +00002152///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002154bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002155 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156
reed@google.comfd4be262012-05-25 01:04:12 +00002157 const SkPath* srcPtr = &src;
2158 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002159
reed@google.comfd4be262012-05-25 01:04:12 +00002160 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec)) {
2161 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
2163
reed@google.comfd4be262012-05-25 01:04:12 +00002164 if (!rec.applyToPath(dst, *srcPtr)) {
2165 if (srcPtr == &tmpPath) {
2166 // If path's were copy-on-write, this trick would not be needed.
2167 // As it is, we want to save making a deep-copy from tmpPath -> dst
2168 // since we know we're just going to delete tmpPath when we return,
2169 // so the swap saves that copy.
2170 dst->swap(tmpPath);
2171 } else {
2172 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 }
2174 }
reed@google.comfd4be262012-05-25 01:04:12 +00002175 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176}
2177
reed@google.come4f10a72012-05-15 20:47:50 +00002178const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002179 SkRect* storage,
2180 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002182
reed@google.come4f10a72012-05-15 20:47:50 +00002183 const SkRect* src = &origSrc;
2184
reed@google.com9efd9a02012-01-30 15:41:43 +00002185 if (this->getLooper()) {
2186 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002187 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002188 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002190
reed@google.come4f10a72012-05-15 20:47:50 +00002191 SkRect tmpSrc;
2192 if (this->getPathEffect()) {
2193 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2194 src = &tmpSrc;
2195 }
2196
reed@google.coma584aed2012-05-16 14:06:02 +00002197 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002198 // since we're stroked, outset the rect by the radius (and join type)
2199 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2200 if (0 == radius) { // hairline
2201 radius = SK_Scalar1;
2202 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2203 SkScalar scale = this->getStrokeMiter();
2204 if (scale > SK_Scalar1) {
2205 radius = SkScalarMul(radius, scale);
2206 }
2207 }
reed@google.come4f10a72012-05-15 20:47:50 +00002208 storage->set(src->fLeft - radius, src->fTop - radius,
2209 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002210 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002211 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002212 }
2213
reed@google.com9efd9a02012-01-30 15:41:43 +00002214 if (this->getMaskFilter()) {
2215 this->getMaskFilter()->computeFastBounds(*storage, storage);
2216 }
2217
reed@android.comd252db02009-04-01 18:31:44 +00002218 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219}
2220
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002221///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002223static bool has_thick_frame(const SkPaint& paint) {
2224 return paint.getStrokeWidth() > 0 &&
2225 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226}
2227
2228SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2229 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002230 bool applyStrokeAndPathEffects)
2231 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2233 true);
2234
djsollen@google.com166e6532012-03-20 14:24:38 +00002235 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002236 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2237
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002238 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002240 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241
djsollen@google.com166e6532012-03-20 14:24:38 +00002242 // can't use our canonical size if we need to apply patheffects
2243 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2245 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002246 if (has_thick_frame(fPaint)) {
2247 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2248 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002249 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002251 }
reed@google.com72cf4922011-01-04 19:58:20 +00002252
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002253 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254 fPaint.setStyle(SkPaint::kFill_Style);
2255 fPaint.setPathEffect(NULL);
2256 }
2257
2258 fCache = fPaint.detachCache(NULL);
2259
2260 SkPaint::Style style = SkPaint::kFill_Style;
2261 SkPathEffect* pe = NULL;
2262
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002263 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 style = paint.getStyle(); // restore
2265 pe = paint.getPathEffect(); // restore
2266 }
2267 fPaint.setStyle(style);
2268 fPaint.setPathEffect(pe);
2269 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2270
2271 // now compute fXOffset if needed
2272
2273 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002274 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002276 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2277 &count, NULL), fScale);
2278 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 xOffset = -width;
2282 }
2283 fXPos = xOffset;
2284 fPrevAdvance = 0;
2285
2286 fText = text;
2287 fStop = text + length;
reed@google.com7b4531f2012-08-07 15:53:00 +00002288
reed@google.com44da42e2011-11-10 20:04:47 +00002289 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290}
2291
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002292SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 SkGlyphCache::AttachCache(fCache);
2294}
2295
reed@google.com7b4531f2012-08-07 15:53:00 +00002296bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
2297 if (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2299
2300 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002301 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002303 if (glyph.fWidth) {
reed@google.com7b4531f2012-08-07 15:53:00 +00002304 if (path) {
2305 *path = fCache->findPath(glyph);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002306 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002307 } else {
2308 if (path) {
2309 *path = NULL;
2310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002312 if (xpos) {
2313 *xpos = fXPos;
2314 }
2315 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002316 }
reed@google.com7b4531f2012-08-07 15:53:00 +00002317 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002319
2320///////////////////////////////////////////////////////////////////////////////
2321
2322bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002323 if (fLooper) {
2324 return false;
2325 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002326 SkXfermode::Mode mode;
2327 if (SkXfermode::AsMode(fXfermode, &mode)) {
2328 switch (mode) {
2329 case SkXfermode::kSrcOver_Mode:
2330 case SkXfermode::kSrcATop_Mode:
2331 case SkXfermode::kDstOut_Mode:
2332 case SkXfermode::kDstOver_Mode:
2333 case SkXfermode::kPlus_Mode:
2334 return 0 == this->getAlpha();
2335 case SkXfermode::kDst_Mode:
2336 return true;
2337 default:
2338 break;
2339 }
2340 }
2341 return false;
2342}
2343
2344
reed@google.com15356a62011-11-03 19:29:08 +00002345//////////// Move these to their own file soon.
2346
robertphillips@google.com0456e0b2012-06-27 14:03:26 +00002347SK_DEFINE_INST_COUNT(SkDrawLooper)
2348
reed@google.com9efd9a02012-01-30 15:41:43 +00002349bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
2350 SkCanvas canvas;
2351
2352 this->init(&canvas);
2353 for (;;) {
2354 SkPaint p(paint);
2355 if (this->next(&canvas, &p)) {
2356 p.setLooper(NULL);
2357 if (!p.canComputeFastBounds()) {
2358 return false;
2359 }
2360 } else {
2361 break;
2362 }
2363 }
2364 return true;
2365}
2366
2367void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
2368 SkRect* dst) {
2369 SkCanvas canvas;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002370
reed@google.com9efd9a02012-01-30 15:41:43 +00002371 this->init(&canvas);
2372 for (bool firstTime = true;; firstTime = false) {
2373 SkPaint p(paint);
2374 if (this->next(&canvas, &p)) {
2375 SkRect r(src);
2376
2377 p.setLooper(NULL);
2378 p.computeFastBounds(r, &r);
2379 canvas.getTotalMatrix().mapRect(&r);
2380
2381 if (firstTime) {
2382 *dst = r;
2383 } else {
2384 dst->join(r);
2385 }
2386 } else {
2387 break;
2388 }
2389 }
2390}
2391