blob: 9197adde0f35eb2870b865df676eb49fddabe60f [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
10#include "SkPaint.h"
11#include "SkColorFilter.h"
12#include "SkDrawLooper.h"
13#include "SkFontHost.h"
14#include "SkMaskFilter.h"
15#include "SkPathEffect.h"
16#include "SkRasterizer.h"
17#include "SkShader.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000018#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkScalerContext.h"
20#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000021#include "SkTextFormatParams.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkTypeface.h"
23#include "SkXfermode.h"
24#include "SkAutoKern.h"
25
reed@google.coma3237872011-07-05 19:20:48 +000026// define this to get a printf for out-of-range parameter in setters
27// e.g. setTextSize(-1)
28//#define SK_REPORT_API_RANGE_CHECK
29
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#define SK_DefaultTextSize SkIntToScalar(12)
31
32#define SK_DefaultFlags 0 //(kNativeHintsText_Flag)
33
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000034#ifdef ANDROID
35#define GEN_ID_INC fGenerationID++
36#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
37#else
38#define GEN_ID_INC
39#define GEN_ID_INC_EVAL(expression)
40#endif
41
reed@android.coma3122b92009-08-13 20:38:25 +000042SkPaint::SkPaint() {
43 // since we may have padding, we zero everything so that our memcmp() call
44 // in operator== will work correctly.
45 // with this, we can skip 0 and null individual initializations
46 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000047
reed@android.coma3122b92009-08-13 20:38:25 +000048#if 0 // not needed with the bzero call above
49 fTypeface = NULL;
50 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 fPathEffect = NULL;
52 fShader = NULL;
53 fXfermode = NULL;
54 fMaskFilter = NULL;
55 fColorFilter = NULL;
56 fRasterizer = NULL;
57 fLooper = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000059#endif
60
61 fTextSize = SK_DefaultTextSize;
62 fTextScaleX = SK_Scalar1;
63 fColor = SK_ColorBLACK;
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 fMiterLimit = SK_DefaultMiterLimit;
65 fFlags = SK_DefaultFlags;
66 fCapType = kDefault_Cap;
67 fJoinType = kDefault_Join;
68 fTextAlign = kLeft_Align;
69 fStyle = kFill_Style;
70 fTextEncoding = kUTF8_TextEncoding;
agl@chromium.org309485b2009-07-21 17:41:32 +000071 fHinting = kNormal_Hinting;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000072#ifdef ANDROID
73 fGenerationID = 0;
74#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000075}
76
reed@google.com6fb7e2e2011-02-08 22:22:52 +000077SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 memcpy(this, &src, sizeof(src));
79
reed@google.com82065d62011-02-07 15:30:46 +000080 SkSafeRef(fTypeface);
81 SkSafeRef(fPathEffect);
82 SkSafeRef(fShader);
83 SkSafeRef(fXfermode);
84 SkSafeRef(fMaskFilter);
85 SkSafeRef(fColorFilter);
86 SkSafeRef(fRasterizer);
87 SkSafeRef(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088}
89
reed@google.com6fb7e2e2011-02-08 22:22:52 +000090SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000091 SkSafeUnref(fTypeface);
92 SkSafeUnref(fPathEffect);
93 SkSafeUnref(fShader);
94 SkSafeUnref(fXfermode);
95 SkSafeUnref(fMaskFilter);
96 SkSafeUnref(fColorFilter);
97 SkSafeUnref(fRasterizer);
98 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000099}
100
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000101SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 SkASSERT(&src);
103
reed@google.com82065d62011-02-07 15:30:46 +0000104 SkSafeRef(src.fTypeface);
105 SkSafeRef(src.fPathEffect);
106 SkSafeRef(src.fShader);
107 SkSafeRef(src.fXfermode);
108 SkSafeRef(src.fMaskFilter);
109 SkSafeRef(src.fColorFilter);
110 SkSafeRef(src.fRasterizer);
111 SkSafeRef(src.fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
reed@google.com82065d62011-02-07 15:30:46 +0000113 SkSafeUnref(fTypeface);
114 SkSafeUnref(fPathEffect);
115 SkSafeUnref(fShader);
116 SkSafeUnref(fXfermode);
117 SkSafeUnref(fMaskFilter);
118 SkSafeUnref(fColorFilter);
119 SkSafeUnref(fRasterizer);
120 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000122#ifdef ANDROID
123 uint32_t oldGenerationID = fGenerationID;
124#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 memcpy(this, &src, sizeof(src));
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000126#ifdef ANDROID
127 fGenerationID = oldGenerationID + 1;
128#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130 return *this;
131}
132
reed@google.comb530ef52011-07-20 19:55:42 +0000133bool operator==(const SkPaint& a, const SkPaint& b) {
134 return !memcmp(&a, &b, sizeof(a));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135}
136
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000137void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkPaint init;
139
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000140#ifdef ANDROID
141 uint32_t oldGenerationID = fGenerationID;
142#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000144#ifdef ANDROID
145 fGenerationID = oldGenerationID + 1;
146#endif
147}
148
149#ifdef ANDROID
150uint32_t SkPaint::getGenerationID() const {
151 return fGenerationID;
152}
153#endif
154
155void SkPaint::setHinting(Hinting hintingLevel) {
156 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
157 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158}
159
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000160void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000161 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 fFlags = flags;
163}
164
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000165void SkPaint::setAntiAlias(bool doAA) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000166 GEN_ID_INC_EVAL(doAA != isAntiAlias());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
168}
169
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000170void SkPaint::setDither(bool doDither) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000171 GEN_ID_INC_EVAL(doDither != isDither());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
173}
174
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000175void SkPaint::setSubpixelText(bool doSubpixel) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000176 GEN_ID_INC_EVAL(doSubpixel != isSubpixelText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
178}
179
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000180void SkPaint::setLCDRenderText(bool doLCDRender) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000181 GEN_ID_INC_EVAL(doLCDRender != isLCDRenderText());
agl@chromium.org309485b2009-07-21 17:41:32 +0000182 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
183}
184
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000185void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000186 GEN_ID_INC_EVAL(doEmbeddedBitmapText != isEmbeddedBitmapText());
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000187 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
188}
189
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000190void SkPaint::setAutohinted(bool useAutohinter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000191 GEN_ID_INC_EVAL(useAutohinter != isAutohinted());
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000192 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
193}
194
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000195void SkPaint::setLinearText(bool doLinearText) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000196 GEN_ID_INC_EVAL(doLinearText != isLinearText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
198}
199
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000200void SkPaint::setUnderlineText(bool doUnderline) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000201 GEN_ID_INC_EVAL(doUnderline != isUnderlineText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
203}
204
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000205void SkPaint::setStrikeThruText(bool doStrikeThru) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000206 GEN_ID_INC_EVAL(doStrikeThru != isStrikeThruText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
208}
209
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000210void SkPaint::setFakeBoldText(bool doFakeBold) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000211 GEN_ID_INC_EVAL(doFakeBold != isFakeBoldText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
213}
214
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000215void SkPaint::setDevKernText(bool doDevKern) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000216 GEN_ID_INC_EVAL(doDevKern != isDevKernText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
218}
219
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000220void SkPaint::setFilterBitmap(bool doFilter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000221 GEN_ID_INC_EVAL(doFilter != isFilterBitmap());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
223}
224
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000225void SkPaint::setStyle(Style style) {
226 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000227 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000229 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000230#ifdef SK_REPORT_API_RANGE_CHECK
231 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
232#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000233 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234}
235
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000236void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000237 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 fColor = color;
239}
240
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000241void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000242 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
243 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244}
245
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000247 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248}
249
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000250void SkPaint::setStrokeWidth(SkScalar width) {
251 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000252 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000254 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000255#ifdef SK_REPORT_API_RANGE_CHECK
256 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
257#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259}
260
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000261void SkPaint::setStrokeMiter(SkScalar limit) {
262 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000263 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000265 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000266#ifdef SK_REPORT_API_RANGE_CHECK
267 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
268#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000269 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270}
271
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000272void SkPaint::setStrokeCap(Cap ct) {
273 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000274 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000276 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000277#ifdef SK_REPORT_API_RANGE_CHECK
278 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
279#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000283void SkPaint::setStrokeJoin(Join jt) {
284 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000285 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000287 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000288#ifdef SK_REPORT_API_RANGE_CHECK
289 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
290#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000291 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292}
293
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000296void SkPaint::setTextAlign(Align align) {
297 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000298 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000300 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000301#ifdef SK_REPORT_API_RANGE_CHECK
302 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
303#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000304 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305}
306
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000307void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000308 if (ts > 0) {
309 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000311 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000312#ifdef SK_REPORT_API_RANGE_CHECK
313 SkDebugf("SkPaint::setTextSize() called with negative value\n");
314#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316}
317
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000318void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000319 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 fTextScaleX = scaleX;
321}
322
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000324 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 fTextSkewX = skewX;
326}
327
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000328void SkPaint::setTextEncoding(TextEncoding encoding) {
329 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000330 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000332 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000333#ifdef SK_REPORT_API_RANGE_CHECK
334 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
335#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337}
338
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000339///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000343 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 return font;
345}
346
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000347SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000349 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 return r;
351}
352
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000353SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000355 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 return looper;
357}
358
359///////////////////////////////////////////////////////////////////////////////
360
361#include "SkGlyphCache.h"
362#include "SkUtils.h"
363
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000364static void DetachDescProc(const SkDescriptor* desc, void* context) {
365 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
366}
367
368#ifdef ANDROID
369const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
370 SkGlyphCache* cache;
371 descriptorProc(NULL, DetachDescProc, &cache, true);
372
373 const SkGlyph& glyph = cache->getUnicharMetrics(text);
374
375 SkGlyphCache::AttachCache(cache);
376 return glyph;
377}
378
379const void* SkPaint::findImage(const SkGlyph& glyph) {
380 // See ::detachCache()
381 SkGlyphCache* cache;
382 descriptorProc(NULL, DetachDescProc, &cache, true);
383
384 const void* image = cache->findImage(glyph);
385
386 SkGlyphCache::AttachCache(cache);
387 return image;
388}
389#endif
390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
392 uint16_t glyphs[]) const {
393 if (byteLength == 0) {
394 return 0;
395 }
reed@google.com72cf4922011-01-04 19:58:20 +0000396
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 SkASSERT(textData != NULL);
398
399 if (NULL == glyphs) {
400 switch (this->getTextEncoding()) {
401 case kUTF8_TextEncoding:
402 return SkUTF8_CountUnichars((const char*)textData, byteLength);
403 case kUTF16_TextEncoding:
404 return SkUTF16_CountUnichars((const uint16_t*)textData,
405 byteLength >> 1);
406 case kGlyphID_TextEncoding:
407 return byteLength >> 1;
408 default:
409 SkASSERT(!"unknown text encoding");
410 }
411 return 0;
412 }
reed@google.com72cf4922011-01-04 19:58:20 +0000413
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000415
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 // handle this encoding before the setup for the glyphcache
417 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
418 // we want to ignore the low bit of byteLength
419 memcpy(glyphs, textData, byteLength >> 1 << 1);
420 return byteLength >> 1;
421 }
reed@google.com72cf4922011-01-04 19:58:20 +0000422
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 SkAutoGlyphCache autoCache(*this, NULL);
424 SkGlyphCache* cache = autoCache.getCache();
425
426 const char* text = (const char*)textData;
427 const char* stop = text + byteLength;
428 uint16_t* gptr = glyphs;
429
430 switch (this->getTextEncoding()) {
431 case SkPaint::kUTF8_TextEncoding:
432 while (text < stop) {
433 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
434 }
435 break;
436 case SkPaint::kUTF16_TextEncoding: {
437 const uint16_t* text16 = (const uint16_t*)text;
438 const uint16_t* stop16 = (const uint16_t*)stop;
439 while (text16 < stop16) {
440 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
441 }
442 break;
443 }
444 default:
445 SkASSERT(!"unknown text encoding");
446 }
447 return gptr - glyphs;
448}
449
reed@android.coma5dcaf62010-02-05 17:12:32 +0000450bool SkPaint::containsText(const void* textData, size_t byteLength) const {
451 if (0 == byteLength) {
452 return true;
453 }
reed@google.com72cf4922011-01-04 19:58:20 +0000454
reed@android.coma5dcaf62010-02-05 17:12:32 +0000455 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000456
reed@android.coma5dcaf62010-02-05 17:12:32 +0000457 // handle this encoding before the setup for the glyphcache
458 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
459 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
460 size_t count = byteLength >> 1;
461 for (size_t i = 0; i < count; i++) {
462 if (0 == glyphID[i]) {
463 return false;
464 }
465 }
466 return true;
467 }
reed@google.com72cf4922011-01-04 19:58:20 +0000468
reed@android.coma5dcaf62010-02-05 17:12:32 +0000469 SkAutoGlyphCache autoCache(*this, NULL);
470 SkGlyphCache* cache = autoCache.getCache();
471
472 switch (this->getTextEncoding()) {
473 case SkPaint::kUTF8_TextEncoding: {
474 const char* text = static_cast<const char*>(textData);
475 const char* stop = text + byteLength;
476 while (text < stop) {
477 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
478 return false;
479 }
480 }
481 break;
482 }
483 case SkPaint::kUTF16_TextEncoding: {
484 const uint16_t* text = static_cast<const uint16_t*>(textData);
485 const uint16_t* stop = text + (byteLength >> 1);
486 while (text < stop) {
487 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
488 return false;
489 }
490 }
491 break;
492 }
493 default:
494 SkASSERT(!"unknown text encoding");
495 return false;
496 }
497 return true;
498}
499
reed@android.com9d3a9852010-01-08 14:07:42 +0000500void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
501 SkUnichar textData[]) const {
502 if (count <= 0) {
503 return;
504 }
505
506 SkASSERT(glyphs != NULL);
507 SkASSERT(textData != NULL);
508
509 SkAutoGlyphCache autoCache(*this, NULL);
510 SkGlyphCache* cache = autoCache.getCache();
511
512 for (int index = 0; index < count; index++) {
513 textData[index] = cache->glyphToUnichar(glyphs[index]);
514 }
515}
516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517///////////////////////////////////////////////////////////////////////////////
518
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000519static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
520 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 SkASSERT(cache != NULL);
522 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
525}
526
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000527static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
528 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 SkASSERT(cache != NULL);
530 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
533}
534
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000535static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
536 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 SkASSERT(cache != NULL);
538 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
541}
542
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000543static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
544 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 SkASSERT(cache != NULL);
546 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000547
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
549}
550
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000551static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
552 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkASSERT(cache != NULL);
554 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 const uint16_t* ptr = *(const uint16_t**)text;
557 unsigned glyphID = *ptr;
558 ptr += 1;
559 *text = (const char*)ptr;
560 return cache->getGlyphIDMetrics(glyphID);
561}
562
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000563static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
564 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 SkASSERT(cache != NULL);
566 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 const uint16_t* ptr = *(const uint16_t**)text;
569 ptr -= 1;
570 unsigned glyphID = *ptr;
571 *text = (const char*)ptr;
572 return cache->getGlyphIDMetrics(glyphID);
573}
574
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000575static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
576 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 SkASSERT(cache != NULL);
578 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
581}
582
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000583static const SkGlyph& sk_getAdvance_utf8_prev(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->getUnicharAdvance(SkUTF8_PrevUnichar(text));
589}
590
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000591static const SkGlyph& sk_getAdvance_utf16_next(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->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
597}
598
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000599static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
600 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkASSERT(cache != NULL);
602 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
605}
606
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000607static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
608 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 SkASSERT(cache != NULL);
610 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 const uint16_t* ptr = *(const uint16_t**)text;
613 unsigned glyphID = *ptr;
614 ptr += 1;
615 *text = (const char*)ptr;
616 return cache->getGlyphIDAdvance(glyphID);
617}
618
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000619static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
620 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkASSERT(cache != NULL);
622 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000623
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 const uint16_t* ptr = *(const uint16_t**)text;
625 ptr -= 1;
626 unsigned glyphID = *ptr;
627 *text = (const char*)ptr;
628 return cache->getGlyphIDAdvance(glyphID);
629}
630
631SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000632 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
634 sk_getMetrics_utf8_next,
635 sk_getMetrics_utf16_next,
636 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 sk_getMetrics_utf8_prev,
639 sk_getMetrics_utf16_prev,
640 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 sk_getAdvance_utf8_next,
643 sk_getAdvance_utf16_next,
644 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 sk_getAdvance_utf8_prev,
647 sk_getAdvance_utf16_prev,
648 sk_getAdvance_glyph_prev
649 };
reed@google.com72cf4922011-01-04 19:58:20 +0000650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 unsigned index = this->getTextEncoding();
652
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000653 if (kBackward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000655 }
656 if (!needFullMetrics && !this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 index += 6;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000658 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659
660 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
661 return gMeasureCacheProcs[index];
662}
663
664///////////////////////////////////////////////////////////////////////////////
665
666static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000667 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 SkASSERT(cache != NULL);
669 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
672}
673
674static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000675 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 SkASSERT(cache != NULL);
677 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
680}
681
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000682static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
683 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 SkASSERT(cache != NULL);
685 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000686
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
688}
689
690static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000691 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 SkASSERT(cache != NULL);
693 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
696 x, y);
697}
698
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000699static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
700 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 SkASSERT(cache != NULL);
702 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 const uint16_t* ptr = *(const uint16_t**)text;
705 unsigned glyphID = *ptr;
706 ptr += 1;
707 *text = (const char*)ptr;
708 return cache->getGlyphIDMetrics(glyphID);
709}
710
711static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000712 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 SkASSERT(cache != NULL);
714 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 const uint16_t* ptr = *(const uint16_t**)text;
717 unsigned glyphID = *ptr;
718 ptr += 1;
719 *text = (const char*)ptr;
720 return cache->getGlyphIDMetrics(glyphID, x, y);
721}
722
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000723SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 static const SkDrawCacheProc gDrawCacheProcs[] = {
725 sk_getMetrics_utf8_00,
726 sk_getMetrics_utf16_00,
727 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 sk_getMetrics_utf8_xy,
730 sk_getMetrics_utf16_xy,
731 sk_getMetrics_glyph_xy
732 };
reed@google.com72cf4922011-01-04 19:58:20 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735 if (fFlags & kSubpixelText_Flag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000737 }
reed@google.com72cf4922011-01-04 19:58:20 +0000738
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
740 return gDrawCacheProcs[index];
741}
742
743///////////////////////////////////////////////////////////////////////////////
744
745class SkAutoRestorePaintTextSizeAndFrame {
746public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000747 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
748 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 fTextSize = paint->getTextSize();
750 fStyle = paint->getStyle();
751 fPaint->setStyle(SkPaint::kFill_Style);
752 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000753
754 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 fPaint->setStyle(fStyle);
756 fPaint->setTextSize(fTextSize);
757 }
reed@google.com72cf4922011-01-04 19:58:20 +0000758
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759private:
760 SkPaint* fPaint;
761 SkScalar fTextSize;
762 SkPaint::Style fStyle;
763};
764
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000765static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 bounds->set(SkIntToScalar(g.fLeft),
767 SkIntToScalar(g.fTop),
768 SkIntToScalar(g.fLeft + g.fWidth),
769 SkIntToScalar(g.fTop + g.fHeight));
770}
771
reed@android.come88f5512010-03-19 14:42:28 +0000772// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
773// we don't overflow along the way
774typedef int64_t Sk48Dot16;
775
776#ifdef SK_SCALAR_IS_FLOAT
777 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000778 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000779 }
780#else
781 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
782 // just return the low 32bits
783 return static_cast<SkFixed>(x);
784 }
785#endif
786
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000787static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000788 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 bounds->join(SkIntToScalar(g.fLeft) + sx,
790 SkIntToScalar(g.fTop),
791 SkIntToScalar(g.fLeft + g.fWidth) + sx,
792 SkIntToScalar(g.fTop + g.fHeight));
793}
794
795SkScalar SkPaint::measure_text(SkGlyphCache* cache,
796 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000797 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000799 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000801 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000803 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 return 0;
805 }
806
807 SkMeasureCacheProc glyphCacheProc;
808 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
809 NULL != bounds);
810
811 int n = 1;
812 const char* stop = (const char*)text + byteLength;
813 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000814 // our accumulated fixed-point advances might overflow 16.16, so we use
815 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
816 // very end.
817 Sk48Dot16 x = g->fAdvanceX;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818
819 SkAutoKern autokern;
820
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000821 if (NULL == bounds) {
822 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 int rsb;
824 for (; text < stop; n++) {
825 rsb = g->fRsbDelta;
826 g = &glyphCacheProc(cache, &text);
827 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
828 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000829 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 for (; text < stop; n++) {
831 x += glyphCacheProc(cache, &text).fAdvanceX;
832 }
833 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000834 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000836 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 int rsb;
838 for (; text < stop; n++) {
839 rsb = g->fRsbDelta;
840 g = &glyphCacheProc(cache, &text);
841 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
842 join_bounds(*g, bounds, x);
843 x += g->fAdvanceX;
844 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000845 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 for (; text < stop; n++) {
847 g = &glyphCacheProc(cache, &text);
848 join_bounds(*g, bounds, x);
849 x += g->fAdvanceX;
850 }
851 }
852 }
853 SkASSERT(text == stop);
854
855 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000856 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857}
858
859SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000860 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 const char* text = (const char*)textData;
862 SkASSERT(text != NULL || length == 0);
863
864 SkScalar scale = 0;
865 SkAutoRestorePaintTextSizeAndFrame restore(this);
866
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000867 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 scale = fTextSize / kCanonicalTextSizeForPaths;
869 // this gets restored by restore
870 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
871 }
reed@google.com72cf4922011-01-04 19:58:20 +0000872
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000873 SkMatrix zoomMatrix, *zoomPtr = NULL;
874 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 zoomMatrix.setScale(zoom, zoom);
876 zoomPtr = &zoomMatrix;
877 }
878
879 SkAutoGlyphCache autoCache(*this, zoomPtr);
880 SkGlyphCache* cache = autoCache.getCache();
881
882 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000883
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000884 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 int tempCount;
886
887 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000888 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000890 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
892 bounds->fTop = SkScalarMul(bounds->fTop, scale);
893 bounds->fRight = SkScalarMul(bounds->fRight, scale);
894 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
895 }
896 }
897 }
898 return width;
899}
900
901typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
902
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000903static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 return text < stop;
905}
906
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000907static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 return text > stop;
909}
910
911static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000912 const char** text, size_t length,
913 const char** stop) {
914 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 *stop = *text + length;
916 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000917 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 // text should point to the end of the buffer, and stop to the beginning
919 *stop = *text;
920 *text += length;
921 return backward_textBufferPred;
922 }
923}
924
925size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
926 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000927 TextBufferDirection tbd) const {
928 if (0 == length || 0 >= maxWidth) {
929 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000931 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 return 0;
933 }
934
935 SkASSERT(textD != NULL);
936 const char* text = (const char*)textD;
937
938 SkScalar scale = 0;
939 SkAutoRestorePaintTextSizeAndFrame restore(this);
940
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000941 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +0000943 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 // this gets restored by restore
945 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
946 }
reed@google.com72cf4922011-01-04 19:58:20 +0000947
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 SkAutoGlyphCache autoCache(*this, NULL);
949 SkGlyphCache* cache = autoCache.getCache();
950
951 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
952 const char* stop;
953 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@android.come88f5512010-03-19 14:42:28 +0000954 // use 64bits for our accumulator, to avoid overflowing 16.16
955 Sk48Dot16 max = SkScalarToFixed(maxWidth);
956 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957
958 SkAutoKern autokern;
959
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000960 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000962 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 const char* curr = text;
964 const SkGlyph& g = glyphCacheProc(cache, &text);
965 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000966 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 width -= x;
968 text = curr;
969 break;
970 }
971 rsb = g.fRsbDelta;
972 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000973 } else {
974 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 const char* curr = text;
976 SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000977 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 width -= x;
979 text = curr;
980 break;
981 }
982 }
983 }
reed@google.com72cf4922011-01-04 19:58:20 +0000984
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000985 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +0000986 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000987 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000989 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 *measuredWidth = scalarWidth;
991 }
reed@google.com72cf4922011-01-04 19:58:20 +0000992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 // return the number of bytes measured
994 return (kForward_TextBufferDirection == tbd) ?
995 text - stop + length : stop - text + length;
996}
997
998///////////////////////////////////////////////////////////////////////////////
999
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001000static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
1002 return false; // don't detach the cache
1003}
1004
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001005static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
1007}
1008
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001009SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 SkScalar scale = 0;
1011 SkAutoRestorePaintTextSizeAndFrame restore(this);
1012
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001013 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 scale = fTextSize / kCanonicalTextSizeForPaths;
1015 // this gets restored by restore
1016 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1017 }
reed@google.com72cf4922011-01-04 19:58:20 +00001018
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001019 SkMatrix zoomMatrix, *zoomPtr = NULL;
1020 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 zoomMatrix.setScale(zoom, zoom);
1022 zoomPtr = &zoomMatrix;
1023 }
1024
1025#if 0
1026 SkAutoGlyphCache autoCache(*this, zoomPtr);
1027 SkGlyphCache* cache = autoCache.getCache();
1028 const FontMetrics& my = cache->getFontMetricsY();
1029#endif
1030 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001031 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001033 }
reed@google.com72cf4922011-01-04 19:58:20 +00001034
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
1036
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001037 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1039 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1040 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1041 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1042 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
1043 }
1044 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1045}
1046
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001047///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001049static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 bounds->set(g.fLeft * scale,
1051 g.fTop * scale,
1052 (g.fLeft + g.fWidth) * scale,
1053 (g.fTop + g.fHeight) * scale);
1054}
1055
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001056int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1057 SkScalar widths[], SkRect bounds[]) const {
1058 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061
1062 SkASSERT(NULL != textData);
1063
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001064 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067
1068 SkAutoRestorePaintTextSizeAndFrame restore(this);
1069 SkScalar scale = 0;
1070
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001071 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 scale = fTextSize / kCanonicalTextSizeForPaths;
1073 // this gets restored by restore
1074 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1075 }
1076
1077 SkAutoGlyphCache autoCache(*this, NULL);
1078 SkGlyphCache* cache = autoCache.getCache();
1079 SkMeasureCacheProc glyphCacheProc;
1080 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1081 NULL != bounds);
1082
1083 const char* text = (const char*)textData;
1084 const char* stop = text + byteLength;
1085 int count = 0;
1086
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001087 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 // we adjust the widths returned here through auto-kerning
1089 SkAutoKern autokern;
1090 SkFixed prevWidth = 0;
1091
1092 if (scale) {
1093 while (text < stop) {
1094 const SkGlyph& g = glyphCacheProc(cache, &text);
1095 if (widths) {
1096 SkFixed adjust = autokern.adjust(g);
1097
1098 if (count > 0) {
1099 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1100 *widths++ = SkScalarMul(w, scale);
1101 }
1102 prevWidth = g.fAdvanceX;
1103 }
1104 if (bounds) {
1105 set_bounds(g, bounds++, scale);
1106 }
1107 ++count;
1108 }
1109 if (count > 0 && widths) {
1110 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1111 }
1112 } else {
1113 while (text < stop) {
1114 const SkGlyph& g = glyphCacheProc(cache, &text);
1115 if (widths) {
1116 SkFixed adjust = autokern.adjust(g);
1117
1118 if (count > 0) {
1119 *widths++ = SkFixedToScalar(prevWidth + adjust);
1120 }
1121 prevWidth = g.fAdvanceX;
1122 }
1123 if (bounds) {
1124 set_bounds(g, bounds++);
1125 }
1126 ++count;
1127 }
1128 if (count > 0 && widths) {
1129 *widths = SkFixedToScalar(prevWidth);
1130 }
1131 }
1132 } else { // no devkern
1133 if (scale) {
1134 while (text < stop) {
1135 const SkGlyph& g = glyphCacheProc(cache, &text);
1136 if (widths) {
1137 *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
1138 scale);
1139 }
1140 if (bounds) {
1141 set_bounds(g, bounds++, scale);
1142 }
1143 ++count;
1144 }
1145 } else {
1146 while (text < stop) {
1147 const SkGlyph& g = glyphCacheProc(cache, &text);
1148 if (widths) {
1149 *widths++ = SkFixedToScalar(g.fAdvanceX);
1150 }
1151 if (bounds) {
1152 set_bounds(g, bounds++);
1153 }
1154 ++count;
1155 }
1156 }
1157 }
1158
1159 SkASSERT(text == stop);
1160 return count;
1161}
1162
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001163///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164
1165#include "SkDraw.h"
1166
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001167void SkPaint::getTextPath(const void* textData, size_t length,
1168 SkScalar x, SkScalar y, SkPath* path) const {
1169 SkASSERT(length == 0 || textData != NULL);
1170
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001172 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001174 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
1176 SkTextToPathIter iter(text, length, *this, false, true);
1177 SkMatrix matrix;
1178 SkScalar prevXPos = 0;
1179
1180 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1181 matrix.postTranslate(x, y);
1182 path->reset();
1183
1184 SkScalar xpos;
1185 const SkPath* iterPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001186 while ((iterPath = iter.next(&xpos)) != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 matrix.postTranslate(xpos - prevXPos, 0);
1188 path->addPath(*iterPath, matrix);
1189 prevXPos = xpos;
1190 }
1191}
1192
1193static void add_flattenable(SkDescriptor* desc, uint32_t tag,
1194 SkFlattenableWriteBuffer* buffer) {
1195 buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
1196}
1197
reed@google.com2739b272011-09-28 17:26:42 +00001198// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001199static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001202 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001203 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001204 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001205 }
1206
reed@google.com65dd8f82011-03-14 13:31:16 +00001207 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001208 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001209 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001210
1211 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
reed@android.com1cdcb512009-08-24 19:11:00 +00001214// if linear-text is on, then we force hinting to be off (since that's sort of
1215// the point of linear-text.
1216static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1217 SkPaint::Hinting h = paint.getHinting();
1218 if (paint.isLinearText()) {
1219 h = SkPaint::kNo_Hinting;
1220 }
1221 return h;
1222}
1223
reed@google.com4f79b9b2011-09-13 13:23:26 +00001224// Beyond this size, LCD doesn't appreciably improve quality, but it always
1225// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001226#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1227 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1228#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001229
1230static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1231 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1232 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1233 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001234 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001235}
1236
reed@google.com72cf4922011-01-04 19:58:20 +00001237/*
1238 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1239 * that vary only slightly when we create our key into the font cache, since the font scaler
1240 * typically returns the same looking resuts for tiny changes in the matrix.
1241 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001242static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001243#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001244 int n = sk_float_round2int(x * 1024);
1245 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001246#else
1247 // round to the nearest 10 fractional bits
1248 return (x + (1 << 5)) & ~(1024 - 1);
1249#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001250}
1251
reed@android.com36a4c2a2009-07-22 19:52:11 +00001252void SkScalerContext::MakeRec(const SkPaint& paint,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001253 const SkMatrix* deviceMatrix, Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001254 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255
reed@google.com7d26c592011-06-13 13:01:10 +00001256 rec->fOrigFontID = SkTypeface::UniqueID(paint.getTypeface());
1257 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 rec->fTextSize = paint.getTextSize();
1259 rec->fPreScaleX = paint.getTextScaleX();
1260 rec->fPreSkewX = paint.getTextSkewX();
1261
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001262 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001263 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1264 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1265 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1266 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001267 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1269 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1270 }
reed@google.com72cf4922011-01-04 19:58:20 +00001271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 SkPaint::Style style = paint.getStyle();
1273 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001274
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001275 unsigned flags = SkFontHost::ComputeGammaFlag(paint);
1276
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001277 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001278#ifdef SK_USE_FREETYPE_EMBOLDEN
1279 flags |= SkScalerContext::kEmbolden_Flag;
1280#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001281 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1282 kStdFakeBoldInterpKeys,
1283 kStdFakeBoldInterpValues,
1284 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001286
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001287 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 style = SkPaint::kStrokeAndFill_Style;
1289 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001290 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001292 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001293#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 }
1295
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001296 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001298 }
reed@google.com72cf4922011-01-04 19:58:20 +00001299
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001300 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 rec->fFrameWidth = strokeWidth;
1302 rec->fMiterLimit = paint.getStrokeMiter();
1303 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1304
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001305 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001307 }
1308 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 rec->fFrameWidth = 0;
1310 rec->fMiterLimit = 0;
1311 rec->fStrokeJoin = 0;
1312 }
1313
reed@google.com02b53312011-05-18 19:00:53 +00001314 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1315
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001316 if (SkMask::kLCD16_Format == rec->fMaskFormat ||
1317 SkMask::kLCD32_Format == rec->fMaskFormat)
1318 {
reed@google.com02b53312011-05-18 19:00:53 +00001319 SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
1320 SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
reed@google.com4f79b9b2011-09-13 13:23:26 +00001321 if (SkFontHost::kNONE_LCDOrder == order || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001322 // eeek, can't support LCD
1323 rec->fMaskFormat = SkMask::kA8_Format;
1324 } else {
1325 if (SkFontHost::kVertical_LCDOrientation == orient) {
1326 flags |= SkScalerContext::kLCD_Vertical_Flag;
1327 }
1328 if (SkFontHost::kBGR_LCDOrder == order) {
1329 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1330 }
1331 }
1332 }
1333
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001334 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001335 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001336 }
1337 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001338 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001339 }
1340 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001341 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001342 }
reed@google.com02b53312011-05-18 19:00:53 +00001343 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001344
reed@google.com9d757672011-05-18 21:14:39 +00001345 // setHinting modifies fFlags, so do this last
1346 rec->setHinting(computeHinting(paint));
1347
reed@android.com36a4c2a2009-07-22 19:52:11 +00001348 /* Allow the fonthost to modify our rec before we use it as a key into the
1349 cache. This way if we're asking for something that they will ignore,
1350 they can modify our rec up front, so we don't create duplicate cache
1351 entries.
1352 */
1353 SkFontHost::FilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001354
1355 // No need to differentiate gamma if we're BW
1356 if (SkMask::kBW_Format == rec->fMaskFormat) {
1357 rec->fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
1358 SkScalerContext::kGammaForWhite_Flag);
1359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360}
1361
1362#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1363
reed@google.com17fb3872011-05-04 14:31:07 +00001364#ifdef SK_DEBUG
1365 #define TEST_DESC
1366#endif
1367
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
1369 void (*proc)(const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001370 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 SkScalerContext::Rec rec;
1372
1373 SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001374 if (ignoreGamma) {
1375 rec.fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
1376 SkScalerContext::kGammaForWhite_Flag);
1377 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378
1379 size_t descSize = sizeof(rec);
1380 int entryCount = 1;
1381 SkPathEffect* pe = this->getPathEffect();
1382 SkMaskFilter* mf = this->getMaskFilter();
1383 SkRasterizer* ra = this->getRasterizer();
1384
1385 SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1386 SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1387 SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1388
1389 if (pe) {
1390 peBuffer.writeFlattenable(pe);
1391 descSize += peBuffer.size();
1392 entryCount += 1;
1393 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1394 // seems like we could support kLCD as well at this point...
1395 }
1396 if (mf) {
1397 mfBuffer.writeFlattenable(mf);
1398 descSize += mfBuffer.size();
1399 entryCount += 1;
1400 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1401 }
1402 if (ra) {
1403 raBuffer.writeFlattenable(ra);
1404 descSize += raBuffer.size();
1405 entryCount += 1;
1406 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1407 }
1408 descSize += SkDescriptor::ComputeOverhead(entryCount);
1409
1410 SkAutoDescriptor ad(descSize);
1411 SkDescriptor* desc = ad.getDesc();
1412
1413 desc->init();
1414 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1415
1416 if (pe) {
1417 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1418 }
1419 if (mf) {
1420 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1421 }
1422 if (ra) {
1423 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1424 }
1425
1426 SkASSERT(descSize == desc->getLength());
1427 desc->computeChecksum();
1428
reed@google.com17fb3872011-05-04 14:31:07 +00001429#ifdef TEST_DESC
1430 {
1431 // Check that we completely write the bytes in desc (our key), and that
1432 // there are no uninitialized bytes. If there were, then we would get
1433 // false-misses (or worse, false-hits) in our fontcache.
1434 //
1435 // We do this buy filling 2 others, one with 0s and the other with 1s
1436 // and create those, and then check that all 3 are identical.
1437 SkAutoDescriptor ad1(descSize);
1438 SkAutoDescriptor ad2(descSize);
1439 SkDescriptor* desc1 = ad1.getDesc();
1440 SkDescriptor* desc2 = ad2.getDesc();
1441
1442 memset(desc1, 0x00, descSize);
1443 memset(desc2, 0xFF, descSize);
1444
1445 desc1->init();
1446 desc2->init();
1447 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1448 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1449
1450 if (pe) {
1451 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1452 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1453 }
1454 if (mf) {
1455 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1456 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1457 }
1458 if (ra) {
1459 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1460 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1461 }
1462
1463 SkASSERT(descSize == desc1->getLength());
1464 SkASSERT(descSize == desc2->getLength());
1465 desc1->computeChecksum();
1466 desc2->computeChecksum();
1467 SkASSERT(!memcmp(desc, desc1, descSize));
1468 SkASSERT(!memcmp(desc, desc2, descSize));
1469 }
1470#endif
1471
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472 proc(desc, context);
1473}
1474
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001475SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 SkGlyphCache* cache;
1477 this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
1478 return cache;
1479}
1480
1481///////////////////////////////////////////////////////////////////////////////
1482
1483#include "SkStream.h"
1484
reed@android.comaefd2bc2009-03-30 21:02:14 +00001485static uintptr_t asint(const void* p) {
1486 return reinterpret_cast<uintptr_t>(p);
1487}
1488
1489union Scalar32 {
1490 SkScalar fScalar;
1491 uint32_t f32;
1492};
1493
1494static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1495 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1496 Scalar32 tmp;
1497 tmp.fScalar = value;
1498 *ptr = tmp.f32;
1499 return ptr + 1;
1500}
1501
1502static SkScalar read_scalar(const uint32_t*& ptr) {
1503 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1504 Scalar32 tmp;
1505 tmp.f32 = *ptr++;
1506 return tmp.fScalar;
1507}
1508
1509static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1510 SkASSERT(a == (uint8_t)a);
1511 SkASSERT(b == (uint8_t)b);
1512 SkASSERT(c == (uint8_t)c);
1513 SkASSERT(d == (uint8_t)d);
1514 return (a << 24) | (b << 16) | (c << 8) | d;
1515}
1516
1517enum FlatFlags {
1518 kHasTypeface_FlatFlag = 0x01,
1519 kHasEffects_FlatFlag = 0x02
1520};
1521
1522// The size of a flat paint's POD fields
1523static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
1524 1 * sizeof(SkColor) +
1525 1 * sizeof(uint16_t) +
1526 6 * sizeof(uint8_t);
1527
1528/* To save space/time, we analyze the paint, and write a truncated version of
1529 it if there are not tricky elements like shaders, etc.
1530 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001532 uint8_t flatFlags = 0;
1533 if (this->getTypeface()) {
1534 flatFlags |= kHasTypeface_FlatFlag;
1535 }
1536 if (asint(this->getPathEffect()) |
1537 asint(this->getShader()) |
1538 asint(this->getXfermode()) |
1539 asint(this->getMaskFilter()) |
1540 asint(this->getColorFilter()) |
1541 asint(this->getRasterizer()) |
1542 asint(this->getLooper())) {
1543 flatFlags |= kHasEffects_FlatFlag;
1544 }
reed@google.com72cf4922011-01-04 19:58:20 +00001545
reed@android.comaefd2bc2009-03-30 21:02:14 +00001546 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1547 uint32_t* ptr = buffer.reserve(kPODPaintSize);
1548
1549 ptr = write_scalar(ptr, this->getTextSize());
1550 ptr = write_scalar(ptr, this->getTextScaleX());
1551 ptr = write_scalar(ptr, this->getTextSkewX());
1552 ptr = write_scalar(ptr, this->getStrokeWidth());
1553 ptr = write_scalar(ptr, this->getStrokeMiter());
1554 *ptr++ = this->getColor();
1555 *ptr++ = (this->getFlags() << 16) | (this->getTextAlign() << 8) | flatFlags;
1556 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1557 this->getStyle(), this->getTextEncoding());
1558
1559 // now we're done with ptr and the (pre)reserved space. If we need to write
1560 // additional fields, use the buffer directly
1561 if (flatFlags & kHasTypeface_FlatFlag) {
1562 buffer.writeTypeface(this->getTypeface());
1563 }
1564 if (flatFlags & kHasEffects_FlatFlag) {
1565 buffer.writeFlattenable(this->getPathEffect());
1566 buffer.writeFlattenable(this->getShader());
1567 buffer.writeFlattenable(this->getXfermode());
1568 buffer.writeFlattenable(this->getMaskFilter());
1569 buffer.writeFlattenable(this->getColorFilter());
1570 buffer.writeFlattenable(this->getRasterizer());
1571 buffer.writeFlattenable(this->getLooper());
1572 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573}
1574
1575void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001576 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1577 const void* podData = buffer.skip(kPODPaintSize);
1578 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@google.com72cf4922011-01-04 19:58:20 +00001579
reed@android.comaefd2bc2009-03-30 21:02:14 +00001580 // the order we read must match the order we wrote in flatten()
1581 this->setTextSize(read_scalar(pod));
1582 this->setTextScaleX(read_scalar(pod));
1583 this->setTextSkewX(read_scalar(pod));
1584 this->setStrokeWidth(read_scalar(pod));
reed@google.com72cf4922011-01-04 19:58:20 +00001585 this->setStrokeMiter(read_scalar(pod));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001586 this->setColor(*pod++);
1587
1588 uint32_t tmp = *pod++;
1589 this->setFlags(tmp >> 16);
1590 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xFF));
1591 uint8_t flatFlags = tmp & 0xFF;
1592
1593 tmp = *pod++;
1594 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1595 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1596 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1597 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
1598
1599 if (flatFlags & kHasTypeface_FlatFlag) {
1600 this->setTypeface(buffer.readTypeface());
1601 } else {
1602 this->setTypeface(NULL);
1603 }
1604
1605 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com82065d62011-02-07 15:30:46 +00001606 SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable()));
1607 SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable()));
1608 SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable()));
1609 SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable()));
1610 SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable()));
1611 SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable()));
1612 SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001613 } else {
1614 this->setPathEffect(NULL);
1615 this->setShader(NULL);
1616 this->setXfermode(NULL);
1617 this->setMaskFilter(NULL);
1618 this->setColorFilter(NULL);
1619 this->setRasterizer(NULL);
1620 this->setLooper(NULL);
1621 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
1624///////////////////////////////////////////////////////////////////////////////
1625
reed@google.com82065d62011-02-07 15:30:46 +00001626SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001627 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 SkRefCnt_SafeAssign(fShader, shader);
1629 return shader;
1630}
1631
reed@google.com82065d62011-02-07 15:30:46 +00001632SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001633 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 SkRefCnt_SafeAssign(fColorFilter, filter);
1635 return filter;
1636}
1637
reed@google.com82065d62011-02-07 15:30:46 +00001638SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001639 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 SkRefCnt_SafeAssign(fXfermode, mode);
1641 return mode;
1642}
1643
reed@android.com0baf1932009-06-24 12:41:42 +00001644SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00001645 SkSafeUnref(fXfermode);
1646 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001647 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00001648 return fXfermode;
1649}
1650
reed@google.com82065d62011-02-07 15:30:46 +00001651SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001652 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 SkRefCnt_SafeAssign(fPathEffect, effect);
1654 return effect;
1655}
1656
reed@google.com82065d62011-02-07 15:30:46 +00001657SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001658 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659 SkRefCnt_SafeAssign(fMaskFilter, filter);
1660 return filter;
1661}
1662
reed@google.com82065d62011-02-07 15:30:46 +00001663///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001665bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666 SkPath effectPath, strokePath;
1667 const SkPath* path = &src;
1668
1669 SkScalar width = this->getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001670
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671 switch (this->getStyle()) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001672 case SkPaint::kFill_Style:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001674 break;
1675 case SkPaint::kStrokeAndFill_Style:
1676 if (width == 0) {
1677 width = -1; // mark it as no-stroke
1678 }
1679 break;
1680 case SkPaint::kStroke_Style:
1681 break;
1682 default:
1683 SkASSERT(!"unknown paint style");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 }
1685
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001686 if (this->getPathEffect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687 // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001688 if (this->getStyle() == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001690 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001692 if (this->getPathEffect()->filterPath(&effectPath, src, &width)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693 path = &effectPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001694 }
reed@google.com72cf4922011-01-04 19:58:20 +00001695
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696 // restore the width if we earlier had to lie, and if we're still set to no-stroke
1697 // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
1698 // and we want to respect that (i.e. don't overwrite their setting for width)
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001699 if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001700 width = this->getStrokeWidth();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001701 if (width == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 width = -1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001703 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 }
1705 }
reed@google.com72cf4922011-01-04 19:58:20 +00001706
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001707 if (width > 0 && !path->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708 SkStroke stroker(*this, width);
1709 stroker.strokePath(*path, &strokePath);
1710 path = &strokePath;
1711 }
1712
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001713 if (path == &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 *dst = src;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001715 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 SkASSERT(path == &effectPath || path == &strokePath);
1717 dst->swap(*(SkPath*)path);
1718 }
1719
1720 return width != 0; // return true if we're filled, or false if we're hairline (width == 0)
1721}
1722
reed@android.comd252db02009-04-01 18:31:44 +00001723const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
1724 SkRect* storage) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00001726 SkASSERT(this->getStyle() != SkPaint::kFill_Style);
1727
1728 // since we're stroked, outset the rect by the radius (and join type)
1729 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
1730 if (0 == radius) { // hairline
1731 radius = SK_Scalar1;
1732 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
1733 SkScalar scale = this->getStrokeMiter();
1734 if (scale > SK_Scalar1) {
1735 radius = SkScalarMul(radius, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 }
reed@android.comd252db02009-04-01 18:31:44 +00001738 storage->set(src.fLeft - radius, src.fTop - radius,
1739 src.fRight + radius, src.fBottom + radius);
1740 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741}
1742
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001743///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001745static bool has_thick_frame(const SkPaint& paint) {
1746 return paint.getStrokeWidth() > 0 &&
1747 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748}
1749
1750SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
1751 const SkPaint& paint,
1752 bool applyStrokeAndPathEffects,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001753 bool forceLinearTextOn) : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
1755 true);
1756
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001757 if (forceLinearTextOn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 fPaint.setLinearText(true);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001759 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
1761
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001762 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001764 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765
1766 // can't use our canonical size if we need to apply patheffects/strokes
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001767 if (fPaint.isLinearText() && !applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
1769 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001770 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001772 }
reed@google.com72cf4922011-01-04 19:58:20 +00001773
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001774 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 fPaint.setStyle(SkPaint::kFill_Style);
1776 fPaint.setPathEffect(NULL);
1777 }
1778
1779 fCache = fPaint.detachCache(NULL);
1780
1781 SkPaint::Style style = SkPaint::kFill_Style;
1782 SkPathEffect* pe = NULL;
1783
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001784 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 style = paint.getStyle(); // restore
1786 pe = paint.getPathEffect(); // restore
1787 }
1788 fPaint.setStyle(style);
1789 fPaint.setPathEffect(pe);
1790 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
1791
1792 // now compute fXOffset if needed
1793
1794 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001795 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001797 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
1798 &count, NULL), fScale);
1799 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001801 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 xOffset = -width;
1803 }
1804 fXPos = xOffset;
1805 fPrevAdvance = 0;
1806
1807 fText = text;
1808 fStop = text + length;
1809}
1810
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001811SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 SkGlyphCache::AttachCache(fCache);
1813}
1814
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001815const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
1816 while (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
1818
1819 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
1820 fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking();
1821
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001822 if (glyph.fWidth) {
1823 if (xpos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 *xpos = fXPos;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001825 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 return fCache->findPath(glyph);
1827 }
1828 }
1829 return NULL;
1830}