blob: 0608320f40c8e99cad330ee424835ac6ee88a626 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/sgl/SkPaint.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.com72cf4922011-01-04 19:58:20 +00005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com72cf4922011-01-04 19:58:20 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com72cf4922011-01-04 19:58:20 +000011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkPaint.h"
19#include "SkColorFilter.h"
20#include "SkDrawLooper.h"
21#include "SkFontHost.h"
22#include "SkMaskFilter.h"
23#include "SkPathEffect.h"
24#include "SkRasterizer.h"
25#include "SkShader.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000026#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkScalerContext.h"
28#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000029#include "SkTextFormatParams.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkTypeface.h"
31#include "SkXfermode.h"
32#include "SkAutoKern.h"
33
34#define SK_DefaultTextSize SkIntToScalar(12)
35
36#define SK_DefaultFlags 0 //(kNativeHintsText_Flag)
37
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000038#ifdef ANDROID
39#define GEN_ID_INC fGenerationID++
40#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
41#else
42#define GEN_ID_INC
43#define GEN_ID_INC_EVAL(expression)
44#endif
45
reed@android.coma3122b92009-08-13 20:38:25 +000046SkPaint::SkPaint() {
47 // since we may have padding, we zero everything so that our memcmp() call
48 // in operator== will work correctly.
49 // with this, we can skip 0 and null individual initializations
50 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000051
reed@android.coma3122b92009-08-13 20:38:25 +000052#if 0 // not needed with the bzero call above
53 fTypeface = NULL;
54 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 fPathEffect = NULL;
56 fShader = NULL;
57 fXfermode = NULL;
58 fMaskFilter = NULL;
59 fColorFilter = NULL;
60 fRasterizer = NULL;
61 fLooper = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000063#endif
64
65 fTextSize = SK_DefaultTextSize;
66 fTextScaleX = SK_Scalar1;
67 fColor = SK_ColorBLACK;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 fMiterLimit = SK_DefaultMiterLimit;
69 fFlags = SK_DefaultFlags;
70 fCapType = kDefault_Cap;
71 fJoinType = kDefault_Join;
72 fTextAlign = kLeft_Align;
73 fStyle = kFill_Style;
74 fTextEncoding = kUTF8_TextEncoding;
agl@chromium.org309485b2009-07-21 17:41:32 +000075 fHinting = kNormal_Hinting;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000076#ifdef ANDROID
77 fGenerationID = 0;
78#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000079}
80
reed@google.com6fb7e2e2011-02-08 22:22:52 +000081SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 memcpy(this, &src, sizeof(src));
83
reed@google.com82065d62011-02-07 15:30:46 +000084 SkSafeRef(fTypeface);
85 SkSafeRef(fPathEffect);
86 SkSafeRef(fShader);
87 SkSafeRef(fXfermode);
88 SkSafeRef(fMaskFilter);
89 SkSafeRef(fColorFilter);
90 SkSafeRef(fRasterizer);
91 SkSafeRef(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092}
93
reed@google.com6fb7e2e2011-02-08 22:22:52 +000094SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000095 SkSafeUnref(fTypeface);
96 SkSafeUnref(fPathEffect);
97 SkSafeUnref(fShader);
98 SkSafeUnref(fXfermode);
99 SkSafeUnref(fMaskFilter);
100 SkSafeUnref(fColorFilter);
101 SkSafeUnref(fRasterizer);
102 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103}
104
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000105SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 SkASSERT(&src);
107
reed@google.com82065d62011-02-07 15:30:46 +0000108 SkSafeRef(src.fTypeface);
109 SkSafeRef(src.fPathEffect);
110 SkSafeRef(src.fShader);
111 SkSafeRef(src.fXfermode);
112 SkSafeRef(src.fMaskFilter);
113 SkSafeRef(src.fColorFilter);
114 SkSafeRef(src.fRasterizer);
115 SkSafeRef(src.fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116
reed@google.com82065d62011-02-07 15:30:46 +0000117 SkSafeUnref(fTypeface);
118 SkSafeUnref(fPathEffect);
119 SkSafeUnref(fShader);
120 SkSafeUnref(fXfermode);
121 SkSafeUnref(fMaskFilter);
122 SkSafeUnref(fColorFilter);
123 SkSafeUnref(fRasterizer);
124 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000126#ifdef ANDROID
127 uint32_t oldGenerationID = fGenerationID;
128#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 memcpy(this, &src, sizeof(src));
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000130#ifdef ANDROID
131 fGenerationID = oldGenerationID + 1;
132#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133
134 return *this;
135}
136
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000137int operator==(const SkPaint& a, const SkPaint& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 return memcmp(&a, &b, sizeof(a)) == 0;
139}
140
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000141void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkPaint init;
143
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000144#ifdef ANDROID
145 uint32_t oldGenerationID = fGenerationID;
146#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 *this = init;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000148#ifdef ANDROID
149 fGenerationID = oldGenerationID + 1;
150#endif
151}
152
153#ifdef ANDROID
154uint32_t SkPaint::getGenerationID() const {
155 return fGenerationID;
156}
157#endif
158
159void SkPaint::setHinting(Hinting hintingLevel) {
160 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
161 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162}
163
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000164void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000165 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 fFlags = flags;
167}
168
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000169void SkPaint::setAntiAlias(bool doAA) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000170 GEN_ID_INC_EVAL(doAA != isAntiAlias());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
172}
173
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000174void SkPaint::setDither(bool doDither) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000175 GEN_ID_INC_EVAL(doDither != isDither());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
177}
178
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000179void SkPaint::setSubpixelText(bool doSubpixel) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000180 GEN_ID_INC_EVAL(doSubpixel != isSubpixelText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
182}
183
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000184void SkPaint::setLCDRenderText(bool doLCDRender) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000185 GEN_ID_INC_EVAL(doLCDRender != isLCDRenderText());
agl@chromium.org309485b2009-07-21 17:41:32 +0000186 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
187}
188
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000189void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000190 GEN_ID_INC_EVAL(doEmbeddedBitmapText != isEmbeddedBitmapText());
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000191 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
192}
193
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000194void SkPaint::setAutohinted(bool useAutohinter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000195 GEN_ID_INC_EVAL(useAutohinter != isAutohinted());
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000196 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
197}
198
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000199void SkPaint::setLinearText(bool doLinearText) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000200 GEN_ID_INC_EVAL(doLinearText != isLinearText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
202}
203
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000204void SkPaint::setUnderlineText(bool doUnderline) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000205 GEN_ID_INC_EVAL(doUnderline != isUnderlineText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
207}
208
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000209void SkPaint::setStrikeThruText(bool doStrikeThru) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000210 GEN_ID_INC_EVAL(doStrikeThru != isStrikeThruText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
212}
213
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000214void SkPaint::setFakeBoldText(bool doFakeBold) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000215 GEN_ID_INC_EVAL(doFakeBold != isFakeBoldText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
217}
218
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000219void SkPaint::setDevKernText(bool doDevKern) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000220 GEN_ID_INC_EVAL(doDevKern != isDevKernText());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
222}
223
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000224void SkPaint::setFilterBitmap(bool doFilter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000225 GEN_ID_INC_EVAL(doFilter != isFilterBitmap());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
227}
228
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000229void SkPaint::setStyle(Style style) {
230 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000231 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000233 } else {
234 SkDEBUGCODE(SkDebugf("SkPaint::setStyle(%d) out of range\n", style);)
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236}
237
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000238void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000239 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fColor = color;
241}
242
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000243void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000244 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
245 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246}
247
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000248void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000249 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250}
251
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000252void SkPaint::setStrokeWidth(SkScalar width) {
253 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000254 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000256 } else {
257 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");)
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 {
266 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");)
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setStrokeCap(Cap ct) {
271 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000272 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000274 } else {
275 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);)
276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277}
278
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000279void SkPaint::setStrokeJoin(Join jt) {
280 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000281 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000283 } else {
284 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);)
285 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286}
287
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000288///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000290void SkPaint::setTextAlign(Align align) {
291 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000292 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000294 } else {
295 SkDEBUGCODE(SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);)
296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297}
298
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000299void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000300 if (ts > 0) {
301 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303 } else {
304 SkDEBUGCODE(SkDebugf("SkPaint::setTextSize() called with negative value\n");)
305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306}
307
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000308void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000309 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 fTextScaleX = scaleX;
311}
312
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000313void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000314 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fTextSkewX = skewX;
316}
317
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000318void SkPaint::setTextEncoding(TextEncoding encoding) {
319 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000320 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000322 } else {
323 SkDEBUGCODE(SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);)
324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325}
326
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000327///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000329SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000331 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 return font;
333}
334
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000335SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000337 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 return r;
339}
340
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000341SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000343 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 return looper;
345}
346
347///////////////////////////////////////////////////////////////////////////////
348
349#include "SkGlyphCache.h"
350#include "SkUtils.h"
351
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000352static void DetachDescProc(const SkDescriptor* desc, void* context) {
353 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
354}
355
356#ifdef ANDROID
357const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
358 SkGlyphCache* cache;
359 descriptorProc(NULL, DetachDescProc, &cache, true);
360
361 const SkGlyph& glyph = cache->getUnicharMetrics(text);
362
363 SkGlyphCache::AttachCache(cache);
364 return glyph;
365}
366
367const void* SkPaint::findImage(const SkGlyph& glyph) {
368 // See ::detachCache()
369 SkGlyphCache* cache;
370 descriptorProc(NULL, DetachDescProc, &cache, true);
371
372 const void* image = cache->findImage(glyph);
373
374 SkGlyphCache::AttachCache(cache);
375 return image;
376}
377#endif
378
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
380 uint16_t glyphs[]) const {
381 if (byteLength == 0) {
382 return 0;
383 }
reed@google.com72cf4922011-01-04 19:58:20 +0000384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 SkASSERT(textData != NULL);
386
387 if (NULL == glyphs) {
388 switch (this->getTextEncoding()) {
389 case kUTF8_TextEncoding:
390 return SkUTF8_CountUnichars((const char*)textData, byteLength);
391 case kUTF16_TextEncoding:
392 return SkUTF16_CountUnichars((const uint16_t*)textData,
393 byteLength >> 1);
394 case kGlyphID_TextEncoding:
395 return byteLength >> 1;
396 default:
397 SkASSERT(!"unknown text encoding");
398 }
399 return 0;
400 }
reed@google.com72cf4922011-01-04 19:58:20 +0000401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000403
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 // handle this encoding before the setup for the glyphcache
405 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
406 // we want to ignore the low bit of byteLength
407 memcpy(glyphs, textData, byteLength >> 1 << 1);
408 return byteLength >> 1;
409 }
reed@google.com72cf4922011-01-04 19:58:20 +0000410
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 SkAutoGlyphCache autoCache(*this, NULL);
412 SkGlyphCache* cache = autoCache.getCache();
413
414 const char* text = (const char*)textData;
415 const char* stop = text + byteLength;
416 uint16_t* gptr = glyphs;
417
418 switch (this->getTextEncoding()) {
419 case SkPaint::kUTF8_TextEncoding:
420 while (text < stop) {
421 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
422 }
423 break;
424 case SkPaint::kUTF16_TextEncoding: {
425 const uint16_t* text16 = (const uint16_t*)text;
426 const uint16_t* stop16 = (const uint16_t*)stop;
427 while (text16 < stop16) {
428 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
429 }
430 break;
431 }
432 default:
433 SkASSERT(!"unknown text encoding");
434 }
435 return gptr - glyphs;
436}
437
reed@android.coma5dcaf62010-02-05 17:12:32 +0000438bool SkPaint::containsText(const void* textData, size_t byteLength) const {
439 if (0 == byteLength) {
440 return true;
441 }
reed@google.com72cf4922011-01-04 19:58:20 +0000442
reed@android.coma5dcaf62010-02-05 17:12:32 +0000443 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000444
reed@android.coma5dcaf62010-02-05 17:12:32 +0000445 // handle this encoding before the setup for the glyphcache
446 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
447 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
448 size_t count = byteLength >> 1;
449 for (size_t i = 0; i < count; i++) {
450 if (0 == glyphID[i]) {
451 return false;
452 }
453 }
454 return true;
455 }
reed@google.com72cf4922011-01-04 19:58:20 +0000456
reed@android.coma5dcaf62010-02-05 17:12:32 +0000457 SkAutoGlyphCache autoCache(*this, NULL);
458 SkGlyphCache* cache = autoCache.getCache();
459
460 switch (this->getTextEncoding()) {
461 case SkPaint::kUTF8_TextEncoding: {
462 const char* text = static_cast<const char*>(textData);
463 const char* stop = text + byteLength;
464 while (text < stop) {
465 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
466 return false;
467 }
468 }
469 break;
470 }
471 case SkPaint::kUTF16_TextEncoding: {
472 const uint16_t* text = static_cast<const uint16_t*>(textData);
473 const uint16_t* stop = text + (byteLength >> 1);
474 while (text < stop) {
475 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
476 return false;
477 }
478 }
479 break;
480 }
481 default:
482 SkASSERT(!"unknown text encoding");
483 return false;
484 }
485 return true;
486}
487
reed@android.com9d3a9852010-01-08 14:07:42 +0000488void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
489 SkUnichar textData[]) const {
490 if (count <= 0) {
491 return;
492 }
493
494 SkASSERT(glyphs != NULL);
495 SkASSERT(textData != NULL);
496
497 SkAutoGlyphCache autoCache(*this, NULL);
498 SkGlyphCache* cache = autoCache.getCache();
499
500 for (int index = 0; index < count; index++) {
501 textData[index] = cache->glyphToUnichar(glyphs[index]);
502 }
503}
504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505///////////////////////////////////////////////////////////////////////////////
506
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000507static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
508 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 SkASSERT(cache != NULL);
510 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
513}
514
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000515static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
516 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 SkASSERT(cache != NULL);
518 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000519
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
521}
522
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000523static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
524 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 SkASSERT(cache != NULL);
526 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
529}
530
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000531static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
532 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 SkASSERT(cache != NULL);
534 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
537}
538
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000539static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
540 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 SkASSERT(cache != NULL);
542 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000543
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 const uint16_t* ptr = *(const uint16_t**)text;
545 unsigned glyphID = *ptr;
546 ptr += 1;
547 *text = (const char*)ptr;
548 return cache->getGlyphIDMetrics(glyphID);
549}
550
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000551static const SkGlyph& sk_getMetrics_glyph_prev(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 ptr -= 1;
558 unsigned glyphID = *ptr;
559 *text = (const char*)ptr;
560 return cache->getGlyphIDMetrics(glyphID);
561}
562
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000563static const SkGlyph& sk_getAdvance_utf8_next(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 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
569}
570
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000571static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
572 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkASSERT(cache != NULL);
574 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
577}
578
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000579static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
580 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 SkASSERT(cache != NULL);
582 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
585}
586
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000587static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
588 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 SkASSERT(cache != NULL);
590 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
593}
594
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000595static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
596 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 SkASSERT(cache != NULL);
598 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 const uint16_t* ptr = *(const uint16_t**)text;
601 unsigned glyphID = *ptr;
602 ptr += 1;
603 *text = (const char*)ptr;
604 return cache->getGlyphIDAdvance(glyphID);
605}
606
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000607static const SkGlyph& sk_getAdvance_glyph_prev(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 ptr -= 1;
614 unsigned glyphID = *ptr;
615 *text = (const char*)ptr;
616 return cache->getGlyphIDAdvance(glyphID);
617}
618
619SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000620 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
622 sk_getMetrics_utf8_next,
623 sk_getMetrics_utf16_next,
624 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 sk_getMetrics_utf8_prev,
627 sk_getMetrics_utf16_prev,
628 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000629
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 sk_getAdvance_utf8_next,
631 sk_getAdvance_utf16_next,
632 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000633
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 sk_getAdvance_utf8_prev,
635 sk_getAdvance_utf16_prev,
636 sk_getAdvance_glyph_prev
637 };
reed@google.com72cf4922011-01-04 19:58:20 +0000638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 unsigned index = this->getTextEncoding();
640
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000641 if (kBackward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000643 }
644 if (!needFullMetrics && !this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 index += 6;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000646 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647
648 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
649 return gMeasureCacheProcs[index];
650}
651
652///////////////////////////////////////////////////////////////////////////////
653
654static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000655 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 SkASSERT(cache != NULL);
657 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
660}
661
662static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000663 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 SkASSERT(cache != NULL);
665 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
668}
669
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000670static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
671 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 SkASSERT(cache != NULL);
673 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000674
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
676}
677
678static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000679 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 SkASSERT(cache != NULL);
681 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000682
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
684 x, y);
685}
686
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000687static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
688 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 SkASSERT(cache != NULL);
690 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 const uint16_t* ptr = *(const uint16_t**)text;
693 unsigned glyphID = *ptr;
694 ptr += 1;
695 *text = (const char*)ptr;
696 return cache->getGlyphIDMetrics(glyphID);
697}
698
699static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000700 const char** text, SkFixed x, SkFixed y) {
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, x, y);
709}
710
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000711SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 static const SkDrawCacheProc gDrawCacheProcs[] = {
713 sk_getMetrics_utf8_00,
714 sk_getMetrics_utf16_00,
715 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 sk_getMetrics_utf8_xy,
718 sk_getMetrics_utf16_xy,
719 sk_getMetrics_glyph_xy
720 };
reed@google.com72cf4922011-01-04 19:58:20 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000723 if (fFlags & kSubpixelText_Flag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000725 }
reed@google.com72cf4922011-01-04 19:58:20 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
728 return gDrawCacheProcs[index];
729}
730
731///////////////////////////////////////////////////////////////////////////////
732
733class SkAutoRestorePaintTextSizeAndFrame {
734public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000735 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
736 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 fTextSize = paint->getTextSize();
738 fStyle = paint->getStyle();
739 fPaint->setStyle(SkPaint::kFill_Style);
740 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000741
742 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 fPaint->setStyle(fStyle);
744 fPaint->setTextSize(fTextSize);
745 }
reed@google.com72cf4922011-01-04 19:58:20 +0000746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747private:
748 SkPaint* fPaint;
749 SkScalar fTextSize;
750 SkPaint::Style fStyle;
751};
752
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000753static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 bounds->set(SkIntToScalar(g.fLeft),
755 SkIntToScalar(g.fTop),
756 SkIntToScalar(g.fLeft + g.fWidth),
757 SkIntToScalar(g.fTop + g.fHeight));
758}
759
reed@android.come88f5512010-03-19 14:42:28 +0000760// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
761// we don't overflow along the way
762typedef int64_t Sk48Dot16;
763
764#ifdef SK_SCALAR_IS_FLOAT
765 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000766 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000767 }
768#else
769 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
770 // just return the low 32bits
771 return static_cast<SkFixed>(x);
772 }
773#endif
774
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000775static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000776 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 bounds->join(SkIntToScalar(g.fLeft) + sx,
778 SkIntToScalar(g.fTop),
779 SkIntToScalar(g.fLeft + g.fWidth) + sx,
780 SkIntToScalar(g.fTop + g.fHeight));
781}
782
783SkScalar SkPaint::measure_text(SkGlyphCache* cache,
784 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000785 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000787 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000789 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000791 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 return 0;
793 }
794
795 SkMeasureCacheProc glyphCacheProc;
796 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
797 NULL != bounds);
798
799 int n = 1;
800 const char* stop = (const char*)text + byteLength;
801 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000802 // our accumulated fixed-point advances might overflow 16.16, so we use
803 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
804 // very end.
805 Sk48Dot16 x = g->fAdvanceX;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806
807 SkAutoKern autokern;
808
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000809 if (NULL == bounds) {
810 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 int rsb;
812 for (; text < stop; n++) {
813 rsb = g->fRsbDelta;
814 g = &glyphCacheProc(cache, &text);
815 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
816 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000817 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 for (; text < stop; n++) {
819 x += glyphCacheProc(cache, &text).fAdvanceX;
820 }
821 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000822 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000824 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 int rsb;
826 for (; text < stop; n++) {
827 rsb = g->fRsbDelta;
828 g = &glyphCacheProc(cache, &text);
829 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
830 join_bounds(*g, bounds, x);
831 x += g->fAdvanceX;
832 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000833 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 for (; text < stop; n++) {
835 g = &glyphCacheProc(cache, &text);
836 join_bounds(*g, bounds, x);
837 x += g->fAdvanceX;
838 }
839 }
840 }
841 SkASSERT(text == stop);
842
843 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000844 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845}
846
847SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000848 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 const char* text = (const char*)textData;
850 SkASSERT(text != NULL || length == 0);
851
852 SkScalar scale = 0;
853 SkAutoRestorePaintTextSizeAndFrame restore(this);
854
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000855 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 scale = fTextSize / kCanonicalTextSizeForPaths;
857 // this gets restored by restore
858 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
859 }
reed@google.com72cf4922011-01-04 19:58:20 +0000860
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000861 SkMatrix zoomMatrix, *zoomPtr = NULL;
862 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 zoomMatrix.setScale(zoom, zoom);
864 zoomPtr = &zoomMatrix;
865 }
866
867 SkAutoGlyphCache autoCache(*this, zoomPtr);
868 SkGlyphCache* cache = autoCache.getCache();
869
870 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000871
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000872 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 int tempCount;
874
875 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000876 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000878 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
880 bounds->fTop = SkScalarMul(bounds->fTop, scale);
881 bounds->fRight = SkScalarMul(bounds->fRight, scale);
882 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
883 }
884 }
885 }
886 return width;
887}
888
889typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
890
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000891static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 return text < stop;
893}
894
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000895static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 return text > stop;
897}
898
899static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000900 const char** text, size_t length,
901 const char** stop) {
902 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 *stop = *text + length;
904 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000905 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 // text should point to the end of the buffer, and stop to the beginning
907 *stop = *text;
908 *text += length;
909 return backward_textBufferPred;
910 }
911}
912
913size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
914 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000915 TextBufferDirection tbd) const {
916 if (0 == length || 0 >= maxWidth) {
917 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000919 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 return 0;
921 }
922
923 SkASSERT(textD != NULL);
924 const char* text = (const char*)textD;
925
926 SkScalar scale = 0;
927 SkAutoRestorePaintTextSizeAndFrame restore(this);
928
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000929 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +0000931 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 // this gets restored by restore
933 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
934 }
reed@google.com72cf4922011-01-04 19:58:20 +0000935
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 SkAutoGlyphCache autoCache(*this, NULL);
937 SkGlyphCache* cache = autoCache.getCache();
938
939 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
940 const char* stop;
941 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@android.come88f5512010-03-19 14:42:28 +0000942 // use 64bits for our accumulator, to avoid overflowing 16.16
943 Sk48Dot16 max = SkScalarToFixed(maxWidth);
944 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945
946 SkAutoKern autokern;
947
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000948 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000950 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 const char* curr = text;
952 const SkGlyph& g = glyphCacheProc(cache, &text);
953 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000954 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 width -= x;
956 text = curr;
957 break;
958 }
959 rsb = g.fRsbDelta;
960 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000961 } else {
962 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 const char* curr = text;
964 SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000965 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 width -= x;
967 text = curr;
968 break;
969 }
970 }
971 }
reed@google.com72cf4922011-01-04 19:58:20 +0000972
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000973 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +0000974 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000975 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000977 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 *measuredWidth = scalarWidth;
979 }
reed@google.com72cf4922011-01-04 19:58:20 +0000980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 // return the number of bytes measured
982 return (kForward_TextBufferDirection == tbd) ?
983 text - stop + length : stop - text + length;
984}
985
986///////////////////////////////////////////////////////////////////////////////
987
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000988static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
990 return false; // don't detach the cache
991}
992
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000993static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
995}
996
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000997SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 SkScalar scale = 0;
999 SkAutoRestorePaintTextSizeAndFrame restore(this);
1000
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001001 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 scale = fTextSize / kCanonicalTextSizeForPaths;
1003 // this gets restored by restore
1004 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1005 }
reed@google.com72cf4922011-01-04 19:58:20 +00001006
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001007 SkMatrix zoomMatrix, *zoomPtr = NULL;
1008 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 zoomMatrix.setScale(zoom, zoom);
1010 zoomPtr = &zoomMatrix;
1011 }
1012
1013#if 0
1014 SkAutoGlyphCache autoCache(*this, zoomPtr);
1015 SkGlyphCache* cache = autoCache.getCache();
1016 const FontMetrics& my = cache->getFontMetricsY();
1017#endif
1018 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001019 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001021 }
reed@google.com72cf4922011-01-04 19:58:20 +00001022
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
1024
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001025 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1027 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1028 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1029 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1030 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
1031 }
1032 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1033}
1034
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001035///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001037static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 bounds->set(g.fLeft * scale,
1039 g.fTop * scale,
1040 (g.fLeft + g.fWidth) * scale,
1041 (g.fTop + g.fHeight) * scale);
1042}
1043
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001044int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1045 SkScalar widths[], SkRect bounds[]) const {
1046 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001048 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049
1050 SkASSERT(NULL != textData);
1051
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001052 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001054 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055
1056 SkAutoRestorePaintTextSizeAndFrame restore(this);
1057 SkScalar scale = 0;
1058
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001059 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 scale = fTextSize / kCanonicalTextSizeForPaths;
1061 // this gets restored by restore
1062 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1063 }
1064
1065 SkAutoGlyphCache autoCache(*this, NULL);
1066 SkGlyphCache* cache = autoCache.getCache();
1067 SkMeasureCacheProc glyphCacheProc;
1068 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1069 NULL != bounds);
1070
1071 const char* text = (const char*)textData;
1072 const char* stop = text + byteLength;
1073 int count = 0;
1074
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001075 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 // we adjust the widths returned here through auto-kerning
1077 SkAutoKern autokern;
1078 SkFixed prevWidth = 0;
1079
1080 if (scale) {
1081 while (text < stop) {
1082 const SkGlyph& g = glyphCacheProc(cache, &text);
1083 if (widths) {
1084 SkFixed adjust = autokern.adjust(g);
1085
1086 if (count > 0) {
1087 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1088 *widths++ = SkScalarMul(w, scale);
1089 }
1090 prevWidth = g.fAdvanceX;
1091 }
1092 if (bounds) {
1093 set_bounds(g, bounds++, scale);
1094 }
1095 ++count;
1096 }
1097 if (count > 0 && widths) {
1098 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1099 }
1100 } else {
1101 while (text < stop) {
1102 const SkGlyph& g = glyphCacheProc(cache, &text);
1103 if (widths) {
1104 SkFixed adjust = autokern.adjust(g);
1105
1106 if (count > 0) {
1107 *widths++ = SkFixedToScalar(prevWidth + adjust);
1108 }
1109 prevWidth = g.fAdvanceX;
1110 }
1111 if (bounds) {
1112 set_bounds(g, bounds++);
1113 }
1114 ++count;
1115 }
1116 if (count > 0 && widths) {
1117 *widths = SkFixedToScalar(prevWidth);
1118 }
1119 }
1120 } else { // no devkern
1121 if (scale) {
1122 while (text < stop) {
1123 const SkGlyph& g = glyphCacheProc(cache, &text);
1124 if (widths) {
1125 *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
1126 scale);
1127 }
1128 if (bounds) {
1129 set_bounds(g, bounds++, scale);
1130 }
1131 ++count;
1132 }
1133 } else {
1134 while (text < stop) {
1135 const SkGlyph& g = glyphCacheProc(cache, &text);
1136 if (widths) {
1137 *widths++ = SkFixedToScalar(g.fAdvanceX);
1138 }
1139 if (bounds) {
1140 set_bounds(g, bounds++);
1141 }
1142 ++count;
1143 }
1144 }
1145 }
1146
1147 SkASSERT(text == stop);
1148 return count;
1149}
1150
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001151///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152
1153#include "SkDraw.h"
1154
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001155void SkPaint::getTextPath(const void* textData, size_t length,
1156 SkScalar x, SkScalar y, SkPath* path) const {
1157 SkASSERT(length == 0 || textData != NULL);
1158
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001160 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001162 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163
1164 SkTextToPathIter iter(text, length, *this, false, true);
1165 SkMatrix matrix;
1166 SkScalar prevXPos = 0;
1167
1168 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1169 matrix.postTranslate(x, y);
1170 path->reset();
1171
1172 SkScalar xpos;
1173 const SkPath* iterPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001174 while ((iterPath = iter.next(&xpos)) != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 matrix.postTranslate(xpos - prevXPos, 0);
1176 path->addPath(*iterPath, matrix);
1177 prevXPos = xpos;
1178 }
1179}
1180
1181static void add_flattenable(SkDescriptor* desc, uint32_t tag,
1182 SkFlattenableWriteBuffer* buffer) {
1183 buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
1184}
1185
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001186static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001189 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001190 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001191 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001192 }
1193
reed@android.comf5493692009-07-22 19:21:01 +00001194#if defined(SK_SUPPORT_LCDTEXT)
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001195 if (flags & SkPaint::kLCDRenderText_Flag) {
agl@chromium.org309485b2009-07-21 17:41:32 +00001196 return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ?
1197 SkMask::kHorizontalLCD_Format : SkMask::kVerticalLCD_Format;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001198 }
reed@google.com65dd8f82011-03-14 13:31:16 +00001199#else
1200 if (flags & SkPaint::kLCDRenderText_Flag) {
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001201#if !defined(SK_SUPPORT_888_TEXT)
reed@google.comf67e4cf2011-03-15 20:56:58 +00001202 return SkMask::kLCD16_Format;
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001203#else
1204 return SkMask::kLCD32_Format;
1205#endif
reed@google.com65dd8f82011-03-14 13:31:16 +00001206 }
agl@chromium.org309485b2009-07-21 17:41:32 +00001207#endif
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001208
1209 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210}
1211
reed@android.com1cdcb512009-08-24 19:11:00 +00001212// if linear-text is on, then we force hinting to be off (since that's sort of
1213// the point of linear-text.
1214static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1215 SkPaint::Hinting h = paint.getHinting();
1216 if (paint.isLinearText()) {
1217 h = SkPaint::kNo_Hinting;
1218 }
1219 return h;
1220}
1221
reed@google.com72cf4922011-01-04 19:58:20 +00001222/*
1223 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1224 * that vary only slightly when we create our key into the font cache, since the font scaler
1225 * typically returns the same looking resuts for tiny changes in the matrix.
1226 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001227static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001228#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001229 int n = sk_float_round2int(x * 1024);
1230 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001231#else
1232 // round to the nearest 10 fractional bits
1233 return (x + (1 << 5)) & ~(1024 - 1);
1234#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001235}
1236
reed@android.com36a4c2a2009-07-22 19:52:11 +00001237void SkScalerContext::MakeRec(const SkPaint& paint,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001238 const SkMatrix* deviceMatrix, Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001239 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240
reed@google.com7d26c592011-06-13 13:01:10 +00001241 rec->fOrigFontID = SkTypeface::UniqueID(paint.getTypeface());
1242 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 rec->fTextSize = paint.getTextSize();
1244 rec->fPreScaleX = paint.getTextScaleX();
1245 rec->fPreSkewX = paint.getTextSkewX();
1246
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001247 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001248 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1249 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1250 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1251 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001252 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1254 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1255 }
reed@google.com72cf4922011-01-04 19:58:20 +00001256
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 SkPaint::Style style = paint.getStyle();
1258 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001259
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001260 unsigned flags = SkFontHost::ComputeGammaFlag(paint);
1261
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001262 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001263#ifdef SK_USE_FREETYPE_EMBOLDEN
1264 flags |= SkScalerContext::kEmbolden_Flag;
1265#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001266 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1267 kStdFakeBoldInterpKeys,
1268 kStdFakeBoldInterpValues,
1269 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001271
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001272 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 style = SkPaint::kStrokeAndFill_Style;
1274 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001275 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001277 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001278#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 }
1280
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001281 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001283 }
reed@google.com72cf4922011-01-04 19:58:20 +00001284
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001285 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 rec->fFrameWidth = strokeWidth;
1287 rec->fMiterLimit = paint.getStrokeMiter();
1288 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1289
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001290 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001292 }
1293 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 rec->fFrameWidth = 0;
1295 rec->fMiterLimit = 0;
1296 rec->fStrokeJoin = 0;
1297 }
1298
reed@google.com02b53312011-05-18 19:00:53 +00001299 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1300
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001301 if (SkMask::kLCD16_Format == rec->fMaskFormat ||
1302 SkMask::kLCD32_Format == rec->fMaskFormat)
1303 {
reed@google.com02b53312011-05-18 19:00:53 +00001304 SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
1305 SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
1306 if (SkFontHost::kNONE_LCDOrder == order) {
1307 // eeek, can't support LCD
1308 rec->fMaskFormat = SkMask::kA8_Format;
1309 } else {
1310 if (SkFontHost::kVertical_LCDOrientation == orient) {
1311 flags |= SkScalerContext::kLCD_Vertical_Flag;
1312 }
1313 if (SkFontHost::kBGR_LCDOrder == order) {
1314 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1315 }
1316 }
1317 }
1318
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001319 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001320 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001321 }
1322 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001323 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001324 }
1325 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001326 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001327 }
reed@google.com02b53312011-05-18 19:00:53 +00001328 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001329
reed@google.com9d757672011-05-18 21:14:39 +00001330 // setHinting modifies fFlags, so do this last
1331 rec->setHinting(computeHinting(paint));
1332
reed@android.com36a4c2a2009-07-22 19:52:11 +00001333 /* Allow the fonthost to modify our rec before we use it as a key into the
1334 cache. This way if we're asking for something that they will ignore,
1335 they can modify our rec up front, so we don't create duplicate cache
1336 entries.
1337 */
1338 SkFontHost::FilterRec(rec);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339}
1340
1341#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1342
reed@google.com17fb3872011-05-04 14:31:07 +00001343#ifdef SK_DEBUG
1344 #define TEST_DESC
1345#endif
1346
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
1348 void (*proc)(const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001349 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350 SkScalerContext::Rec rec;
1351
1352 SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001353 if (ignoreGamma) {
1354 rec.fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
1355 SkScalerContext::kGammaForWhite_Flag);
1356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357
1358 size_t descSize = sizeof(rec);
1359 int entryCount = 1;
1360 SkPathEffect* pe = this->getPathEffect();
1361 SkMaskFilter* mf = this->getMaskFilter();
1362 SkRasterizer* ra = this->getRasterizer();
1363
1364 SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1365 SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1366 SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1367
1368 if (pe) {
1369 peBuffer.writeFlattenable(pe);
1370 descSize += peBuffer.size();
1371 entryCount += 1;
1372 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1373 // seems like we could support kLCD as well at this point...
1374 }
1375 if (mf) {
1376 mfBuffer.writeFlattenable(mf);
1377 descSize += mfBuffer.size();
1378 entryCount += 1;
1379 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1380 }
1381 if (ra) {
1382 raBuffer.writeFlattenable(ra);
1383 descSize += raBuffer.size();
1384 entryCount += 1;
1385 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1386 }
1387 descSize += SkDescriptor::ComputeOverhead(entryCount);
1388
1389 SkAutoDescriptor ad(descSize);
1390 SkDescriptor* desc = ad.getDesc();
1391
1392 desc->init();
1393 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1394
1395 if (pe) {
1396 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1397 }
1398 if (mf) {
1399 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1400 }
1401 if (ra) {
1402 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1403 }
1404
1405 SkASSERT(descSize == desc->getLength());
1406 desc->computeChecksum();
1407
reed@google.com17fb3872011-05-04 14:31:07 +00001408#ifdef TEST_DESC
1409 {
1410 // Check that we completely write the bytes in desc (our key), and that
1411 // there are no uninitialized bytes. If there were, then we would get
1412 // false-misses (or worse, false-hits) in our fontcache.
1413 //
1414 // We do this buy filling 2 others, one with 0s and the other with 1s
1415 // and create those, and then check that all 3 are identical.
1416 SkAutoDescriptor ad1(descSize);
1417 SkAutoDescriptor ad2(descSize);
1418 SkDescriptor* desc1 = ad1.getDesc();
1419 SkDescriptor* desc2 = ad2.getDesc();
1420
1421 memset(desc1, 0x00, descSize);
1422 memset(desc2, 0xFF, descSize);
1423
1424 desc1->init();
1425 desc2->init();
1426 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1427 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1428
1429 if (pe) {
1430 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1431 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1432 }
1433 if (mf) {
1434 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1435 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1436 }
1437 if (ra) {
1438 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1439 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1440 }
1441
1442 SkASSERT(descSize == desc1->getLength());
1443 SkASSERT(descSize == desc2->getLength());
1444 desc1->computeChecksum();
1445 desc2->computeChecksum();
1446 SkASSERT(!memcmp(desc, desc1, descSize));
1447 SkASSERT(!memcmp(desc, desc2, descSize));
1448 }
1449#endif
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 proc(desc, context);
1452}
1453
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001454SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 SkGlyphCache* cache;
1456 this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
1457 return cache;
1458}
1459
1460///////////////////////////////////////////////////////////////////////////////
1461
1462#include "SkStream.h"
1463
reed@android.comaefd2bc2009-03-30 21:02:14 +00001464static uintptr_t asint(const void* p) {
1465 return reinterpret_cast<uintptr_t>(p);
1466}
1467
1468union Scalar32 {
1469 SkScalar fScalar;
1470 uint32_t f32;
1471};
1472
1473static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1474 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1475 Scalar32 tmp;
1476 tmp.fScalar = value;
1477 *ptr = tmp.f32;
1478 return ptr + 1;
1479}
1480
1481static SkScalar read_scalar(const uint32_t*& ptr) {
1482 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1483 Scalar32 tmp;
1484 tmp.f32 = *ptr++;
1485 return tmp.fScalar;
1486}
1487
1488static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1489 SkASSERT(a == (uint8_t)a);
1490 SkASSERT(b == (uint8_t)b);
1491 SkASSERT(c == (uint8_t)c);
1492 SkASSERT(d == (uint8_t)d);
1493 return (a << 24) | (b << 16) | (c << 8) | d;
1494}
1495
1496enum FlatFlags {
1497 kHasTypeface_FlatFlag = 0x01,
1498 kHasEffects_FlatFlag = 0x02
1499};
1500
1501// The size of a flat paint's POD fields
1502static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
1503 1 * sizeof(SkColor) +
1504 1 * sizeof(uint16_t) +
1505 6 * sizeof(uint8_t);
1506
1507/* To save space/time, we analyze the paint, and write a truncated version of
1508 it if there are not tricky elements like shaders, etc.
1509 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001511 uint8_t flatFlags = 0;
1512 if (this->getTypeface()) {
1513 flatFlags |= kHasTypeface_FlatFlag;
1514 }
1515 if (asint(this->getPathEffect()) |
1516 asint(this->getShader()) |
1517 asint(this->getXfermode()) |
1518 asint(this->getMaskFilter()) |
1519 asint(this->getColorFilter()) |
1520 asint(this->getRasterizer()) |
1521 asint(this->getLooper())) {
1522 flatFlags |= kHasEffects_FlatFlag;
1523 }
reed@google.com72cf4922011-01-04 19:58:20 +00001524
reed@android.comaefd2bc2009-03-30 21:02:14 +00001525 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1526 uint32_t* ptr = buffer.reserve(kPODPaintSize);
1527
1528 ptr = write_scalar(ptr, this->getTextSize());
1529 ptr = write_scalar(ptr, this->getTextScaleX());
1530 ptr = write_scalar(ptr, this->getTextSkewX());
1531 ptr = write_scalar(ptr, this->getStrokeWidth());
1532 ptr = write_scalar(ptr, this->getStrokeMiter());
1533 *ptr++ = this->getColor();
1534 *ptr++ = (this->getFlags() << 16) | (this->getTextAlign() << 8) | flatFlags;
1535 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1536 this->getStyle(), this->getTextEncoding());
1537
1538 // now we're done with ptr and the (pre)reserved space. If we need to write
1539 // additional fields, use the buffer directly
1540 if (flatFlags & kHasTypeface_FlatFlag) {
1541 buffer.writeTypeface(this->getTypeface());
1542 }
1543 if (flatFlags & kHasEffects_FlatFlag) {
1544 buffer.writeFlattenable(this->getPathEffect());
1545 buffer.writeFlattenable(this->getShader());
1546 buffer.writeFlattenable(this->getXfermode());
1547 buffer.writeFlattenable(this->getMaskFilter());
1548 buffer.writeFlattenable(this->getColorFilter());
1549 buffer.writeFlattenable(this->getRasterizer());
1550 buffer.writeFlattenable(this->getLooper());
1551 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552}
1553
1554void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001555 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1556 const void* podData = buffer.skip(kPODPaintSize);
1557 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@google.com72cf4922011-01-04 19:58:20 +00001558
reed@android.comaefd2bc2009-03-30 21:02:14 +00001559 // the order we read must match the order we wrote in flatten()
1560 this->setTextSize(read_scalar(pod));
1561 this->setTextScaleX(read_scalar(pod));
1562 this->setTextSkewX(read_scalar(pod));
1563 this->setStrokeWidth(read_scalar(pod));
reed@google.com72cf4922011-01-04 19:58:20 +00001564 this->setStrokeMiter(read_scalar(pod));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001565 this->setColor(*pod++);
1566
1567 uint32_t tmp = *pod++;
1568 this->setFlags(tmp >> 16);
1569 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xFF));
1570 uint8_t flatFlags = tmp & 0xFF;
1571
1572 tmp = *pod++;
1573 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1574 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1575 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1576 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
1577
1578 if (flatFlags & kHasTypeface_FlatFlag) {
1579 this->setTypeface(buffer.readTypeface());
1580 } else {
1581 this->setTypeface(NULL);
1582 }
1583
1584 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com82065d62011-02-07 15:30:46 +00001585 SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable()));
1586 SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable()));
1587 SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable()));
1588 SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable()));
1589 SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable()));
1590 SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable()));
1591 SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001592 } else {
1593 this->setPathEffect(NULL);
1594 this->setShader(NULL);
1595 this->setXfermode(NULL);
1596 this->setMaskFilter(NULL);
1597 this->setColorFilter(NULL);
1598 this->setRasterizer(NULL);
1599 this->setLooper(NULL);
1600 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601}
1602
1603///////////////////////////////////////////////////////////////////////////////
1604
reed@google.com82065d62011-02-07 15:30:46 +00001605SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001606 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 SkRefCnt_SafeAssign(fShader, shader);
1608 return shader;
1609}
1610
reed@google.com82065d62011-02-07 15:30:46 +00001611SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001612 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 SkRefCnt_SafeAssign(fColorFilter, filter);
1614 return filter;
1615}
1616
reed@google.com82065d62011-02-07 15:30:46 +00001617SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001618 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 SkRefCnt_SafeAssign(fXfermode, mode);
1620 return mode;
1621}
1622
reed@android.com0baf1932009-06-24 12:41:42 +00001623SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00001624 SkSafeUnref(fXfermode);
1625 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001626 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00001627 return fXfermode;
1628}
1629
reed@google.com82065d62011-02-07 15:30:46 +00001630SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001631 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 SkRefCnt_SafeAssign(fPathEffect, effect);
1633 return effect;
1634}
1635
reed@google.com82065d62011-02-07 15:30:46 +00001636SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001637 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 SkRefCnt_SafeAssign(fMaskFilter, filter);
1639 return filter;
1640}
1641
reed@google.com82065d62011-02-07 15:30:46 +00001642///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001644bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 SkPath effectPath, strokePath;
1646 const SkPath* path = &src;
1647
1648 SkScalar width = this->getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001649
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650 switch (this->getStyle()) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001651 case SkPaint::kFill_Style:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001653 break;
1654 case SkPaint::kStrokeAndFill_Style:
1655 if (width == 0) {
1656 width = -1; // mark it as no-stroke
1657 }
1658 break;
1659 case SkPaint::kStroke_Style:
1660 break;
1661 default:
1662 SkASSERT(!"unknown paint style");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 }
1664
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001665 if (this->getPathEffect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001666 // 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 +00001667 if (this->getStyle() == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001669 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001671 if (this->getPathEffect()->filterPath(&effectPath, src, &width)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672 path = &effectPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001673 }
reed@google.com72cf4922011-01-04 19:58:20 +00001674
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675 // restore the width if we earlier had to lie, and if we're still set to no-stroke
1676 // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
1677 // and we want to respect that (i.e. don't overwrite their setting for width)
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001678 if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001679 width = this->getStrokeWidth();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001680 if (width == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681 width = -1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001682 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683 }
1684 }
reed@google.com72cf4922011-01-04 19:58:20 +00001685
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001686 if (width > 0 && !path->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687 SkStroke stroker(*this, width);
1688 stroker.strokePath(*path, &strokePath);
1689 path = &strokePath;
1690 }
1691
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001692 if (path == &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693 *dst = src;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001694 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 SkASSERT(path == &effectPath || path == &strokePath);
1696 dst->swap(*(SkPath*)path);
1697 }
1698
1699 return width != 0; // return true if we're filled, or false if we're hairline (width == 0)
1700}
1701
reed@android.comd252db02009-04-01 18:31:44 +00001702const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
1703 SkRect* storage) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00001705 SkASSERT(this->getStyle() != SkPaint::kFill_Style);
1706
1707 // since we're stroked, outset the rect by the radius (and join type)
1708 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
1709 if (0 == radius) { // hairline
1710 radius = SK_Scalar1;
1711 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
1712 SkScalar scale = this->getStrokeMiter();
1713 if (scale > SK_Scalar1) {
1714 radius = SkScalarMul(radius, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 }
reed@android.comd252db02009-04-01 18:31:44 +00001717 storage->set(src.fLeft - radius, src.fTop - radius,
1718 src.fRight + radius, src.fBottom + radius);
1719 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720}
1721
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001722///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001724static bool has_thick_frame(const SkPaint& paint) {
1725 return paint.getStrokeWidth() > 0 &&
1726 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727}
1728
1729SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
1730 const SkPaint& paint,
1731 bool applyStrokeAndPathEffects,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001732 bool forceLinearTextOn) : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
1734 true);
1735
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001736 if (forceLinearTextOn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 fPaint.setLinearText(true);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001738 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
1740
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001741 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001743 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744
1745 // can't use our canonical size if we need to apply patheffects/strokes
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001746 if (fPaint.isLinearText() && !applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
1748 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001749 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001751 }
reed@google.com72cf4922011-01-04 19:58:20 +00001752
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001753 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 fPaint.setStyle(SkPaint::kFill_Style);
1755 fPaint.setPathEffect(NULL);
1756 }
1757
1758 fCache = fPaint.detachCache(NULL);
1759
1760 SkPaint::Style style = SkPaint::kFill_Style;
1761 SkPathEffect* pe = NULL;
1762
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001763 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 style = paint.getStyle(); // restore
1765 pe = paint.getPathEffect(); // restore
1766 }
1767 fPaint.setStyle(style);
1768 fPaint.setPathEffect(pe);
1769 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
1770
1771 // now compute fXOffset if needed
1772
1773 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001774 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001776 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
1777 &count, NULL), fScale);
1778 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001780 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 xOffset = -width;
1782 }
1783 fXPos = xOffset;
1784 fPrevAdvance = 0;
1785
1786 fText = text;
1787 fStop = text + length;
1788}
1789
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001790SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791 SkGlyphCache::AttachCache(fCache);
1792}
1793
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001794const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
1795 while (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
1797
1798 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
1799 fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking();
1800
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001801 if (glyph.fWidth) {
1802 if (xpos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 *xpos = fXPos;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001804 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 return fCache->findPath(glyph);
1806 }
1807 }
1808 return NULL;
1809}