blob: 55f16af6709c572dd98e4908cab0ca8310859cb5 [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
reed@android.coma3122b92009-08-13 20:38:25 +000038SkPaint::SkPaint() {
39 // since we may have padding, we zero everything so that our memcmp() call
40 // in operator== will work correctly.
41 // with this, we can skip 0 and null individual initializations
42 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000043
reed@android.coma3122b92009-08-13 20:38:25 +000044#if 0 // not needed with the bzero call above
45 fTypeface = NULL;
46 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 fPathEffect = NULL;
48 fShader = NULL;
49 fXfermode = NULL;
50 fMaskFilter = NULL;
51 fColorFilter = NULL;
52 fRasterizer = NULL;
53 fLooper = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000055#endif
56
57 fTextSize = SK_DefaultTextSize;
58 fTextScaleX = SK_Scalar1;
59 fColor = SK_ColorBLACK;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 fMiterLimit = SK_DefaultMiterLimit;
61 fFlags = SK_DefaultFlags;
62 fCapType = kDefault_Cap;
63 fJoinType = kDefault_Join;
64 fTextAlign = kLeft_Align;
65 fStyle = kFill_Style;
66 fTextEncoding = kUTF8_TextEncoding;
agl@chromium.org309485b2009-07-21 17:41:32 +000067 fHinting = kNormal_Hinting;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068}
69
reed@google.com6fb7e2e2011-02-08 22:22:52 +000070SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 memcpy(this, &src, sizeof(src));
72
reed@google.com82065d62011-02-07 15:30:46 +000073 SkSafeRef(fTypeface);
74 SkSafeRef(fPathEffect);
75 SkSafeRef(fShader);
76 SkSafeRef(fXfermode);
77 SkSafeRef(fMaskFilter);
78 SkSafeRef(fColorFilter);
79 SkSafeRef(fRasterizer);
80 SkSafeRef(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000081}
82
reed@google.com6fb7e2e2011-02-08 22:22:52 +000083SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000084 SkSafeUnref(fTypeface);
85 SkSafeUnref(fPathEffect);
86 SkSafeUnref(fShader);
87 SkSafeUnref(fXfermode);
88 SkSafeUnref(fMaskFilter);
89 SkSafeUnref(fColorFilter);
90 SkSafeUnref(fRasterizer);
91 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092}
93
reed@google.com6fb7e2e2011-02-08 22:22:52 +000094SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 SkASSERT(&src);
96
reed@google.com82065d62011-02-07 15:30:46 +000097 SkSafeRef(src.fTypeface);
98 SkSafeRef(src.fPathEffect);
99 SkSafeRef(src.fShader);
100 SkSafeRef(src.fXfermode);
101 SkSafeRef(src.fMaskFilter);
102 SkSafeRef(src.fColorFilter);
103 SkSafeRef(src.fRasterizer);
104 SkSafeRef(src.fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105
reed@google.com82065d62011-02-07 15:30:46 +0000106 SkSafeUnref(fTypeface);
107 SkSafeUnref(fPathEffect);
108 SkSafeUnref(fShader);
109 SkSafeUnref(fXfermode);
110 SkSafeUnref(fMaskFilter);
111 SkSafeUnref(fColorFilter);
112 SkSafeUnref(fRasterizer);
113 SkSafeUnref(fLooper);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
115 memcpy(this, &src, sizeof(src));
116
117 return *this;
118}
119
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000120int operator==(const SkPaint& a, const SkPaint& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 return memcmp(&a, &b, sizeof(a)) == 0;
122}
123
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000124void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkPaint init;
126
127 *this = init;
128}
129
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000130void SkPaint::setFlags(uint32_t flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 fFlags = flags;
132}
133
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000134void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
136}
137
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000138void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
140}
141
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000142void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
144}
145
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000146void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000147 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
148}
149
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000150void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000151 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
152}
153
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000154void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000155 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
156}
157
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000158void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
160}
161
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000162void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
164}
165
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000166void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
168}
169
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000170void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
172}
173
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000174void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
176}
177
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000178void SkPaint::setFilterBitmap(bool doFilter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
180}
181
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000182void SkPaint::setStyle(Style style) {
183 if ((unsigned)style < kStyleCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 fStyle = style;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000185 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186#ifdef SK_DEBUG
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000187 else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190#endif
191}
192
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000193void SkPaint::setColor(SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 fColor = color;
195}
196
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000197void SkPaint::setAlpha(U8CPU a) {
198 fColor = SkColorSetARGB(a, SkColorGetR(fColor),
199 SkColorGetG(fColor), SkColorGetB(fColor));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200}
201
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000202void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 fColor = SkColorSetARGB(a, r, g, b);
204}
205
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000206void SkPaint::setStrokeWidth(SkScalar width) {
207 if (width >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 fWidth = width;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210#ifdef SK_DEBUG
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000211 else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214#endif
215}
216
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000217void SkPaint::setStrokeMiter(SkScalar limit) {
218 if (limit >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fMiterLimit = limit;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000220 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221#ifdef SK_DEBUG
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000222 else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225#endif
226}
227
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000228void SkPaint::setStrokeCap(Cap ct) {
229 if ((unsigned)ct < kCapCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000231 } else {
232 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);)
233 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234}
235
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000236void SkPaint::setStrokeJoin(Join jt) {
237 if ((unsigned)jt < kJoinCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000239 } else {
240 SkDEBUGCODE(SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);)
241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242}
243
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000244///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000246void SkPaint::setTextAlign(Align align) {
247 if ((unsigned)align < kAlignCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000249 } else {
250 SkDEBUGCODE(SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);)
251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252}
253
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000254void SkPaint::setTextSize(SkScalar ts) {
255 if (ts >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000257 } else {
258 SkDEBUGCODE(SkDebugf("SkPaint::setTextSize() called with negative value\n");)
259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setTextScaleX(SkScalar scaleX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 fTextScaleX = scaleX;
264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setTextSkewX(SkScalar skewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 fTextSkewX = skewX;
268}
269
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000270void SkPaint::setTextEncoding(TextEncoding encoding) {
271 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000273 } else {
274 SkDEBUGCODE(SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);)
275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276}
277
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000278///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000280SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 SkRefCnt_SafeAssign(fTypeface, font);
282 return font;
283}
284
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000285SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 SkRefCnt_SafeAssign(fRasterizer, r);
287 return r;
288}
289
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000290SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 SkRefCnt_SafeAssign(fLooper, looper);
292 return looper;
293}
294
295///////////////////////////////////////////////////////////////////////////////
296
297#include "SkGlyphCache.h"
298#include "SkUtils.h"
299
300int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
301 uint16_t glyphs[]) const {
302 if (byteLength == 0) {
303 return 0;
304 }
reed@google.com72cf4922011-01-04 19:58:20 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 SkASSERT(textData != NULL);
307
308 if (NULL == glyphs) {
309 switch (this->getTextEncoding()) {
310 case kUTF8_TextEncoding:
311 return SkUTF8_CountUnichars((const char*)textData, byteLength);
312 case kUTF16_TextEncoding:
313 return SkUTF16_CountUnichars((const uint16_t*)textData,
314 byteLength >> 1);
315 case kGlyphID_TextEncoding:
316 return byteLength >> 1;
317 default:
318 SkASSERT(!"unknown text encoding");
319 }
320 return 0;
321 }
reed@google.com72cf4922011-01-04 19:58:20 +0000322
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 // handle this encoding before the setup for the glyphcache
326 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
327 // we want to ignore the low bit of byteLength
328 memcpy(glyphs, textData, byteLength >> 1 << 1);
329 return byteLength >> 1;
330 }
reed@google.com72cf4922011-01-04 19:58:20 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 SkAutoGlyphCache autoCache(*this, NULL);
333 SkGlyphCache* cache = autoCache.getCache();
334
335 const char* text = (const char*)textData;
336 const char* stop = text + byteLength;
337 uint16_t* gptr = glyphs;
338
339 switch (this->getTextEncoding()) {
340 case SkPaint::kUTF8_TextEncoding:
341 while (text < stop) {
342 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
343 }
344 break;
345 case SkPaint::kUTF16_TextEncoding: {
346 const uint16_t* text16 = (const uint16_t*)text;
347 const uint16_t* stop16 = (const uint16_t*)stop;
348 while (text16 < stop16) {
349 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
350 }
351 break;
352 }
353 default:
354 SkASSERT(!"unknown text encoding");
355 }
356 return gptr - glyphs;
357}
358
reed@android.coma5dcaf62010-02-05 17:12:32 +0000359bool SkPaint::containsText(const void* textData, size_t byteLength) const {
360 if (0 == byteLength) {
361 return true;
362 }
reed@google.com72cf4922011-01-04 19:58:20 +0000363
reed@android.coma5dcaf62010-02-05 17:12:32 +0000364 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000365
reed@android.coma5dcaf62010-02-05 17:12:32 +0000366 // handle this encoding before the setup for the glyphcache
367 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
368 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
369 size_t count = byteLength >> 1;
370 for (size_t i = 0; i < count; i++) {
371 if (0 == glyphID[i]) {
372 return false;
373 }
374 }
375 return true;
376 }
reed@google.com72cf4922011-01-04 19:58:20 +0000377
reed@android.coma5dcaf62010-02-05 17:12:32 +0000378 SkAutoGlyphCache autoCache(*this, NULL);
379 SkGlyphCache* cache = autoCache.getCache();
380
381 switch (this->getTextEncoding()) {
382 case SkPaint::kUTF8_TextEncoding: {
383 const char* text = static_cast<const char*>(textData);
384 const char* stop = text + byteLength;
385 while (text < stop) {
386 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
387 return false;
388 }
389 }
390 break;
391 }
392 case SkPaint::kUTF16_TextEncoding: {
393 const uint16_t* text = static_cast<const uint16_t*>(textData);
394 const uint16_t* stop = text + (byteLength >> 1);
395 while (text < stop) {
396 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
397 return false;
398 }
399 }
400 break;
401 }
402 default:
403 SkASSERT(!"unknown text encoding");
404 return false;
405 }
406 return true;
407}
408
reed@android.com9d3a9852010-01-08 14:07:42 +0000409void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
410 SkUnichar textData[]) const {
411 if (count <= 0) {
412 return;
413 }
414
415 SkASSERT(glyphs != NULL);
416 SkASSERT(textData != NULL);
417
418 SkAutoGlyphCache autoCache(*this, NULL);
419 SkGlyphCache* cache = autoCache.getCache();
420
421 for (int index = 0; index < count; index++) {
422 textData[index] = cache->glyphToUnichar(glyphs[index]);
423 }
424}
425
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426///////////////////////////////////////////////////////////////////////////////
427
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000428static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
429 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 SkASSERT(cache != NULL);
431 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000432
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
434}
435
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000436static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
437 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkASSERT(cache != NULL);
439 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
442}
443
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000444static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
445 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 SkASSERT(cache != NULL);
447 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
450}
451
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000452static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
453 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 SkASSERT(cache != NULL);
455 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000456
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
458}
459
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000460static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
461 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkASSERT(cache != NULL);
463 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 const uint16_t* ptr = *(const uint16_t**)text;
466 unsigned glyphID = *ptr;
467 ptr += 1;
468 *text = (const char*)ptr;
469 return cache->getGlyphIDMetrics(glyphID);
470}
471
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000472static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
473 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 SkASSERT(cache != NULL);
475 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 const uint16_t* ptr = *(const uint16_t**)text;
478 ptr -= 1;
479 unsigned glyphID = *ptr;
480 *text = (const char*)ptr;
481 return cache->getGlyphIDMetrics(glyphID);
482}
483
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000484static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
485 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 SkASSERT(cache != NULL);
487 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
490}
491
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000492static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
493 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 SkASSERT(cache != NULL);
495 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
498}
499
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000500static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
501 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 SkASSERT(cache != NULL);
503 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
506}
507
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000508static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
509 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkASSERT(cache != NULL);
511 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000512
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
514}
515
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000516static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
517 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 SkASSERT(cache != NULL);
519 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 const uint16_t* ptr = *(const uint16_t**)text;
522 unsigned glyphID = *ptr;
523 ptr += 1;
524 *text = (const char*)ptr;
525 return cache->getGlyphIDAdvance(glyphID);
526}
527
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000528static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
529 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 SkASSERT(cache != NULL);
531 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 const uint16_t* ptr = *(const uint16_t**)text;
534 ptr -= 1;
535 unsigned glyphID = *ptr;
536 *text = (const char*)ptr;
537 return cache->getGlyphIDAdvance(glyphID);
538}
539
540SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000541 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
543 sk_getMetrics_utf8_next,
544 sk_getMetrics_utf16_next,
545 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 sk_getMetrics_utf8_prev,
548 sk_getMetrics_utf16_prev,
549 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 sk_getAdvance_utf8_next,
552 sk_getAdvance_utf16_next,
553 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000554
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 sk_getAdvance_utf8_prev,
556 sk_getAdvance_utf16_prev,
557 sk_getAdvance_glyph_prev
558 };
reed@google.com72cf4922011-01-04 19:58:20 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 unsigned index = this->getTextEncoding();
561
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000562 if (kBackward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000564 }
565 if (!needFullMetrics && !this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 index += 6;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000567 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568
569 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
570 return gMeasureCacheProcs[index];
571}
572
573///////////////////////////////////////////////////////////////////////////////
574
575static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000576 const char** text, SkFixed, SkFixed) {
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->getUnicharMetrics(SkUTF8_NextUnichar(text));
581}
582
583static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000584 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 SkASSERT(cache != NULL);
586 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000587
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
589}
590
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000591static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
592 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkASSERT(cache != NULL);
594 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
597}
598
599static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000600 const char** text, SkFixed x, SkFixed y) {
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->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
605 x, y);
606}
607
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000608static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
609 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 SkASSERT(cache != NULL);
611 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 const uint16_t* ptr = *(const uint16_t**)text;
614 unsigned glyphID = *ptr;
615 ptr += 1;
616 *text = (const char*)ptr;
617 return cache->getGlyphIDMetrics(glyphID);
618}
619
620static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000621 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkASSERT(cache != NULL);
623 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000624
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 const uint16_t* ptr = *(const uint16_t**)text;
626 unsigned glyphID = *ptr;
627 ptr += 1;
628 *text = (const char*)ptr;
629 return cache->getGlyphIDMetrics(glyphID, x, y);
630}
631
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000632SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 static const SkDrawCacheProc gDrawCacheProcs[] = {
634 sk_getMetrics_utf8_00,
635 sk_getMetrics_utf16_00,
636 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 sk_getMetrics_utf8_xy,
639 sk_getMetrics_utf16_xy,
640 sk_getMetrics_glyph_xy
641 };
reed@google.com72cf4922011-01-04 19:58:20 +0000642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000644 if (fFlags & kSubpixelText_Flag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 index += 3;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000646 }
reed@google.com72cf4922011-01-04 19:58:20 +0000647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
649 return gDrawCacheProcs[index];
650}
651
652///////////////////////////////////////////////////////////////////////////////
653
654class SkAutoRestorePaintTextSizeAndFrame {
655public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000656 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
657 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 fTextSize = paint->getTextSize();
659 fStyle = paint->getStyle();
660 fPaint->setStyle(SkPaint::kFill_Style);
661 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000662
663 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 fPaint->setStyle(fStyle);
665 fPaint->setTextSize(fTextSize);
666 }
reed@google.com72cf4922011-01-04 19:58:20 +0000667
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668private:
669 SkPaint* fPaint;
670 SkScalar fTextSize;
671 SkPaint::Style fStyle;
672};
673
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000674static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 bounds->set(SkIntToScalar(g.fLeft),
676 SkIntToScalar(g.fTop),
677 SkIntToScalar(g.fLeft + g.fWidth),
678 SkIntToScalar(g.fTop + g.fHeight));
679}
680
reed@android.come88f5512010-03-19 14:42:28 +0000681// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
682// we don't overflow along the way
683typedef int64_t Sk48Dot16;
684
685#ifdef SK_SCALAR_IS_FLOAT
686 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000687 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000688 }
689#else
690 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
691 // just return the low 32bits
692 return static_cast<SkFixed>(x);
693 }
694#endif
695
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000696static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000697 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 bounds->join(SkIntToScalar(g.fLeft) + sx,
699 SkIntToScalar(g.fTop),
700 SkIntToScalar(g.fLeft + g.fWidth) + sx,
701 SkIntToScalar(g.fTop + g.fHeight));
702}
703
704SkScalar SkPaint::measure_text(SkGlyphCache* cache,
705 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000706 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000708 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000710 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000712 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 return 0;
714 }
715
716 SkMeasureCacheProc glyphCacheProc;
717 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
718 NULL != bounds);
719
720 int n = 1;
721 const char* stop = (const char*)text + byteLength;
722 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000723 // our accumulated fixed-point advances might overflow 16.16, so we use
724 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
725 // very end.
726 Sk48Dot16 x = g->fAdvanceX;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727
728 SkAutoKern autokern;
729
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000730 if (NULL == bounds) {
731 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 int rsb;
733 for (; text < stop; n++) {
734 rsb = g->fRsbDelta;
735 g = &glyphCacheProc(cache, &text);
736 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
737 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000738 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 for (; text < stop; n++) {
740 x += glyphCacheProc(cache, &text).fAdvanceX;
741 }
742 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000743 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000745 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 int rsb;
747 for (; text < stop; n++) {
748 rsb = g->fRsbDelta;
749 g = &glyphCacheProc(cache, &text);
750 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
751 join_bounds(*g, bounds, x);
752 x += g->fAdvanceX;
753 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000754 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 for (; text < stop; n++) {
756 g = &glyphCacheProc(cache, &text);
757 join_bounds(*g, bounds, x);
758 x += g->fAdvanceX;
759 }
760 }
761 }
762 SkASSERT(text == stop);
763
764 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +0000765 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766}
767
768SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000769 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 const char* text = (const char*)textData;
771 SkASSERT(text != NULL || length == 0);
772
773 SkScalar scale = 0;
774 SkAutoRestorePaintTextSizeAndFrame restore(this);
775
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000776 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 scale = fTextSize / kCanonicalTextSizeForPaths;
778 // this gets restored by restore
779 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
780 }
reed@google.com72cf4922011-01-04 19:58:20 +0000781
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000782 SkMatrix zoomMatrix, *zoomPtr = NULL;
783 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 zoomMatrix.setScale(zoom, zoom);
785 zoomPtr = &zoomMatrix;
786 }
787
788 SkAutoGlyphCache autoCache(*this, zoomPtr);
789 SkGlyphCache* cache = autoCache.getCache();
790
791 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +0000792
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000793 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 int tempCount;
795
796 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000797 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000799 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
801 bounds->fTop = SkScalarMul(bounds->fTop, scale);
802 bounds->fRight = SkScalarMul(bounds->fRight, scale);
803 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
804 }
805 }
806 }
807 return width;
808}
809
810typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
811
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000812static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 return text < stop;
814}
815
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000816static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 return text > stop;
818}
819
820static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000821 const char** text, size_t length,
822 const char** stop) {
823 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 *stop = *text + length;
825 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000826 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 // text should point to the end of the buffer, and stop to the beginning
828 *stop = *text;
829 *text += length;
830 return backward_textBufferPred;
831 }
832}
833
834size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
835 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000836 TextBufferDirection tbd) const {
837 if (0 == length || 0 >= maxWidth) {
838 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000840 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 return 0;
842 }
843
844 SkASSERT(textD != NULL);
845 const char* text = (const char*)textD;
846
847 SkScalar scale = 0;
848 SkAutoRestorePaintTextSizeAndFrame restore(this);
849
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000850 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +0000852 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 // this gets restored by restore
854 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
855 }
reed@google.com72cf4922011-01-04 19:58:20 +0000856
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 SkAutoGlyphCache autoCache(*this, NULL);
858 SkGlyphCache* cache = autoCache.getCache();
859
860 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
861 const char* stop;
862 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@android.come88f5512010-03-19 14:42:28 +0000863 // use 64bits for our accumulator, to avoid overflowing 16.16
864 Sk48Dot16 max = SkScalarToFixed(maxWidth);
865 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866
867 SkAutoKern autokern;
868
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000869 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000871 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 const char* curr = text;
873 const SkGlyph& g = glyphCacheProc(cache, &text);
874 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000875 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 width -= x;
877 text = curr;
878 break;
879 }
880 rsb = g.fRsbDelta;
881 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000882 } else {
883 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 const char* curr = text;
885 SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000886 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 width -= x;
888 text = curr;
889 break;
890 }
891 }
892 }
reed@google.com72cf4922011-01-04 19:58:20 +0000893
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000894 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +0000895 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000896 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000898 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 *measuredWidth = scalarWidth;
900 }
reed@google.com72cf4922011-01-04 19:58:20 +0000901
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 // return the number of bytes measured
903 return (kForward_TextBufferDirection == tbd) ?
904 text - stop + length : stop - text + length;
905}
906
907///////////////////////////////////////////////////////////////////////////////
908
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000909static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
911 return false; // don't detach the cache
912}
913
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000914static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
916}
917
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000918SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 SkScalar scale = 0;
920 SkAutoRestorePaintTextSizeAndFrame restore(this);
921
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000922 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 scale = fTextSize / kCanonicalTextSizeForPaths;
924 // this gets restored by restore
925 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
926 }
reed@google.com72cf4922011-01-04 19:58:20 +0000927
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000928 SkMatrix zoomMatrix, *zoomPtr = NULL;
929 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 zoomMatrix.setScale(zoom, zoom);
931 zoomPtr = &zoomMatrix;
932 }
933
934#if 0
935 SkAutoGlyphCache autoCache(*this, zoomPtr);
936 SkGlyphCache* cache = autoCache.getCache();
937 const FontMetrics& my = cache->getFontMetricsY();
938#endif
939 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000940 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000942 }
reed@google.com72cf4922011-01-04 19:58:20 +0000943
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
945
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000946 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 metrics->fTop = SkScalarMul(metrics->fTop, scale);
948 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
949 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
950 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
951 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
952 }
953 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
954}
955
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000956///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000958static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 bounds->set(g.fLeft * scale,
960 g.fTop * scale,
961 (g.fLeft + g.fWidth) * scale,
962 (g.fTop + g.fHeight) * scale);
963}
964
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000965int SkPaint::getTextWidths(const void* textData, size_t byteLength,
966 SkScalar widths[], SkRect bounds[]) const {
967 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000969 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970
971 SkASSERT(NULL != textData);
972
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000973 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000975 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976
977 SkAutoRestorePaintTextSizeAndFrame restore(this);
978 SkScalar scale = 0;
979
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000980 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 scale = fTextSize / kCanonicalTextSizeForPaths;
982 // this gets restored by restore
983 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
984 }
985
986 SkAutoGlyphCache autoCache(*this, NULL);
987 SkGlyphCache* cache = autoCache.getCache();
988 SkMeasureCacheProc glyphCacheProc;
989 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
990 NULL != bounds);
991
992 const char* text = (const char*)textData;
993 const char* stop = text + byteLength;
994 int count = 0;
995
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000996 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 // we adjust the widths returned here through auto-kerning
998 SkAutoKern autokern;
999 SkFixed prevWidth = 0;
1000
1001 if (scale) {
1002 while (text < stop) {
1003 const SkGlyph& g = glyphCacheProc(cache, &text);
1004 if (widths) {
1005 SkFixed adjust = autokern.adjust(g);
1006
1007 if (count > 0) {
1008 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1009 *widths++ = SkScalarMul(w, scale);
1010 }
1011 prevWidth = g.fAdvanceX;
1012 }
1013 if (bounds) {
1014 set_bounds(g, bounds++, scale);
1015 }
1016 ++count;
1017 }
1018 if (count > 0 && widths) {
1019 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1020 }
1021 } else {
1022 while (text < stop) {
1023 const SkGlyph& g = glyphCacheProc(cache, &text);
1024 if (widths) {
1025 SkFixed adjust = autokern.adjust(g);
1026
1027 if (count > 0) {
1028 *widths++ = SkFixedToScalar(prevWidth + adjust);
1029 }
1030 prevWidth = g.fAdvanceX;
1031 }
1032 if (bounds) {
1033 set_bounds(g, bounds++);
1034 }
1035 ++count;
1036 }
1037 if (count > 0 && widths) {
1038 *widths = SkFixedToScalar(prevWidth);
1039 }
1040 }
1041 } else { // no devkern
1042 if (scale) {
1043 while (text < stop) {
1044 const SkGlyph& g = glyphCacheProc(cache, &text);
1045 if (widths) {
1046 *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
1047 scale);
1048 }
1049 if (bounds) {
1050 set_bounds(g, bounds++, scale);
1051 }
1052 ++count;
1053 }
1054 } else {
1055 while (text < stop) {
1056 const SkGlyph& g = glyphCacheProc(cache, &text);
1057 if (widths) {
1058 *widths++ = SkFixedToScalar(g.fAdvanceX);
1059 }
1060 if (bounds) {
1061 set_bounds(g, bounds++);
1062 }
1063 ++count;
1064 }
1065 }
1066 }
1067
1068 SkASSERT(text == stop);
1069 return count;
1070}
1071
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001072///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073
1074#include "SkDraw.h"
1075
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001076void SkPaint::getTextPath(const void* textData, size_t length,
1077 SkScalar x, SkScalar y, SkPath* path) const {
1078 SkASSERT(length == 0 || textData != NULL);
1079
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001081 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084
1085 SkTextToPathIter iter(text, length, *this, false, true);
1086 SkMatrix matrix;
1087 SkScalar prevXPos = 0;
1088
1089 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1090 matrix.postTranslate(x, y);
1091 path->reset();
1092
1093 SkScalar xpos;
1094 const SkPath* iterPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001095 while ((iterPath = iter.next(&xpos)) != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 matrix.postTranslate(xpos - prevXPos, 0);
1097 path->addPath(*iterPath, matrix);
1098 prevXPos = xpos;
1099 }
1100}
1101
1102static void add_flattenable(SkDescriptor* desc, uint32_t tag,
1103 SkFlattenableWriteBuffer* buffer) {
1104 buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
1105}
1106
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001107static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001110 // Antialiasing being disabled trumps all other settings.
1111 if (!(flags & SkPaint::kAntiAlias_Flag))
1112 return SkMask::kBW_Format;
1113
reed@android.comf5493692009-07-22 19:21:01 +00001114#if defined(SK_SUPPORT_LCDTEXT)
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001115 if (flags & SkPaint::kLCDRenderText_Flag) {
agl@chromium.org309485b2009-07-21 17:41:32 +00001116 return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ?
1117 SkMask::kHorizontalLCD_Format : SkMask::kVerticalLCD_Format;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001118 }
agl@chromium.org309485b2009-07-21 17:41:32 +00001119#endif
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001120
1121 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122}
1123
reed@android.com1cdcb512009-08-24 19:11:00 +00001124// if linear-text is on, then we force hinting to be off (since that's sort of
1125// the point of linear-text.
1126static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1127 SkPaint::Hinting h = paint.getHinting();
1128 if (paint.isLinearText()) {
1129 h = SkPaint::kNo_Hinting;
1130 }
1131 return h;
1132}
1133
reed@google.com72cf4922011-01-04 19:58:20 +00001134/*
1135 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1136 * that vary only slightly when we create our key into the font cache, since the font scaler
1137 * typically returns the same looking resuts for tiny changes in the matrix.
1138 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001139static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001140#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001141 int n = sk_float_round2int(x * 1024);
1142 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001143#else
1144 // round to the nearest 10 fractional bits
1145 return (x + (1 << 5)) & ~(1024 - 1);
1146#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001147}
1148
reed@android.com36a4c2a2009-07-22 19:52:11 +00001149void SkScalerContext::MakeRec(const SkPaint& paint,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001150 const SkMatrix* deviceMatrix, Rec* rec) {
reed@android.com36a4c2a2009-07-22 19:52:11 +00001151 SkASSERT(deviceMatrix == NULL ||
1152 (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153
1154 rec->fFontID = SkTypeface::UniqueID(paint.getTypeface());
1155 rec->fTextSize = paint.getTextSize();
1156 rec->fPreScaleX = paint.getTextScaleX();
1157 rec->fPreSkewX = paint.getTextSkewX();
1158
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001159 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001160 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1161 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1162 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1163 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001164 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1166 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1167 }
reed@google.com72cf4922011-01-04 19:58:20 +00001168
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 SkPaint::Style style = paint.getStyle();
1170 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001171
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001172 unsigned flags = SkFontHost::ComputeGammaFlag(paint);
1173
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001174 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001175#ifdef SK_USE_FREETYPE_EMBOLDEN
1176 flags |= SkScalerContext::kEmbolden_Flag;
1177#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001178 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1179 kStdFakeBoldInterpKeys,
1180 kStdFakeBoldInterpValues,
1181 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001183
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001184 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 style = SkPaint::kStrokeAndFill_Style;
1186 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001187 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001189 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001190#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 }
1192
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001193 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001195 }
reed@google.com72cf4922011-01-04 19:58:20 +00001196
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001197 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 rec->fFrameWidth = strokeWidth;
1199 rec->fMiterLimit = paint.getStrokeMiter();
1200 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1201
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001202 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001204 }
1205 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 rec->fFrameWidth = 0;
1207 rec->fMiterLimit = 0;
1208 rec->fStrokeJoin = 0;
1209 }
1210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1212 rec->fFlags = SkToU8(flags);
reed@android.com1cdcb512009-08-24 19:11:00 +00001213 rec->setHinting(computeHinting(paint));
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001214 if (paint.isEmbeddedBitmapText()) {
agl@chromium.org13c85582010-01-04 23:56:43 +00001215 rec->fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001216 }
1217 if (paint.isSubpixelText()) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +00001218 rec->fFlags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001219 }
1220 if (paint.isAutohinted()) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +00001221 rec->fFlags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001222 }
reed@android.com36a4c2a2009-07-22 19:52:11 +00001223
1224 /* Allow the fonthost to modify our rec before we use it as a key into the
1225 cache. This way if we're asking for something that they will ignore,
1226 they can modify our rec up front, so we don't create duplicate cache
1227 entries.
1228 */
1229 SkFontHost::FilterRec(rec);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230}
1231
1232#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1233
1234void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
1235 void (*proc)(const SkDescriptor*, void*),
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001236 void* context) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 SkScalerContext::Rec rec;
1238
1239 SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
1240
1241 size_t descSize = sizeof(rec);
1242 int entryCount = 1;
1243 SkPathEffect* pe = this->getPathEffect();
1244 SkMaskFilter* mf = this->getMaskFilter();
1245 SkRasterizer* ra = this->getRasterizer();
1246
1247 SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1248 SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1249 SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1250
1251 if (pe) {
1252 peBuffer.writeFlattenable(pe);
1253 descSize += peBuffer.size();
1254 entryCount += 1;
1255 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1256 // seems like we could support kLCD as well at this point...
1257 }
1258 if (mf) {
1259 mfBuffer.writeFlattenable(mf);
1260 descSize += mfBuffer.size();
1261 entryCount += 1;
1262 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1263 }
1264 if (ra) {
1265 raBuffer.writeFlattenable(ra);
1266 descSize += raBuffer.size();
1267 entryCount += 1;
1268 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1269 }
1270 descSize += SkDescriptor::ComputeOverhead(entryCount);
1271
1272 SkAutoDescriptor ad(descSize);
1273 SkDescriptor* desc = ad.getDesc();
1274
1275 desc->init();
1276 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1277
1278 if (pe) {
1279 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1280 }
1281 if (mf) {
1282 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1283 }
1284 if (ra) {
1285 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1286 }
1287
1288 SkASSERT(descSize == desc->getLength());
1289 desc->computeChecksum();
1290
1291 proc(desc, context);
1292}
1293
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001294static void DetachDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
1296}
1297
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001298SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 SkGlyphCache* cache;
1300 this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
1301 return cache;
1302}
1303
1304///////////////////////////////////////////////////////////////////////////////
1305
1306#include "SkStream.h"
1307
reed@android.comaefd2bc2009-03-30 21:02:14 +00001308static uintptr_t asint(const void* p) {
1309 return reinterpret_cast<uintptr_t>(p);
1310}
1311
1312union Scalar32 {
1313 SkScalar fScalar;
1314 uint32_t f32;
1315};
1316
1317static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1318 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1319 Scalar32 tmp;
1320 tmp.fScalar = value;
1321 *ptr = tmp.f32;
1322 return ptr + 1;
1323}
1324
1325static SkScalar read_scalar(const uint32_t*& ptr) {
1326 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1327 Scalar32 tmp;
1328 tmp.f32 = *ptr++;
1329 return tmp.fScalar;
1330}
1331
1332static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1333 SkASSERT(a == (uint8_t)a);
1334 SkASSERT(b == (uint8_t)b);
1335 SkASSERT(c == (uint8_t)c);
1336 SkASSERT(d == (uint8_t)d);
1337 return (a << 24) | (b << 16) | (c << 8) | d;
1338}
1339
1340enum FlatFlags {
1341 kHasTypeface_FlatFlag = 0x01,
1342 kHasEffects_FlatFlag = 0x02
1343};
1344
1345// The size of a flat paint's POD fields
1346static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
1347 1 * sizeof(SkColor) +
1348 1 * sizeof(uint16_t) +
1349 6 * sizeof(uint8_t);
1350
1351/* To save space/time, we analyze the paint, and write a truncated version of
1352 it if there are not tricky elements like shaders, etc.
1353 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001355 uint8_t flatFlags = 0;
1356 if (this->getTypeface()) {
1357 flatFlags |= kHasTypeface_FlatFlag;
1358 }
1359 if (asint(this->getPathEffect()) |
1360 asint(this->getShader()) |
1361 asint(this->getXfermode()) |
1362 asint(this->getMaskFilter()) |
1363 asint(this->getColorFilter()) |
1364 asint(this->getRasterizer()) |
1365 asint(this->getLooper())) {
1366 flatFlags |= kHasEffects_FlatFlag;
1367 }
reed@google.com72cf4922011-01-04 19:58:20 +00001368
reed@android.comaefd2bc2009-03-30 21:02:14 +00001369 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1370 uint32_t* ptr = buffer.reserve(kPODPaintSize);
1371
1372 ptr = write_scalar(ptr, this->getTextSize());
1373 ptr = write_scalar(ptr, this->getTextScaleX());
1374 ptr = write_scalar(ptr, this->getTextSkewX());
1375 ptr = write_scalar(ptr, this->getStrokeWidth());
1376 ptr = write_scalar(ptr, this->getStrokeMiter());
1377 *ptr++ = this->getColor();
1378 *ptr++ = (this->getFlags() << 16) | (this->getTextAlign() << 8) | flatFlags;
1379 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1380 this->getStyle(), this->getTextEncoding());
1381
1382 // now we're done with ptr and the (pre)reserved space. If we need to write
1383 // additional fields, use the buffer directly
1384 if (flatFlags & kHasTypeface_FlatFlag) {
1385 buffer.writeTypeface(this->getTypeface());
1386 }
1387 if (flatFlags & kHasEffects_FlatFlag) {
1388 buffer.writeFlattenable(this->getPathEffect());
1389 buffer.writeFlattenable(this->getShader());
1390 buffer.writeFlattenable(this->getXfermode());
1391 buffer.writeFlattenable(this->getMaskFilter());
1392 buffer.writeFlattenable(this->getColorFilter());
1393 buffer.writeFlattenable(this->getRasterizer());
1394 buffer.writeFlattenable(this->getLooper());
1395 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396}
1397
1398void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001399 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1400 const void* podData = buffer.skip(kPODPaintSize);
1401 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@google.com72cf4922011-01-04 19:58:20 +00001402
reed@android.comaefd2bc2009-03-30 21:02:14 +00001403 // the order we read must match the order we wrote in flatten()
1404 this->setTextSize(read_scalar(pod));
1405 this->setTextScaleX(read_scalar(pod));
1406 this->setTextSkewX(read_scalar(pod));
1407 this->setStrokeWidth(read_scalar(pod));
reed@google.com72cf4922011-01-04 19:58:20 +00001408 this->setStrokeMiter(read_scalar(pod));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001409 this->setColor(*pod++);
1410
1411 uint32_t tmp = *pod++;
1412 this->setFlags(tmp >> 16);
1413 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xFF));
1414 uint8_t flatFlags = tmp & 0xFF;
1415
1416 tmp = *pod++;
1417 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1418 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1419 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1420 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
1421
1422 if (flatFlags & kHasTypeface_FlatFlag) {
1423 this->setTypeface(buffer.readTypeface());
1424 } else {
1425 this->setTypeface(NULL);
1426 }
1427
1428 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com82065d62011-02-07 15:30:46 +00001429 SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable()));
1430 SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable()));
1431 SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable()));
1432 SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable()));
1433 SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable()));
1434 SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable()));
1435 SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001436 } else {
1437 this->setPathEffect(NULL);
1438 this->setShader(NULL);
1439 this->setXfermode(NULL);
1440 this->setMaskFilter(NULL);
1441 this->setColorFilter(NULL);
1442 this->setRasterizer(NULL);
1443 this->setLooper(NULL);
1444 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445}
1446
1447///////////////////////////////////////////////////////////////////////////////
1448
reed@google.com82065d62011-02-07 15:30:46 +00001449SkShader* SkPaint::setShader(SkShader* shader) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 SkRefCnt_SafeAssign(fShader, shader);
1451 return shader;
1452}
1453
reed@google.com82065d62011-02-07 15:30:46 +00001454SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 SkRefCnt_SafeAssign(fColorFilter, filter);
1456 return filter;
1457}
1458
reed@google.com82065d62011-02-07 15:30:46 +00001459SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 SkRefCnt_SafeAssign(fXfermode, mode);
1461 return mode;
1462}
1463
reed@android.com0baf1932009-06-24 12:41:42 +00001464SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00001465 SkSafeUnref(fXfermode);
1466 fXfermode = SkXfermode::Create(mode);
1467 return fXfermode;
1468}
1469
reed@google.com82065d62011-02-07 15:30:46 +00001470SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471 SkRefCnt_SafeAssign(fPathEffect, effect);
1472 return effect;
1473}
1474
reed@google.com82065d62011-02-07 15:30:46 +00001475SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 SkRefCnt_SafeAssign(fMaskFilter, filter);
1477 return filter;
1478}
1479
reed@google.com82065d62011-02-07 15:30:46 +00001480///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001482bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 SkPath effectPath, strokePath;
1484 const SkPath* path = &src;
1485
1486 SkScalar width = this->getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001487
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 switch (this->getStyle()) {
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001489 case SkPaint::kFill_Style:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001491 break;
1492 case SkPaint::kStrokeAndFill_Style:
1493 if (width == 0) {
1494 width = -1; // mark it as no-stroke
1495 }
1496 break;
1497 case SkPaint::kStroke_Style:
1498 break;
1499 default:
1500 SkASSERT(!"unknown paint style");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 }
1502
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001503 if (this->getPathEffect()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 // 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 +00001505 if (this->getStyle() == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 width = -1; // mark it as no-stroke
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001507 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001509 if (this->getPathEffect()->filterPath(&effectPath, src, &width)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 path = &effectPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001511 }
reed@google.com72cf4922011-01-04 19:58:20 +00001512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 // restore the width if we earlier had to lie, and if we're still set to no-stroke
1514 // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
1515 // and we want to respect that (i.e. don't overwrite their setting for width)
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001516 if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517 width = this->getStrokeWidth();
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001518 if (width == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 width = -1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001520 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521 }
1522 }
reed@google.com72cf4922011-01-04 19:58:20 +00001523
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001524 if (width > 0 && !path->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525 SkStroke stroker(*this, width);
1526 stroker.strokePath(*path, &strokePath);
1527 path = &strokePath;
1528 }
1529
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001530 if (path == &src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531 *dst = src;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001532 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533 SkASSERT(path == &effectPath || path == &strokePath);
1534 dst->swap(*(SkPath*)path);
1535 }
1536
1537 return width != 0; // return true if we're filled, or false if we're hairline (width == 0)
1538}
1539
reed@android.comd252db02009-04-01 18:31:44 +00001540const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
1541 SkRect* storage) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00001543 SkASSERT(this->getStyle() != SkPaint::kFill_Style);
1544
1545 // since we're stroked, outset the rect by the radius (and join type)
1546 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
1547 if (0 == radius) { // hairline
1548 radius = SK_Scalar1;
1549 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
1550 SkScalar scale = this->getStrokeMiter();
1551 if (scale > SK_Scalar1) {
1552 radius = SkScalarMul(radius, scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 }
reed@android.comd252db02009-04-01 18:31:44 +00001555 storage->set(src.fLeft - radius, src.fTop - radius,
1556 src.fRight + radius, src.fBottom + radius);
1557 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558}
1559
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001560///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001562static bool has_thick_frame(const SkPaint& paint) {
1563 return paint.getStrokeWidth() > 0 &&
1564 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
1567SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
1568 const SkPaint& paint,
1569 bool applyStrokeAndPathEffects,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001570 bool forceLinearTextOn) : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
1572 true);
1573
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001574 if (forceLinearTextOn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 fPaint.setLinearText(true);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001576 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
1578
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001579 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001581 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582
1583 // can't use our canonical size if we need to apply patheffects/strokes
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001584 if (fPaint.isLinearText() && !applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
1586 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001587 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001589 }
reed@google.com72cf4922011-01-04 19:58:20 +00001590
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001591 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 fPaint.setStyle(SkPaint::kFill_Style);
1593 fPaint.setPathEffect(NULL);
1594 }
1595
1596 fCache = fPaint.detachCache(NULL);
1597
1598 SkPaint::Style style = SkPaint::kFill_Style;
1599 SkPathEffect* pe = NULL;
1600
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001601 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 style = paint.getStyle(); // restore
1603 pe = paint.getPathEffect(); // restore
1604 }
1605 fPaint.setStyle(style);
1606 fPaint.setPathEffect(pe);
1607 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
1608
1609 // now compute fXOffset if needed
1610
1611 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001612 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001614 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
1615 &count, NULL), fScale);
1616 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001618 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 xOffset = -width;
1620 }
1621 fXPos = xOffset;
1622 fPrevAdvance = 0;
1623
1624 fText = text;
1625 fStop = text + length;
1626}
1627
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001628SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 SkGlyphCache::AttachCache(fCache);
1630}
1631
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001632const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
1633 while (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
1635
1636 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
1637 fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking();
1638
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001639 if (glyph.fWidth) {
1640 if (xpos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 *xpos = fXPos;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001642 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643 return fCache->findPath(glyph);
1644 }
1645 }
1646 return NULL;
1647}
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001648