blob: 04a8f5a756577820977f318ebd12bd2087f90124 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkPaint.h"
reed@google.comb0a34d82012-07-11 19:57:55 +000010#include "SkAnnotation.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkFontHost.h"
reed@google.com15356a62011-11-03 19:29:08 +000013#include "SkImageFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000015#include "SkMaskGamma.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkPathEffect.h"
17#include "SkRasterizer.h"
18#include "SkShader.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000019#include "SkScalar.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkScalerContext.h"
21#include "SkStroke.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000022#include "SkTextFormatParams.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTypeface.h"
24#include "SkXfermode.h"
25#include "SkAutoKern.h"
djsollen@google.com60abb072012-02-15 18:49:15 +000026#include "SkGlyphCache.h"
reed@google.comaefdd062012-02-29 13:03:00 +000027#include "SkPaintDefaults.h"
djsollen@google.com2b2ede32012-04-12 13:24:04 +000028#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
reed@google.coma3237872011-07-05 19:20:48 +000030// define this to get a printf for out-of-range parameter in setters
31// e.g. setTextSize(-1)
32//#define SK_REPORT_API_RANGE_CHECK
33
djsollen@google.com56c69772011-11-08 19:00:26 +000034#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000035#define GEN_ID_INC fGenerationID++
36#define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
37#else
38#define GEN_ID_INC
39#define GEN_ID_INC_EVAL(expression)
40#endif
41
reed@android.coma3122b92009-08-13 20:38:25 +000042SkPaint::SkPaint() {
43 // since we may have padding, we zero everything so that our memcmp() call
44 // in operator== will work correctly.
45 // with this, we can skip 0 and null individual initializations
46 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000047
reed@android.coma3122b92009-08-13 20:38:25 +000048#if 0 // not needed with the bzero call above
49 fTypeface = NULL;
50 fTextSkewX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 fPathEffect = NULL;
52 fShader = NULL;
53 fXfermode = NULL;
54 fMaskFilter = NULL;
55 fColorFilter = NULL;
56 fRasterizer = NULL;
57 fLooper = NULL;
reed@google.com15356a62011-11-03 19:29:08 +000058 fImageFilter = NULL;
reed@google.comb0a34d82012-07-11 19:57:55 +000059 fAnnotation = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 fWidth = 0;
reed@android.coma3122b92009-08-13 20:38:25 +000061#endif
62
reed@google.comaefdd062012-02-29 13:03:00 +000063 fTextSize = SkPaintDefaults_TextSize;
reed@android.coma3122b92009-08-13 20:38:25 +000064 fTextScaleX = SK_Scalar1;
65 fColor = SK_ColorBLACK;
reed@google.comaefdd062012-02-29 13:03:00 +000066 fMiterLimit = SkPaintDefaults_MiterLimit;
67 fFlags = SkPaintDefaults_Flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 fCapType = kDefault_Cap;
69 fJoinType = kDefault_Join;
70 fTextAlign = kLeft_Align;
71 fStyle = kFill_Style;
72 fTextEncoding = kUTF8_TextEncoding;
reed@google.comaefdd062012-02-29 13:03:00 +000073 fHinting = SkPaintDefaults_Hinting;
reed@google.comb0a34d82012-07-11 19:57:55 +000074 fPrivFlags = 0;
djsollen@google.com56c69772011-11-08 19:00:26 +000075#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +000076 fGenerationID = 0;
77#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000078}
79
reed@google.com6fb7e2e2011-02-08 22:22:52 +000080SkPaint::SkPaint(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 memcpy(this, &src, sizeof(src));
82
reed@google.com82065d62011-02-07 15:30:46 +000083 SkSafeRef(fTypeface);
84 SkSafeRef(fPathEffect);
85 SkSafeRef(fShader);
86 SkSafeRef(fXfermode);
87 SkSafeRef(fMaskFilter);
88 SkSafeRef(fColorFilter);
89 SkSafeRef(fRasterizer);
90 SkSafeRef(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +000091 SkSafeRef(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +000092 SkSafeRef(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +000093}
94
reed@google.com6fb7e2e2011-02-08 22:22:52 +000095SkPaint::~SkPaint() {
reed@google.com82065d62011-02-07 15:30:46 +000096 SkSafeUnref(fTypeface);
97 SkSafeUnref(fPathEffect);
98 SkSafeUnref(fShader);
99 SkSafeUnref(fXfermode);
100 SkSafeUnref(fMaskFilter);
101 SkSafeUnref(fColorFilter);
102 SkSafeUnref(fRasterizer);
103 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000104 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000105 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106}
107
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000108SkPaint& SkPaint::operator=(const SkPaint& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 SkASSERT(&src);
110
reed@google.com82065d62011-02-07 15:30:46 +0000111 SkSafeRef(src.fTypeface);
112 SkSafeRef(src.fPathEffect);
113 SkSafeRef(src.fShader);
114 SkSafeRef(src.fXfermode);
115 SkSafeRef(src.fMaskFilter);
116 SkSafeRef(src.fColorFilter);
117 SkSafeRef(src.fRasterizer);
118 SkSafeRef(src.fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000119 SkSafeRef(src.fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000120 SkSafeRef(src.fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
reed@google.com82065d62011-02-07 15:30:46 +0000122 SkSafeUnref(fTypeface);
123 SkSafeUnref(fPathEffect);
124 SkSafeUnref(fShader);
125 SkSafeUnref(fXfermode);
126 SkSafeUnref(fMaskFilter);
127 SkSafeUnref(fColorFilter);
128 SkSafeUnref(fRasterizer);
129 SkSafeUnref(fLooper);
reed@google.com15356a62011-11-03 19:29:08 +0000130 SkSafeUnref(fImageFilter);
reed@google.comb0a34d82012-07-11 19:57:55 +0000131 SkSafeUnref(fAnnotation);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132
djsollen@google.com56c69772011-11-08 19:00:26 +0000133#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000134 uint32_t oldGenerationID = fGenerationID;
135#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 memcpy(this, &src, sizeof(src));
djsollen@google.com56c69772011-11-08 19:00:26 +0000137#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000138 fGenerationID = oldGenerationID + 1;
139#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
141 return *this;
142}
143
reed@google.comb530ef52011-07-20 19:55:42 +0000144bool operator==(const SkPaint& a, const SkPaint& b) {
djsollen@google.comb44cd652011-12-01 17:09:21 +0000145#ifdef SK_BUILD_FOR_ANDROID
146 //assumes that fGenerationID is the last field in the struct
147 return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
148#else
reed@google.comb530ef52011-07-20 19:55:42 +0000149 return !memcmp(&a, &b, sizeof(a));
djsollen@google.comb44cd652011-12-01 17:09:21 +0000150#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151}
152
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000153void SkPaint::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 SkPaint init;
155
djsollen@google.com56c69772011-11-08 19:00:26 +0000156#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000157 uint32_t oldGenerationID = fGenerationID;
158#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 *this = init;
djsollen@google.com56c69772011-11-08 19:00:26 +0000160#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000161 fGenerationID = oldGenerationID + 1;
162#endif
163}
164
djsollen@google.com56c69772011-11-08 19:00:26 +0000165#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000166uint32_t SkPaint::getGenerationID() const {
167 return fGenerationID;
168}
169#endif
170
djsollen@google.com60abb072012-02-15 18:49:15 +0000171#ifdef SK_BUILD_FOR_ANDROID
172unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
173 SkAutoGlyphCache autoCache(*this, NULL);
174 SkGlyphCache* cache = autoCache.getCache();
175 return cache->getBaseGlyphCount(text);
176}
177#endif
178
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000179void SkPaint::setHinting(Hinting hintingLevel) {
180 GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting);
181 fHinting = hintingLevel;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182}
183
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000184void SkPaint::setFlags(uint32_t flags) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000185 GEN_ID_INC_EVAL(fFlags != flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 fFlags = flags;
187}
188
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000189void SkPaint::setAntiAlias(bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
191}
192
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000193void SkPaint::setDither(bool doDither) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
195}
196
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000197void SkPaint::setSubpixelText(bool doSubpixel) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
199}
200
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000201void SkPaint::setLCDRenderText(bool doLCDRender) {
agl@chromium.org309485b2009-07-21 17:41:32 +0000202 this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
203}
204
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000205void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
agl@chromium.orge95c91e2010-01-04 18:27:55 +0000206 this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
207}
208
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000209void SkPaint::setAutohinted(bool useAutohinter) {
agl@chromium.orga2c71cb2010-06-17 20:49:17 +0000210 this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
211}
212
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000213void SkPaint::setLinearText(bool doLinearText) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
215}
216
reed@google.com830a23e2011-11-10 15:20:49 +0000217void SkPaint::setVerticalText(bool doVertical) {
reed@google.com830a23e2011-11-10 15:20:49 +0000218 this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
219}
220
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000221void SkPaint::setUnderlineText(bool doUnderline) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
223}
224
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000225void SkPaint::setStrikeThruText(bool doStrikeThru) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
227}
228
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000229void SkPaint::setFakeBoldText(bool doFakeBold) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
231}
232
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000233void SkPaint::setDevKernText(bool doDevKern) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
235}
236
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000237void SkPaint::setFilterBitmap(bool doFilter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
239}
240
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000241void SkPaint::setStyle(Style style) {
242 if ((unsigned)style < kStyleCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000243 GEN_ID_INC_EVAL((unsigned)style != fStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 fStyle = style;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000245 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000246#ifdef SK_REPORT_API_RANGE_CHECK
247 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
248#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000249 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250}
251
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000252void SkPaint::setColor(SkColor color) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000253 GEN_ID_INC_EVAL(color != fColor);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fColor = color;
255}
256
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000257void SkPaint::setAlpha(U8CPU a) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000258 this->setColor(SkColorSetARGB(a, SkColorGetR(fColor),
259 SkColorGetG(fColor), SkColorGetB(fColor)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260}
261
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000262void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000263 this->setColor(SkColorSetARGB(a, r, g, b));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264}
265
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000266void SkPaint::setStrokeWidth(SkScalar width) {
267 if (width >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000268 GEN_ID_INC_EVAL(width != fWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fWidth = width;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000270 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000271#ifdef SK_REPORT_API_RANGE_CHECK
272 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
273#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275}
276
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000277void SkPaint::setStrokeMiter(SkScalar limit) {
278 if (limit >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000279 GEN_ID_INC_EVAL(limit != fMiterLimit);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 fMiterLimit = limit;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000281 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000282#ifdef SK_REPORT_API_RANGE_CHECK
283 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
284#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000285 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286}
287
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000288void SkPaint::setStrokeCap(Cap ct) {
289 if ((unsigned)ct < kCapCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000290 GEN_ID_INC_EVAL((unsigned)ct != fCapType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 fCapType = SkToU8(ct);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000292 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000293#ifdef SK_REPORT_API_RANGE_CHECK
294 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
295#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297}
298
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000299void SkPaint::setStrokeJoin(Join jt) {
300 if ((unsigned)jt < kJoinCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000301 GEN_ID_INC_EVAL((unsigned)jt != fJoinType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 fJoinType = SkToU8(jt);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000303 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000304#ifdef SK_REPORT_API_RANGE_CHECK
305 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
306#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308}
309
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000310///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000312void SkPaint::setTextAlign(Align align) {
313 if ((unsigned)align < kAlignCount) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000314 GEN_ID_INC_EVAL((unsigned)align != fTextAlign);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 fTextAlign = SkToU8(align);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000316 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000317#ifdef SK_REPORT_API_RANGE_CHECK
318 SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
319#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321}
322
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000323void SkPaint::setTextSize(SkScalar ts) {
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +0000324 if (ts >= 0) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000325 GEN_ID_INC_EVAL(ts != fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 fTextSize = ts;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000327 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000328#ifdef SK_REPORT_API_RANGE_CHECK
329 SkDebugf("SkPaint::setTextSize() called with negative value\n");
330#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000331 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332}
333
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000334void SkPaint::setTextScaleX(SkScalar scaleX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000335 GEN_ID_INC_EVAL(scaleX != fTextScaleX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 fTextScaleX = scaleX;
337}
338
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000339void SkPaint::setTextSkewX(SkScalar skewX) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000340 GEN_ID_INC_EVAL(skewX != fTextSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fTextSkewX = skewX;
342}
343
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000344void SkPaint::setTextEncoding(TextEncoding encoding) {
345 if ((unsigned)encoding <= kGlyphID_TextEncoding) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000346 GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 fTextEncoding = encoding;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000348 } else {
reed@google.coma3237872011-07-05 19:20:48 +0000349#ifdef SK_REPORT_API_RANGE_CHECK
350 SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
351#endif
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353}
354
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000355///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000357SkTypeface* SkPaint::setTypeface(SkTypeface* font) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 SkRefCnt_SafeAssign(fTypeface, font);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000359 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 return font;
361}
362
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000363SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 SkRefCnt_SafeAssign(fRasterizer, r);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000365 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 return r;
367}
368
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000369SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 SkRefCnt_SafeAssign(fLooper, looper);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000371 GEN_ID_INC;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 return looper;
373}
374
reed@google.com15356a62011-11-03 19:29:08 +0000375SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) {
376 SkRefCnt_SafeAssign(fImageFilter, imageFilter);
377 GEN_ID_INC;
378 return imageFilter;
379}
380
reed@google.comb0a34d82012-07-11 19:57:55 +0000381SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
382 SkRefCnt_SafeAssign(fAnnotation, annotation);
383 GEN_ID_INC;
384
385 bool isNoDraw = annotation && annotation->isNoDraw();
386 fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag);
387
388 return annotation;
389}
390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391///////////////////////////////////////////////////////////////////////////////
392
393#include "SkGlyphCache.h"
394#include "SkUtils.h"
395
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000396static void DetachDescProc(const SkDescriptor* desc, void* context) {
397 *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
398}
399
djsollen@google.com56c69772011-11-08 19:00:26 +0000400#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000401const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
402 SkGlyphCache* cache;
403 descriptorProc(NULL, DetachDescProc, &cache, true);
404
405 const SkGlyph& glyph = cache->getUnicharMetrics(text);
406
407 SkGlyphCache::AttachCache(cache);
408 return glyph;
409}
410
djsollen@google.com60abb072012-02-15 18:49:15 +0000411const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId) {
412 SkGlyphCache* cache;
413 descriptorProc(NULL, DetachDescProc, &cache, true);
414
415 const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
416
417 SkGlyphCache::AttachCache(cache);
418 return glyph;
419}
420
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000421const void* SkPaint::findImage(const SkGlyph& glyph) {
422 // See ::detachCache()
423 SkGlyphCache* cache;
424 descriptorProc(NULL, DetachDescProc, &cache, true);
425
426 const void* image = cache->findImage(glyph);
427
428 SkGlyphCache::AttachCache(cache);
429 return image;
430}
431#endif
432
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
434 uint16_t glyphs[]) const {
435 if (byteLength == 0) {
436 return 0;
437 }
reed@google.com72cf4922011-01-04 19:58:20 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 SkASSERT(textData != NULL);
440
441 if (NULL == glyphs) {
442 switch (this->getTextEncoding()) {
443 case kUTF8_TextEncoding:
444 return SkUTF8_CountUnichars((const char*)textData, byteLength);
445 case kUTF16_TextEncoding:
446 return SkUTF16_CountUnichars((const uint16_t*)textData,
447 byteLength >> 1);
reed@google.com68bc6f72012-03-14 19:41:55 +0000448 case kUTF32_TextEncoding:
449 return byteLength >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 case kGlyphID_TextEncoding:
451 return byteLength >> 1;
452 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000453 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 }
455 return 0;
456 }
reed@google.com72cf4922011-01-04 19:58:20 +0000457
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 // if we get here, we have a valid glyphs[] array, so time to fill it in
reed@google.com72cf4922011-01-04 19:58:20 +0000459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 // handle this encoding before the setup for the glyphcache
461 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
462 // we want to ignore the low bit of byteLength
463 memcpy(glyphs, textData, byteLength >> 1 << 1);
464 return byteLength >> 1;
465 }
reed@google.com72cf4922011-01-04 19:58:20 +0000466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 SkAutoGlyphCache autoCache(*this, NULL);
468 SkGlyphCache* cache = autoCache.getCache();
469
470 const char* text = (const char*)textData;
471 const char* stop = text + byteLength;
472 uint16_t* gptr = glyphs;
473
474 switch (this->getTextEncoding()) {
475 case SkPaint::kUTF8_TextEncoding:
476 while (text < stop) {
477 *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
478 }
479 break;
480 case SkPaint::kUTF16_TextEncoding: {
481 const uint16_t* text16 = (const uint16_t*)text;
482 const uint16_t* stop16 = (const uint16_t*)stop;
483 while (text16 < stop16) {
484 *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
485 }
486 break;
487 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000488 case kUTF32_TextEncoding: {
489 const int32_t* text32 = (const int32_t*)text;
490 const int32_t* stop32 = (const int32_t*)stop;
491 while (text32 < stop32) {
492 *gptr++ = cache->unicharToGlyph(*text32++);
493 }
494 break;
495 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000497 SkDEBUGFAIL("unknown text encoding");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 }
499 return gptr - glyphs;
500}
501
reed@android.coma5dcaf62010-02-05 17:12:32 +0000502bool SkPaint::containsText(const void* textData, size_t byteLength) const {
503 if (0 == byteLength) {
504 return true;
505 }
reed@google.com72cf4922011-01-04 19:58:20 +0000506
reed@android.coma5dcaf62010-02-05 17:12:32 +0000507 SkASSERT(textData != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000508
reed@android.coma5dcaf62010-02-05 17:12:32 +0000509 // handle this encoding before the setup for the glyphcache
510 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
511 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
512 size_t count = byteLength >> 1;
513 for (size_t i = 0; i < count; i++) {
514 if (0 == glyphID[i]) {
515 return false;
516 }
517 }
518 return true;
519 }
reed@google.com72cf4922011-01-04 19:58:20 +0000520
reed@android.coma5dcaf62010-02-05 17:12:32 +0000521 SkAutoGlyphCache autoCache(*this, NULL);
522 SkGlyphCache* cache = autoCache.getCache();
523
524 switch (this->getTextEncoding()) {
525 case SkPaint::kUTF8_TextEncoding: {
526 const char* text = static_cast<const char*>(textData);
527 const char* stop = text + byteLength;
528 while (text < stop) {
529 if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) {
530 return false;
531 }
532 }
533 break;
534 }
535 case SkPaint::kUTF16_TextEncoding: {
536 const uint16_t* text = static_cast<const uint16_t*>(textData);
537 const uint16_t* stop = text + (byteLength >> 1);
538 while (text < stop) {
539 if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) {
540 return false;
541 }
542 }
543 break;
544 }
reed@google.com68bc6f72012-03-14 19:41:55 +0000545 case SkPaint::kUTF32_TextEncoding: {
546 const int32_t* text = static_cast<const int32_t*>(textData);
547 const int32_t* stop = text + (byteLength >> 2);
548 while (text < stop) {
549 if (0 == cache->unicharToGlyph(*text++)) {
550 return false;
551 }
552 }
553 break;
554 }
reed@android.coma5dcaf62010-02-05 17:12:32 +0000555 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000556 SkDEBUGFAIL("unknown text encoding");
reed@android.coma5dcaf62010-02-05 17:12:32 +0000557 return false;
558 }
559 return true;
560}
561
reed@android.com9d3a9852010-01-08 14:07:42 +0000562void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count,
563 SkUnichar textData[]) const {
564 if (count <= 0) {
565 return;
566 }
567
568 SkASSERT(glyphs != NULL);
569 SkASSERT(textData != NULL);
570
571 SkAutoGlyphCache autoCache(*this, NULL);
572 SkGlyphCache* cache = autoCache.getCache();
573
574 for (int index = 0; index < count; index++) {
575 textData[index] = cache->glyphToUnichar(glyphs[index]);
576 }
577}
578
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579///////////////////////////////////////////////////////////////////////////////
580
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000581static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
582 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 SkASSERT(cache != NULL);
584 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
587}
588
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000589static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache,
590 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 SkASSERT(cache != NULL);
592 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
595}
596
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000597static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
598 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 SkASSERT(cache != NULL);
600 SkASSERT(text != NULL);
reed@google.com68bc6f72012-03-14 19:41:55 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
603}
604
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000605static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache,
606 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(cache != NULL);
608 SkASSERT(text != NULL);
reed@google.com68bc6f72012-03-14 19:41:55 +0000609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
611}
612
reed@google.com68bc6f72012-03-14 19:41:55 +0000613static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
614 const char** text) {
615 SkASSERT(cache != NULL);
616 SkASSERT(text != NULL);
617
618 const int32_t* ptr = *(const int32_t**)text;
619 SkUnichar uni = *ptr++;
620 *text = (const char*)ptr;
621 return cache->getUnicharMetrics(uni);
622}
623
624static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
625 const char** text) {
626 SkASSERT(cache != NULL);
627 SkASSERT(text != NULL);
628
629 const int32_t* ptr = *(const int32_t**)text;
630 SkUnichar uni = *--ptr;
631 *text = (const char*)ptr;
632 return cache->getUnicharMetrics(uni);
633}
634
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000635static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
636 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 SkASSERT(cache != NULL);
638 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 const uint16_t* ptr = *(const uint16_t**)text;
641 unsigned glyphID = *ptr;
642 ptr += 1;
643 *text = (const char*)ptr;
644 return cache->getGlyphIDMetrics(glyphID);
645}
646
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000647static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache,
648 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 SkASSERT(cache != NULL);
650 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 const uint16_t* ptr = *(const uint16_t**)text;
653 ptr -= 1;
654 unsigned glyphID = *ptr;
655 *text = (const char*)ptr;
656 return cache->getGlyphIDMetrics(glyphID);
657}
658
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000659static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
660 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 SkASSERT(cache != NULL);
662 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
665}
666
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000667static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache,
668 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 SkASSERT(cache != NULL);
670 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000671
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
673}
674
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000675static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
676 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 SkASSERT(cache != NULL);
678 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000679
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
681}
682
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000683static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache,
684 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 SkASSERT(cache != NULL);
686 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000687
reed@android.com8a1c16f2008-12-17 15:59:43 +0000688 return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
689}
690
reed@google.com68bc6f72012-03-14 19:41:55 +0000691static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
692 const char** text) {
693 SkASSERT(cache != NULL);
694 SkASSERT(text != NULL);
695
696 const int32_t* ptr = *(const int32_t**)text;
697 SkUnichar uni = *ptr++;
698 *text = (const char*)ptr;
699 return cache->getUnicharAdvance(uni);
700}
701
702static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
703 const char** text) {
704 SkASSERT(cache != NULL);
705 SkASSERT(text != NULL);
706
707 const int32_t* ptr = *(const int32_t**)text;
708 SkUnichar uni = *--ptr;
709 *text = (const char*)ptr;
710 return cache->getUnicharAdvance(uni);
711}
712
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000713static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
714 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 SkASSERT(cache != NULL);
716 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 const uint16_t* ptr = *(const uint16_t**)text;
719 unsigned glyphID = *ptr;
720 ptr += 1;
721 *text = (const char*)ptr;
722 return cache->getGlyphIDAdvance(glyphID);
723}
724
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000725static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache,
726 const char** text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 SkASSERT(cache != NULL);
728 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 const uint16_t* ptr = *(const uint16_t**)text;
731 ptr -= 1;
732 unsigned glyphID = *ptr;
733 *text = (const char*)ptr;
734 return cache->getGlyphIDAdvance(glyphID);
735}
736
737SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000738 bool needFullMetrics) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 static const SkMeasureCacheProc gMeasureCacheProcs[] = {
740 sk_getMetrics_utf8_next,
741 sk_getMetrics_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000742 sk_getMetrics_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 sk_getMetrics_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 sk_getMetrics_utf8_prev,
746 sk_getMetrics_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000747 sk_getMetrics_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 sk_getMetrics_glyph_prev,
reed@google.com72cf4922011-01-04 19:58:20 +0000749
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 sk_getAdvance_utf8_next,
751 sk_getAdvance_utf16_next,
reed@google.com68bc6f72012-03-14 19:41:55 +0000752 sk_getAdvance_utf32_next,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 sk_getAdvance_glyph_next,
reed@google.com72cf4922011-01-04 19:58:20 +0000754
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 sk_getAdvance_utf8_prev,
756 sk_getAdvance_utf16_prev,
reed@google.com68bc6f72012-03-14 19:41:55 +0000757 sk_getAdvance_utf32_prev,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 sk_getAdvance_glyph_prev
759 };
reed@google.com72cf4922011-01-04 19:58:20 +0000760
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 unsigned index = this->getTextEncoding();
762
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000763 if (kBackward_TextBufferDirection == tbd) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000764 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000765 }
766 if (!needFullMetrics && !this->isDevKernText()) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000767 index += 8;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000768 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769
770 SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
771 return gMeasureCacheProcs[index];
772}
773
774///////////////////////////////////////////////////////////////////////////////
775
776static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000777 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 SkASSERT(cache != NULL);
779 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
782}
783
784static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000785 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 SkASSERT(cache != NULL);
787 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
790}
791
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000792static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache,
793 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 SkASSERT(cache != NULL);
795 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000796
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
798}
799
800static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000801 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 SkASSERT(cache != NULL);
803 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
806 x, y);
807}
808
reed@google.com68bc6f72012-03-14 19:41:55 +0000809static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
810 const char** text, SkFixed, SkFixed) {
811 SkASSERT(cache != NULL);
812 SkASSERT(text != NULL);
813
814 const int32_t* ptr = *(const int32_t**)text;
815 SkUnichar uni = *ptr++;
816 *text = (const char*)ptr;
817 return cache->getUnicharMetrics(uni);
818}
819
820static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
821 const char** text, SkFixed x, SkFixed y) {
822 SkASSERT(cache != NULL);
823 SkASSERT(text != NULL);
824
825 const int32_t* ptr = *(const int32_t**)text;
826 SkUnichar uni = *--ptr;
827 *text = (const char*)ptr;
828 return cache->getUnicharMetrics(uni);
829}
830
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000831static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
832 const char** text, SkFixed, SkFixed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 SkASSERT(cache != NULL);
834 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000835
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 const uint16_t* ptr = *(const uint16_t**)text;
837 unsigned glyphID = *ptr;
838 ptr += 1;
839 *text = (const char*)ptr;
840 return cache->getGlyphIDMetrics(glyphID);
841}
842
843static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000844 const char** text, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 SkASSERT(cache != NULL);
846 SkASSERT(text != NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 const uint16_t* ptr = *(const uint16_t**)text;
849 unsigned glyphID = *ptr;
850 ptr += 1;
851 *text = (const char*)ptr;
852 return cache->getGlyphIDMetrics(glyphID, x, y);
853}
854
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000855SkDrawCacheProc SkPaint::getDrawCacheProc() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 static const SkDrawCacheProc gDrawCacheProcs[] = {
857 sk_getMetrics_utf8_00,
858 sk_getMetrics_utf16_00,
reed@google.com68bc6f72012-03-14 19:41:55 +0000859 sk_getMetrics_utf32_00,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 sk_getMetrics_glyph_00,
reed@google.com72cf4922011-01-04 19:58:20 +0000861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 sk_getMetrics_utf8_xy,
863 sk_getMetrics_utf16_xy,
reed@google.com68bc6f72012-03-14 19:41:55 +0000864 sk_getMetrics_utf32_xy,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 sk_getMetrics_glyph_xy
866 };
reed@google.com72cf4922011-01-04 19:58:20 +0000867
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 unsigned index = this->getTextEncoding();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000869 if (fFlags & kSubpixelText_Flag) {
reed@google.com68bc6f72012-03-14 19:41:55 +0000870 index += 4;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000871 }
reed@google.com72cf4922011-01-04 19:58:20 +0000872
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
874 return gDrawCacheProcs[index];
875}
876
877///////////////////////////////////////////////////////////////////////////////
878
879class SkAutoRestorePaintTextSizeAndFrame {
880public:
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000881 SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
882 : fPaint((SkPaint*)paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 fTextSize = paint->getTextSize();
884 fStyle = paint->getStyle();
885 fPaint->setStyle(SkPaint::kFill_Style);
886 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000887
888 ~SkAutoRestorePaintTextSizeAndFrame() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 fPaint->setStyle(fStyle);
890 fPaint->setTextSize(fTextSize);
891 }
reed@google.com72cf4922011-01-04 19:58:20 +0000892
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893private:
894 SkPaint* fPaint;
895 SkScalar fTextSize;
896 SkPaint::Style fStyle;
897};
898
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000899static void set_bounds(const SkGlyph& g, SkRect* bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 bounds->set(SkIntToScalar(g.fLeft),
901 SkIntToScalar(g.fTop),
902 SkIntToScalar(g.fLeft + g.fWidth),
903 SkIntToScalar(g.fTop + g.fHeight));
904}
905
reed@android.come88f5512010-03-19 14:42:28 +0000906// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so
907// we don't overflow along the way
908typedef int64_t Sk48Dot16;
909
910#ifdef SK_SCALAR_IS_FLOAT
911 static inline float Sk48Dot16ToScalar(Sk48Dot16 x) {
reed@android.come89d3ec2010-05-18 21:23:30 +0000912 return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f)
reed@android.come88f5512010-03-19 14:42:28 +0000913 }
914#else
915 static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) {
916 // just return the low 32bits
917 return static_cast<SkFixed>(x);
918 }
919#endif
920
reed@google.com44da42e2011-11-10 20:04:47 +0000921static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) {
reed@android.come88f5512010-03-19 14:42:28 +0000922 SkScalar sx = Sk48Dot16ToScalar(dx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 bounds->join(SkIntToScalar(g.fLeft) + sx,
924 SkIntToScalar(g.fTop),
925 SkIntToScalar(g.fLeft + g.fWidth) + sx,
926 SkIntToScalar(g.fTop + g.fHeight));
927}
928
reed@google.com44da42e2011-11-10 20:04:47 +0000929static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) {
930 SkScalar sy = Sk48Dot16ToScalar(dy);
931 bounds->join(SkIntToScalar(g.fLeft),
932 SkIntToScalar(g.fTop) + sy,
933 SkIntToScalar(g.fLeft + g.fWidth),
934 SkIntToScalar(g.fTop + g.fHeight) + sy);
935}
936
937typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16);
938
939// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
940static SkFixed advance(const SkGlyph& glyph, int xyIndex) {
941 SkASSERT(0 == xyIndex || 1 == xyIndex);
942 return (&glyph.fAdvanceX)[xyIndex];
943}
944
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945SkScalar SkPaint::measure_text(SkGlyphCache* cache,
946 const char* text, size_t byteLength,
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000947 int* count, SkRect* bounds) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 SkASSERT(count);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000949 if (byteLength == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 *count = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000951 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 bounds->setEmpty();
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000953 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 return 0;
955 }
956
957 SkMeasureCacheProc glyphCacheProc;
958 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
959 NULL != bounds);
960
reed@google.com44da42e2011-11-10 20:04:47 +0000961 int xyIndex;
962 JoinBoundsProc joinBoundsProc;
963 if (this->isVerticalText()) {
964 xyIndex = 1;
965 joinBoundsProc = join_bounds_y;
966 } else {
967 xyIndex = 0;
968 joinBoundsProc = join_bounds_x;
969 }
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 int n = 1;
972 const char* stop = (const char*)text + byteLength;
973 const SkGlyph* g = &glyphCacheProc(cache, &text);
reed@android.come88f5512010-03-19 14:42:28 +0000974 // our accumulated fixed-point advances might overflow 16.16, so we use
975 // a 48.16 (64bit) accumulator, and then convert that to scalar at the
976 // very end.
reed@google.com44da42e2011-11-10 20:04:47 +0000977 Sk48Dot16 x = advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
979 SkAutoKern autokern;
980
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000981 if (NULL == bounds) {
982 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 int rsb;
984 for (; text < stop; n++) {
985 rsb = g->fRsbDelta;
986 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +0000987 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000989 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 for (; text < stop; n++) {
reed@google.com44da42e2011-11-10 20:04:47 +0000991 x += advance(glyphCacheProc(cache, &text), xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 }
993 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000994 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 set_bounds(*g, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +0000996 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 int rsb;
998 for (; text < stop; n++) {
999 rsb = g->fRsbDelta;
1000 g = &glyphCacheProc(cache, &text);
1001 x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
reed@google.com44da42e2011-11-10 20:04:47 +00001002 joinBoundsProc(*g, bounds, x);
1003 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001005 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 for (; text < stop; n++) {
1007 g = &glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001008 joinBoundsProc(*g, bounds, x);
1009 x += advance(*g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 }
1011 }
1012 }
1013 SkASSERT(text == stop);
1014
1015 *count = n;
reed@android.come88f5512010-03-19 14:42:28 +00001016 return Sk48Dot16ToScalar(x);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017}
1018
1019SkScalar SkPaint::measureText(const void* textData, size_t length,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001020 SkRect* bounds, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 const char* text = (const char*)textData;
1022 SkASSERT(text != NULL || length == 0);
1023
1024 SkScalar scale = 0;
1025 SkAutoRestorePaintTextSizeAndFrame restore(this);
1026
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001027 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 scale = fTextSize / kCanonicalTextSizeForPaths;
1029 // this gets restored by restore
1030 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1031 }
reed@google.com72cf4922011-01-04 19:58:20 +00001032
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001033 SkMatrix zoomMatrix, *zoomPtr = NULL;
1034 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 zoomMatrix.setScale(zoom, zoom);
1036 zoomPtr = &zoomMatrix;
1037 }
1038
1039 SkAutoGlyphCache autoCache(*this, zoomPtr);
1040 SkGlyphCache* cache = autoCache.getCache();
1041
1042 SkScalar width = 0;
reed@google.com72cf4922011-01-04 19:58:20 +00001043
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001044 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 int tempCount;
1046
1047 width = this->measure_text(cache, text, length, &tempCount, bounds);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001048 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 width = SkScalarMul(width, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001050 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
1052 bounds->fTop = SkScalarMul(bounds->fTop, scale);
1053 bounds->fRight = SkScalarMul(bounds->fRight, scale);
1054 bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
1055 }
1056 }
1057 }
1058 return width;
1059}
1060
1061typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
1062
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001063static bool forward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 return text < stop;
1065}
1066
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001067static bool backward_textBufferPred(const char* text, const char* stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 return text > stop;
1069}
1070
1071static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001072 const char** text, size_t length,
1073 const char** stop) {
1074 if (SkPaint::kForward_TextBufferDirection == tbd) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 *stop = *text + length;
1076 return forward_textBufferPred;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001077 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 // text should point to the end of the buffer, and stop to the beginning
1079 *stop = *text;
1080 *text += length;
1081 return backward_textBufferPred;
1082 }
1083}
1084
1085size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
1086 SkScalar* measuredWidth,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001087 TextBufferDirection tbd) const {
1088 if (0 == length || 0 >= maxWidth) {
1089 if (measuredWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 *measuredWidth = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 return 0;
1093 }
1094
djsollen@google.comc6f2e7d2012-01-04 18:43:43 +00001095 if (0 == fTextSize) {
1096 if (measuredWidth) {
1097 *measuredWidth = 0;
1098 }
1099 return length;
1100 }
1101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 SkASSERT(textD != NULL);
1103 const char* text = (const char*)textD;
1104
1105 SkScalar scale = 0;
1106 SkAutoRestorePaintTextSizeAndFrame restore(this);
1107
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001108 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 scale = fTextSize / kCanonicalTextSizeForPaths;
reed@android.com647d3da2010-05-17 14:15:14 +00001110 maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 // this gets restored by restore
1112 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1113 }
reed@google.com72cf4922011-01-04 19:58:20 +00001114
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 SkAutoGlyphCache autoCache(*this, NULL);
1116 SkGlyphCache* cache = autoCache.getCache();
1117
1118 SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
1119 const char* stop;
1120 SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
reed@google.com44da42e2011-11-10 20:04:47 +00001121 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.come88f5512010-03-19 14:42:28 +00001122 // use 64bits for our accumulator, to avoid overflowing 16.16
1123 Sk48Dot16 max = SkScalarToFixed(maxWidth);
1124 Sk48Dot16 width = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125
1126 SkAutoKern autokern;
1127
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001128 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 int rsb = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001130 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 const char* curr = text;
1132 const SkGlyph& g = glyphCacheProc(cache, &text);
reed@google.com44da42e2011-11-10 20:04:47 +00001133 SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001134 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 width -= x;
1136 text = curr;
1137 break;
1138 }
1139 rsb = g.fRsbDelta;
1140 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001141 } else {
1142 while (pred(text, stop)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 const char* curr = text;
reed@google.com44da42e2011-11-10 20:04:47 +00001144 SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001145 if ((width += x) > max) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 width -= x;
1147 text = curr;
1148 break;
1149 }
1150 }
1151 }
reed@google.com72cf4922011-01-04 19:58:20 +00001152
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001153 if (measuredWidth) {
reed@android.come88f5512010-03-19 14:42:28 +00001154 SkScalar scalarWidth = Sk48Dot16ToScalar(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001155 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 scalarWidth = SkScalarMul(scalarWidth, scale);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 *measuredWidth = scalarWidth;
1159 }
reed@google.com72cf4922011-01-04 19:58:20 +00001160
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 // return the number of bytes measured
1162 return (kForward_TextBufferDirection == tbd) ?
1163 text - stop + length : stop - text + length;
1164}
1165
1166///////////////////////////////////////////////////////////////////////////////
1167
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001168static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
1170 return false; // don't detach the cache
1171}
1172
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001173static void FontMetricsDescProc(const SkDescriptor* desc, void* context) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
1175}
1176
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001177SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178 SkScalar scale = 0;
1179 SkAutoRestorePaintTextSizeAndFrame restore(this);
1180
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001181 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 scale = fTextSize / kCanonicalTextSizeForPaths;
1183 // this gets restored by restore
1184 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1185 }
reed@google.com72cf4922011-01-04 19:58:20 +00001186
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001187 SkMatrix zoomMatrix, *zoomPtr = NULL;
1188 if (zoom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 zoomMatrix.setScale(zoom, zoom);
1190 zoomPtr = &zoomMatrix;
1191 }
1192
1193#if 0
1194 SkAutoGlyphCache autoCache(*this, zoomPtr);
1195 SkGlyphCache* cache = autoCache.getCache();
1196 const FontMetrics& my = cache->getFontMetricsY();
1197#endif
1198 FontMetrics storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001199 if (NULL == metrics) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 metrics = &storage;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001201 }
reed@google.com72cf4922011-01-04 19:58:20 +00001202
reed@google.comffe49f52011-11-22 19:42:41 +00001203 this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001205 if (scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 metrics->fTop = SkScalarMul(metrics->fTop, scale);
1207 metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
1208 metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
1209 metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
1210 metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
1211 }
1212 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
1213}
1214
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001215///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001217static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218 bounds->set(g.fLeft * scale,
1219 g.fTop * scale,
1220 (g.fLeft + g.fWidth) * scale,
1221 (g.fTop + g.fHeight) * scale);
1222}
1223
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001224int SkPaint::getTextWidths(const void* textData, size_t byteLength,
1225 SkScalar widths[], SkRect bounds[]) const {
1226 if (0 == byteLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 return 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001228 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229
1230 SkASSERT(NULL != textData);
1231
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001232 if (NULL == widths && NULL == bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 return this->countText(textData, byteLength);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001234 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235
1236 SkAutoRestorePaintTextSizeAndFrame restore(this);
1237 SkScalar scale = 0;
1238
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001239 if (this->isLinearText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 scale = fTextSize / kCanonicalTextSizeForPaths;
1241 // this gets restored by restore
1242 ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
1243 }
1244
1245 SkAutoGlyphCache autoCache(*this, NULL);
1246 SkGlyphCache* cache = autoCache.getCache();
1247 SkMeasureCacheProc glyphCacheProc;
1248 glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
1249 NULL != bounds);
1250
1251 const char* text = (const char*)textData;
1252 const char* stop = text + byteLength;
1253 int count = 0;
reed@google.com44da42e2011-11-10 20:04:47 +00001254 const int xyIndex = this->isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001256 if (this->isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257 // we adjust the widths returned here through auto-kerning
1258 SkAutoKern autokern;
1259 SkFixed prevWidth = 0;
1260
1261 if (scale) {
1262 while (text < stop) {
1263 const SkGlyph& g = glyphCacheProc(cache, &text);
1264 if (widths) {
1265 SkFixed adjust = autokern.adjust(g);
1266
1267 if (count > 0) {
1268 SkScalar w = SkFixedToScalar(prevWidth + adjust);
1269 *widths++ = SkScalarMul(w, scale);
1270 }
reed@google.com44da42e2011-11-10 20:04:47 +00001271 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 }
1273 if (bounds) {
1274 set_bounds(g, bounds++, scale);
1275 }
1276 ++count;
1277 }
1278 if (count > 0 && widths) {
1279 *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
1280 }
1281 } else {
1282 while (text < stop) {
1283 const SkGlyph& g = glyphCacheProc(cache, &text);
1284 if (widths) {
1285 SkFixed adjust = autokern.adjust(g);
1286
1287 if (count > 0) {
1288 *widths++ = SkFixedToScalar(prevWidth + adjust);
1289 }
reed@google.com44da42e2011-11-10 20:04:47 +00001290 prevWidth = advance(g, xyIndex);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 }
1292 if (bounds) {
1293 set_bounds(g, bounds++);
1294 }
1295 ++count;
1296 }
1297 if (count > 0 && widths) {
1298 *widths = SkFixedToScalar(prevWidth);
1299 }
1300 }
1301 } else { // no devkern
1302 if (scale) {
1303 while (text < stop) {
1304 const SkGlyph& g = glyphCacheProc(cache, &text);
1305 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001306 *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 scale);
1308 }
1309 if (bounds) {
1310 set_bounds(g, bounds++, scale);
1311 }
1312 ++count;
1313 }
1314 } else {
1315 while (text < stop) {
1316 const SkGlyph& g = glyphCacheProc(cache, &text);
1317 if (widths) {
reed@google.com44da42e2011-11-10 20:04:47 +00001318 *widths++ = SkFixedToScalar(advance(g, xyIndex));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 }
1320 if (bounds) {
1321 set_bounds(g, bounds++);
1322 }
1323 ++count;
1324 }
1325 }
1326 }
1327
1328 SkASSERT(text == stop);
1329 return count;
1330}
1331
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001332///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333
1334#include "SkDraw.h"
1335
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001336void SkPaint::getTextPath(const void* textData, size_t length,
1337 SkScalar x, SkScalar y, SkPath* path) const {
1338 SkASSERT(length == 0 || textData != NULL);
1339
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 const char* text = (const char*)textData;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001341 if (text == NULL || length == 0 || path == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 return;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001343 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344
djsollen@google.com166e6532012-03-20 14:24:38 +00001345 SkTextToPathIter iter(text, length, *this, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 SkMatrix matrix;
1347 SkScalar prevXPos = 0;
1348
1349 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1350 matrix.postTranslate(x, y);
1351 path->reset();
1352
1353 SkScalar xpos;
1354 const SkPath* iterPath;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001355 while ((iterPath = iter.next(&xpos)) != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 matrix.postTranslate(xpos - prevXPos, 0);
1357 path->addPath(*iterPath, matrix);
1358 prevXPos = xpos;
1359 }
1360}
1361
reed@google.comca0062e2012-07-20 11:20:32 +00001362void SkPaint::getPosTextPath(const void* textData, size_t length,
1363 const SkPoint pos[], SkPath* path) const {
1364 SkASSERT(length == 0 || textData != NULL);
1365
1366 const char* text = (const char*)textData;
1367 if (text == NULL || length == 0 || path == NULL) {
1368 return;
1369 }
1370
1371 SkTextToPathIter iter(text, length, *this, false);
1372 SkMatrix matrix;
1373 SkPoint prevPos;
1374 prevPos.set(0, 0);
1375
1376 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1377 path->reset();
1378
1379 unsigned int i = 0;
1380 const SkPath* iterPath;
1381 while ((iterPath = iter.next(NULL)) != NULL) {
1382 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
1383 path->addPath(*iterPath, matrix);
1384 prevPos = pos[i];
1385 i++;
1386 }
1387}
1388
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389static void add_flattenable(SkDescriptor* desc, uint32_t tag,
1390 SkFlattenableWriteBuffer* buffer) {
1391 buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
1392}
1393
reed@google.com2739b272011-09-28 17:26:42 +00001394// SkFontHost can override this choice in FilterRec()
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001395static SkMask::Format computeMaskFormat(const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 uint32_t flags = paint.getFlags();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001398 // Antialiasing being disabled trumps all other settings.
reed@google.com65dd8f82011-03-14 13:31:16 +00001399 if (!(flags & SkPaint::kAntiAlias_Flag)) {
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001400 return SkMask::kBW_Format;
reed@google.comf88d6762011-03-10 15:06:27 +00001401 }
1402
reed@google.com65dd8f82011-03-14 13:31:16 +00001403 if (flags & SkPaint::kLCDRenderText_Flag) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001404 return SkMask::kLCD16_Format;
reed@google.com65dd8f82011-03-14 13:31:16 +00001405 }
agl@chromium.orgb4234a22010-01-21 11:43:44 +00001406
1407 return SkMask::kA8_Format;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408}
1409
reed@android.com1cdcb512009-08-24 19:11:00 +00001410// if linear-text is on, then we force hinting to be off (since that's sort of
1411// the point of linear-text.
1412static SkPaint::Hinting computeHinting(const SkPaint& paint) {
1413 SkPaint::Hinting h = paint.getHinting();
1414 if (paint.isLinearText()) {
1415 h = SkPaint::kNo_Hinting;
1416 }
1417 return h;
1418}
1419
reed@google.com1f6b4ae2011-11-22 14:20:55 +00001420// return true if the paint is just a single color (i.e. not a shader). If its
1421// a shader, then we can't compute a const luminance for it :(
1422static bool justAColor(const SkPaint& paint, SkColor* color) {
1423 if (paint.getShader()) {
1424 return false;
1425 }
1426 SkColor c = paint.getColor();
1427 if (paint.getColorFilter()) {
1428 c = paint.getColorFilter()->filterColor(c);
1429 }
1430 if (color) {
1431 *color = c;
1432 }
1433 return true;
1434}
1435
reed@google.comce6dbb62012-02-10 22:01:45 +00001436static SkColor computeLuminanceColor(const SkPaint& paint) {
1437 SkColor c;
1438 if (!justAColor(paint, &c)) {
1439 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
1440 }
1441 return c;
1442}
reed@google.com813d38b2012-02-13 21:37:57 +00001443
reed@google.comdd43df92012-02-15 14:50:29 +00001444#define assert_byte(x) SkASSERT(0 == ((x) >> 8))
1445
reed@google.com4f79b9b2011-09-13 13:23:26 +00001446// Beyond this size, LCD doesn't appreciably improve quality, but it always
1447// cost more RAM and draws slower, so we set a cap.
reed@google.comc27b7412011-09-13 17:20:30 +00001448#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1449 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1450#endif
reed@google.com4f79b9b2011-09-13 13:23:26 +00001451
1452static bool tooBigForLCD(const SkScalerContext::Rec& rec) {
1453 SkScalar area = SkScalarMul(rec.fPost2x2[0][0], rec.fPost2x2[1][1]) -
1454 SkScalarMul(rec.fPost2x2[1][0], rec.fPost2x2[0][1]);
1455 SkScalar size = SkScalarMul(area, rec.fTextSize);
reed@google.comc27b7412011-09-13 17:20:30 +00001456 return SkScalarAbs(size) > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT);
reed@google.com4f79b9b2011-09-13 13:23:26 +00001457}
1458
reed@google.com72cf4922011-01-04 19:58:20 +00001459/*
1460 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1461 * that vary only slightly when we create our key into the font cache, since the font scaler
1462 * typically returns the same looking resuts for tiny changes in the matrix.
1463 */
reed@android.comf2b98d62010-12-20 18:26:13 +00001464static SkScalar sk_relax(SkScalar x) {
reed@google.com72cf4922011-01-04 19:58:20 +00001465#ifdef SK_SCALAR_IS_FLOAT
reed@android.comf2b98d62010-12-20 18:26:13 +00001466 int n = sk_float_round2int(x * 1024);
1467 return n / 1024.0f;
reed@google.com72cf4922011-01-04 19:58:20 +00001468#else
1469 // round to the nearest 10 fractional bits
1470 return (x + (1 << 5)) & ~(1024 - 1);
1471#endif
reed@android.comf2b98d62010-12-20 18:26:13 +00001472}
1473
bungeman@google.com97efada2012-07-30 20:40:50 +00001474//#define SK_GAMMA_SRGB
1475#ifndef SK_GAMMA_CONTRAST
1476 /**
1477 * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
1478 * With lower values small text appears washed out (though correctly so).
1479 * With higher values lcd fringing is worse and the smoothing effect of
1480 * partial coverage is diminished.
1481 */
1482 #define SK_GAMMA_CONTRAST (0.5f)
1483#endif
1484#ifndef SK_GAMMA_EXPONENT
1485 #define SK_GAMMA_EXPONENT (2.2f)
1486#endif
1487
reed@android.com36a4c2a2009-07-22 19:52:11 +00001488void SkScalerContext::MakeRec(const SkPaint& paint,
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001489 const SkMatrix* deviceMatrix, Rec* rec) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001490 SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491
reed@google.com7d26c592011-06-13 13:01:10 +00001492 rec->fOrigFontID = SkTypeface::UniqueID(paint.getTypeface());
1493 rec->fFontID = rec->fOrigFontID;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 rec->fTextSize = paint.getTextSize();
1495 rec->fPreScaleX = paint.getTextScaleX();
1496 rec->fPreSkewX = paint.getTextSkewX();
1497
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001498 if (deviceMatrix) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001499 rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
1500 rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX());
1501 rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY());
1502 rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY());
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001503 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1505 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1506 }
reed@google.com72cf4922011-01-04 19:58:20 +00001507
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 SkPaint::Style style = paint.getStyle();
1509 SkScalar strokeWidth = paint.getStrokeWidth();
reed@google.com72cf4922011-01-04 19:58:20 +00001510
reed@google.comffe49f52011-11-22 19:42:41 +00001511 unsigned flags = 0;
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001512
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001513 if (paint.isFakeBoldText()) {
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001514#ifdef SK_USE_FREETYPE_EMBOLDEN
1515 flags |= SkScalerContext::kEmbolden_Flag;
1516#else
vandebo@chromium.org28be72b2010-11-11 21:37:00 +00001517 SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(),
1518 kStdFakeBoldInterpKeys,
1519 kStdFakeBoldInterpValues,
1520 kStdFakeBoldInterpLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521 SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
reed@google.com72cf4922011-01-04 19:58:20 +00001522
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001523 if (style == SkPaint::kFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 style = SkPaint::kStrokeAndFill_Style;
1525 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001526 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527 strokeWidth += extra;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001528 }
senorblanco@chromium.org4526a842010-02-05 23:08:20 +00001529#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 }
1531
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001532 if (paint.isDevKernText()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533 flags |= SkScalerContext::kDevKernText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001534 }
reed@google.com72cf4922011-01-04 19:58:20 +00001535
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001536 if (style != SkPaint::kFill_Style && strokeWidth > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 rec->fFrameWidth = strokeWidth;
1538 rec->fMiterLimit = paint.getStrokeMiter();
1539 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1540
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001541 if (style == SkPaint::kStrokeAndFill_Style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 flags |= SkScalerContext::kFrameAndFill_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001543 }
1544 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 rec->fFrameWidth = 0;
1546 rec->fMiterLimit = 0;
1547 rec->fStrokeJoin = 0;
1548 }
1549
reed@google.com02b53312011-05-18 19:00:53 +00001550 rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
1551
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +00001552 if (SkMask::kLCD16_Format == rec->fMaskFormat ||
1553 SkMask::kLCD32_Format == rec->fMaskFormat)
1554 {
reed@google.com02b53312011-05-18 19:00:53 +00001555 SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
1556 SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
reed@google.com4f79b9b2011-09-13 13:23:26 +00001557 if (SkFontHost::kNONE_LCDOrder == order || tooBigForLCD(*rec)) {
reed@google.com02b53312011-05-18 19:00:53 +00001558 // eeek, can't support LCD
1559 rec->fMaskFormat = SkMask::kA8_Format;
1560 } else {
1561 if (SkFontHost::kVertical_LCDOrientation == orient) {
1562 flags |= SkScalerContext::kLCD_Vertical_Flag;
1563 }
1564 if (SkFontHost::kBGR_LCDOrder == order) {
1565 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1566 }
1567 }
1568 }
1569
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001570 if (paint.isEmbeddedBitmapText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001571 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001572 }
1573 if (paint.isSubpixelText()) {
reed@google.com02b53312011-05-18 19:00:53 +00001574 flags |= SkScalerContext::kSubpixelPositioning_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001575 }
1576 if (paint.isAutohinted()) {
reed@google.com02b53312011-05-18 19:00:53 +00001577 flags |= SkScalerContext::kAutohinting_Flag;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001578 }
reed@google.com830a23e2011-11-10 15:20:49 +00001579 if (paint.isVerticalText()) {
1580 flags |= SkScalerContext::kVertical_Flag;
1581 }
reed@google.com8351aab2012-01-18 17:06:35 +00001582 if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
1583 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1584 }
reed@google.com02b53312011-05-18 19:00:53 +00001585 rec->fFlags = SkToU16(flags);
reed@android.com36a4c2a2009-07-22 19:52:11 +00001586
reed@google.comffe49f52011-11-22 19:42:41 +00001587 // these modify fFlags, so do them after assigning fFlags
reed@google.com9d757672011-05-18 21:14:39 +00001588 rec->setHinting(computeHinting(paint));
1589
bungeman@google.com97efada2012-07-30 20:40:50 +00001590 rec->setLuminanceColor(computeLuminanceColor(paint));
1591#ifdef SK_GAMMA_SRGB
1592 rec->setDeviceGamma(0);
1593 rec->setPaintGamma(0);
1594#else
1595 rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
1596 rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
1597#endif
1598 rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST));
1599
reed@android.com36a4c2a2009-07-22 19:52:11 +00001600 /* Allow the fonthost to modify our rec before we use it as a key into the
1601 cache. This way if we're asking for something that they will ignore,
1602 they can modify our rec up front, so we don't create duplicate cache
1603 entries.
1604 */
1605 SkFontHost::FilterRec(rec);
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001606
reed@google.com10d2d4d2012-03-01 22:32:51 +00001607 // be sure to call PostMakeRec(rec) before you actually use it!
1608}
1609
1610/**
bungeman@google.com97efada2012-07-30 20:40:50 +00001611 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
1612 * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
1613 * to hold it until the returned pointer is refed or forgotten.
1614 */
1615SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
1616
1617/**
1618 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1619 * the returned SkColorSpaceLuminance pointer is refed or forgotten.
1620 */
1621static SkColorSpaceLuminance* cachedDeviceLuminance(SkScalar gammaExponent) {
1622 static SkColorSpaceLuminance* gDeviceLuminance = NULL;
1623 static SkScalar gGammaExponent = SK_ScalarMin;
1624 if (gGammaExponent != gammaExponent) {
1625 if (0 == gammaExponent) {
1626 gDeviceLuminance = SkNEW(SkSRGBLuminance);
1627 } else {
1628 gDeviceLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
1629 }
1630 gGammaExponent = gammaExponent;
1631 }
1632 return gDeviceLuminance;
1633}
1634
1635/**
1636 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1637 * the returned SkColorSpaceLuminance pointer is refed or forgotten.
1638 */
1639static SkColorSpaceLuminance* cachedPaintLuminance(SkScalar gammaExponent) {
1640 static SkColorSpaceLuminance* gPaintLuminance = NULL;
1641 static SkScalar gGammaExponent = SK_ScalarMin;
1642 if (gGammaExponent != gammaExponent) {
1643 if (0 == gammaExponent) {
1644 gPaintLuminance = SkNEW(SkSRGBLuminance);
1645 } else {
1646 gPaintLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent));
1647 }
1648 gGammaExponent = gammaExponent;
1649 }
1650 return gPaintLuminance;
1651}
1652
1653/**
1654 * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
1655 * the returned SkMaskGamma pointer is refed or forgotten.
1656 */
1657static SkMaskGamma* cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
1658 static SkMaskGamma* gMaskGamma = NULL;
1659 static SkScalar gContrast = SK_ScalarMin;
1660 static SkScalar gPaintGamma = SK_ScalarMin;
1661 static SkScalar gDeviceGamma = SK_ScalarMin;
1662 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
1663 SkSafeUnref(gMaskGamma);
1664 SkColorSpaceLuminance* paintLuminance = cachedPaintLuminance(paintGamma);
1665 SkColorSpaceLuminance* deviceLuminance = cachedDeviceLuminance(deviceGamma);
1666 gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, *paintLuminance, *deviceLuminance));
1667 gContrast = contrast;
1668 gPaintGamma = paintGamma;
1669 gDeviceGamma = deviceGamma;
1670 }
1671 return gMaskGamma;
1672}
1673
1674/**
reed@google.com10d2d4d2012-03-01 22:32:51 +00001675 * We ensure that the rec is self-consistent and efficient (where possible)
1676 */
bungeman@google.com97efada2012-07-30 20:40:50 +00001677void SkScalerContext::PostMakeRec(const SkPaint& paint, SkScalerContext::Rec* rec) {
reed@google.com10d2d4d2012-03-01 22:32:51 +00001678 /**
1679 * If we're asking for A8, we force the colorlum to be gray, since that
bungeman@google.com97efada2012-07-30 20:40:50 +00001680 * limits the number of unique entries, and the scaler will only look at
1681 * the lum of one of them.
reed@google.com10d2d4d2012-03-01 22:32:51 +00001682 */
reed@google.comdd43df92012-02-15 14:50:29 +00001683 switch (rec->fMaskFormat) {
1684 case SkMask::kLCD16_Format:
1685 case SkMask::kLCD32_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001686 // filter down the luminance color to a finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001687 SkColor color = rec->getLuminanceColor();
1688 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1689 SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(),
1690 rec->getPaintGamma(),
1691 rec->getDeviceGamma());
1692 rec->setLuminanceColor(maskGamma->cannonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001693 break;
1694 }
1695 case SkMask::kA8_Format: {
reed@google.comdd43df92012-02-15 14:50:29 +00001696 // filter down the luminance to a single component, since A8 can't
1697 // use per-component information
bungeman@google.com97efada2012-07-30 20:40:50 +00001698
1699 SkColor color = rec->getLuminanceColor();
1700 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1701 U8CPU lum = cachedPaintLuminance(rec->getPaintGamma())->computeLuminance(color);
1702 // HACK: Prevents green from being pre-blended as white.
1703 lum -= ((255 - lum) * lum) / 255;
1704
reed@google.comdd43df92012-02-15 14:50:29 +00001705 // reduce to our finite number of bits
bungeman@google.com97efada2012-07-30 20:40:50 +00001706 SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(),
1707 rec->getPaintGamma(),
1708 rec->getDeviceGamma());
1709 color = SkColorSetRGB(lum, lum, lum);
1710 rec->setLuminanceColor(maskGamma->cannonicalColor(color));
reed@google.comdd43df92012-02-15 14:50:29 +00001711 break;
1712 }
1713 case SkMask::kBW_Format:
1714 // No need to differentiate gamma if we're BW
reed@google.comdd43df92012-02-15 14:50:29 +00001715 rec->setLuminanceColor(0);
reed@google.comdd43df92012-02-15 14:50:29 +00001716 break;
reed@google.com3e8ae5b2011-09-28 15:32:20 +00001717 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718}
1719
1720#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
1721
reed@google.com17fb3872011-05-04 14:31:07 +00001722#ifdef SK_DEBUG
1723 #define TEST_DESC
1724#endif
1725
reed@google.comffe49f52011-11-22 19:42:41 +00001726/*
1727 * ignoreGamma tells us that the caller just wants metrics that are unaffected
1728 * by gamma correction, so we jam the luminance field to 0 (most common value
1729 * for black text) in hopes that we get a cache hit easier. A better solution
1730 * would be for the fontcache lookup to know to ignore the luminance field
1731 * entirely, but not sure how to do that and keep it fast.
1732 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
1734 void (*proc)(const SkDescriptor*, void*),
djsollen@google.com57f49692011-02-23 20:46:31 +00001735 void* context, bool ignoreGamma) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 SkScalerContext::Rec rec;
1737
1738 SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
djsollen@google.com57f49692011-02-23 20:46:31 +00001739 if (ignoreGamma) {
reed@google.comce6dbb62012-02-10 22:01:45 +00001740 rec.setLuminanceColor(0);
djsollen@google.com57f49692011-02-23 20:46:31 +00001741 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742
1743 size_t descSize = sizeof(rec);
1744 int entryCount = 1;
1745 SkPathEffect* pe = this->getPathEffect();
1746 SkMaskFilter* mf = this->getMaskFilter();
1747 SkRasterizer* ra = this->getRasterizer();
1748
djsollen@google.com2b2ede32012-04-12 13:24:04 +00001749 SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1750 SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
1751 SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752
1753 if (pe) {
1754 peBuffer.writeFlattenable(pe);
1755 descSize += peBuffer.size();
1756 entryCount += 1;
1757 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1758 // seems like we could support kLCD as well at this point...
1759 }
1760 if (mf) {
1761 mfBuffer.writeFlattenable(mf);
1762 descSize += mfBuffer.size();
1763 entryCount += 1;
1764 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
1765 }
1766 if (ra) {
1767 raBuffer.writeFlattenable(ra);
1768 descSize += raBuffer.size();
1769 entryCount += 1;
1770 rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
1771 }
reed@google.com10d2d4d2012-03-01 22:32:51 +00001772
1773 ///////////////////////////////////////////////////////////////////////////
1774 // Now that we're done tweaking the rec, call the PostMakeRec cleanup
bungeman@google.com97efada2012-07-30 20:40:50 +00001775 SkScalerContext::PostMakeRec(*this, &rec);
reed@google.com10d2d4d2012-03-01 22:32:51 +00001776
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 descSize += SkDescriptor::ComputeOverhead(entryCount);
1778
1779 SkAutoDescriptor ad(descSize);
1780 SkDescriptor* desc = ad.getDesc();
1781
1782 desc->init();
1783 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1784
1785 if (pe) {
1786 add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
1787 }
1788 if (mf) {
1789 add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
1790 }
1791 if (ra) {
1792 add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
1793 }
1794
1795 SkASSERT(descSize == desc->getLength());
1796 desc->computeChecksum();
1797
reed@google.com17fb3872011-05-04 14:31:07 +00001798#ifdef TEST_DESC
1799 {
1800 // Check that we completely write the bytes in desc (our key), and that
1801 // there are no uninitialized bytes. If there were, then we would get
1802 // false-misses (or worse, false-hits) in our fontcache.
1803 //
1804 // We do this buy filling 2 others, one with 0s and the other with 1s
1805 // and create those, and then check that all 3 are identical.
1806 SkAutoDescriptor ad1(descSize);
1807 SkAutoDescriptor ad2(descSize);
1808 SkDescriptor* desc1 = ad1.getDesc();
1809 SkDescriptor* desc2 = ad2.getDesc();
1810
1811 memset(desc1, 0x00, descSize);
1812 memset(desc2, 0xFF, descSize);
1813
1814 desc1->init();
1815 desc2->init();
1816 desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1817 desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1818
1819 if (pe) {
1820 add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
1821 add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
1822 }
1823 if (mf) {
1824 add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
1825 add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
1826 }
1827 if (ra) {
1828 add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
1829 add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
1830 }
1831
1832 SkASSERT(descSize == desc1->getLength());
1833 SkASSERT(descSize == desc2->getLength());
1834 desc1->computeChecksum();
1835 desc2->computeChecksum();
1836 SkASSERT(!memcmp(desc, desc1, descSize));
1837 SkASSERT(!memcmp(desc, desc2, descSize));
1838 }
1839#endif
1840
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841 proc(desc, context);
1842}
1843
reed@google.com6fb7e2e2011-02-08 22:22:52 +00001844SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 SkGlyphCache* cache;
1846 this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
1847 return cache;
1848}
1849
bungeman@google.com97efada2012-07-30 20:40:50 +00001850/**
1851 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
1852 */
1853//static
1854SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
1855 SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
1856 SkMaskGamma* maskGamma = cachedMaskGamma(rec.getContrast(),
1857 rec.getPaintGamma(),
1858 rec.getDeviceGamma());
1859 return maskGamma->preBlend(rec.getLuminanceColor());
1860}
1861
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862///////////////////////////////////////////////////////////////////////////////
1863
1864#include "SkStream.h"
1865
reed@android.comaefd2bc2009-03-30 21:02:14 +00001866static uintptr_t asint(const void* p) {
1867 return reinterpret_cast<uintptr_t>(p);
1868}
1869
1870union Scalar32 {
1871 SkScalar fScalar;
1872 uint32_t f32;
1873};
1874
1875static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) {
1876 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1877 Scalar32 tmp;
1878 tmp.fScalar = value;
1879 *ptr = tmp.f32;
1880 return ptr + 1;
1881}
1882
1883static SkScalar read_scalar(const uint32_t*& ptr) {
1884 SkASSERT(sizeof(SkScalar) == sizeof(uint32_t));
1885 Scalar32 tmp;
1886 tmp.f32 = *ptr++;
1887 return tmp.fScalar;
1888}
1889
1890static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) {
1891 SkASSERT(a == (uint8_t)a);
1892 SkASSERT(b == (uint8_t)b);
1893 SkASSERT(c == (uint8_t)c);
1894 SkASSERT(d == (uint8_t)d);
1895 return (a << 24) | (b << 16) | (c << 8) | d;
1896}
1897
1898enum FlatFlags {
1899 kHasTypeface_FlatFlag = 0x01,
reed@google.comb0a34d82012-07-11 19:57:55 +00001900 kHasEffects_FlatFlag = 0x02,
reed@android.comaefd2bc2009-03-30 21:02:14 +00001901};
1902
1903// The size of a flat paint's POD fields
1904static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) +
1905 1 * sizeof(SkColor) +
1906 1 * sizeof(uint16_t) +
1907 6 * sizeof(uint8_t);
1908
1909/* To save space/time, we analyze the paint, and write a truncated version of
1910 it if there are not tricky elements like shaders, etc.
1911 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001913 uint8_t flatFlags = 0;
1914 if (this->getTypeface()) {
1915 flatFlags |= kHasTypeface_FlatFlag;
1916 }
1917 if (asint(this->getPathEffect()) |
1918 asint(this->getShader()) |
1919 asint(this->getXfermode()) |
1920 asint(this->getMaskFilter()) |
1921 asint(this->getColorFilter()) |
1922 asint(this->getRasterizer()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001923 asint(this->getLooper()) |
reed@google.comb0a34d82012-07-11 19:57:55 +00001924 asint(this->getAnnotation()) |
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001925 asint(this->getImageFilter())) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001926 flatFlags |= kHasEffects_FlatFlag;
1927 }
reed@google.com72cf4922011-01-04 19:58:20 +00001928
reed@android.comaefd2bc2009-03-30 21:02:14 +00001929 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1930 uint32_t* ptr = buffer.reserve(kPODPaintSize);
1931
1932 ptr = write_scalar(ptr, this->getTextSize());
1933 ptr = write_scalar(ptr, this->getTextScaleX());
1934 ptr = write_scalar(ptr, this->getTextSkewX());
1935 ptr = write_scalar(ptr, this->getStrokeWidth());
1936 ptr = write_scalar(ptr, this->getStrokeMiter());
1937 *ptr++ = this->getColor();
bungeman@google.com24babf42011-11-07 16:33:40 +00001938 // previously flags:16, textAlign:8, flatFlags:8
1939 // now flags:16, hinting:4, textAlign:4, flatFlags:8
1940 *ptr++ = (this->getFlags() << 16) |
1941 // hinting added later. 0 in this nibble means use the default.
1942 ((this->getHinting()+1) << 12) |
1943 (this->getTextAlign() << 8) |
1944 flatFlags;
reed@android.comaefd2bc2009-03-30 21:02:14 +00001945 *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
1946 this->getStyle(), this->getTextEncoding());
1947
1948 // now we're done with ptr and the (pre)reserved space. If we need to write
1949 // additional fields, use the buffer directly
1950 if (flatFlags & kHasTypeface_FlatFlag) {
1951 buffer.writeTypeface(this->getTypeface());
1952 }
1953 if (flatFlags & kHasEffects_FlatFlag) {
1954 buffer.writeFlattenable(this->getPathEffect());
1955 buffer.writeFlattenable(this->getShader());
1956 buffer.writeFlattenable(this->getXfermode());
1957 buffer.writeFlattenable(this->getMaskFilter());
1958 buffer.writeFlattenable(this->getColorFilter());
1959 buffer.writeFlattenable(this->getRasterizer());
1960 buffer.writeFlattenable(this->getLooper());
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00001961 buffer.writeFlattenable(this->getImageFilter());
reed@google.comb0a34d82012-07-11 19:57:55 +00001962 buffer.writeFlattenable(this->getAnnotation());
reed@android.comaefd2bc2009-03-30 21:02:14 +00001963 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964}
1965
1966void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
reed@android.comaefd2bc2009-03-30 21:02:14 +00001967 SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
1968 const void* podData = buffer.skip(kPODPaintSize);
1969 const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
reed@google.com72cf4922011-01-04 19:58:20 +00001970
reed@google.comb0a34d82012-07-11 19:57:55 +00001971 fPrivFlags = 0;
1972
reed@android.comaefd2bc2009-03-30 21:02:14 +00001973 // the order we read must match the order we wrote in flatten()
1974 this->setTextSize(read_scalar(pod));
1975 this->setTextScaleX(read_scalar(pod));
1976 this->setTextSkewX(read_scalar(pod));
1977 this->setStrokeWidth(read_scalar(pod));
reed@google.com72cf4922011-01-04 19:58:20 +00001978 this->setStrokeMiter(read_scalar(pod));
reed@android.comaefd2bc2009-03-30 21:02:14 +00001979 this->setColor(*pod++);
1980
bungeman@google.com24babf42011-11-07 16:33:40 +00001981 // previously flags:16, textAlign:8, flatFlags:8
1982 // now flags:16, hinting:4, textAlign:4, flatFlags:8
reed@android.comaefd2bc2009-03-30 21:02:14 +00001983 uint32_t tmp = *pod++;
1984 this->setFlags(tmp >> 16);
bungeman@google.com24babf42011-11-07 16:33:40 +00001985
1986 // hinting added later. 0 in this nibble means use the default.
1987 uint32_t hinting = (tmp >> 12) & 0xF;
1988 this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
1989
1990 this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
1991
reed@android.comaefd2bc2009-03-30 21:02:14 +00001992 uint8_t flatFlags = tmp & 0xFF;
1993
1994 tmp = *pod++;
1995 this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
1996 this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
1997 this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
1998 this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
1999
2000 if (flatFlags & kHasTypeface_FlatFlag) {
2001 this->setTypeface(buffer.readTypeface());
2002 } else {
2003 this->setTypeface(NULL);
2004 }
2005
2006 if (flatFlags & kHasEffects_FlatFlag) {
reed@google.com82065d62011-02-07 15:30:46 +00002007 SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable()));
2008 SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable()));
2009 SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable()));
2010 SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable()));
2011 SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable()));
2012 SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable()));
2013 SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable()));
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002014 SkSafeUnref(this->setImageFilter((SkImageFilter*) buffer.readFlattenable()));
reed@google.comb0a34d82012-07-11 19:57:55 +00002015 SkSafeUnref(this->setAnnotation((SkAnnotation*) buffer.readFlattenable()));
reed@android.comaefd2bc2009-03-30 21:02:14 +00002016 } else {
2017 this->setPathEffect(NULL);
2018 this->setShader(NULL);
2019 this->setXfermode(NULL);
2020 this->setMaskFilter(NULL);
2021 this->setColorFilter(NULL);
2022 this->setRasterizer(NULL);
2023 this->setLooper(NULL);
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +00002024 this->setImageFilter(NULL);
reed@android.comaefd2bc2009-03-30 21:02:14 +00002025 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026}
2027
2028///////////////////////////////////////////////////////////////////////////////
2029
reed@google.com82065d62011-02-07 15:30:46 +00002030SkShader* SkPaint::setShader(SkShader* shader) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002031 GEN_ID_INC_EVAL(shader != fShader);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032 SkRefCnt_SafeAssign(fShader, shader);
2033 return shader;
2034}
2035
reed@google.com82065d62011-02-07 15:30:46 +00002036SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002037 GEN_ID_INC_EVAL(filter != fColorFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 SkRefCnt_SafeAssign(fColorFilter, filter);
2039 return filter;
2040}
2041
reed@google.com82065d62011-02-07 15:30:46 +00002042SkXfermode* SkPaint::setXfermode(SkXfermode* mode) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002043 GEN_ID_INC_EVAL(mode != fXfermode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 SkRefCnt_SafeAssign(fXfermode, mode);
2045 return mode;
2046}
2047
reed@android.com0baf1932009-06-24 12:41:42 +00002048SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
reed@android.comd66eef72009-06-24 12:29:16 +00002049 SkSafeUnref(fXfermode);
2050 fXfermode = SkXfermode::Create(mode);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002051 GEN_ID_INC;
reed@android.comd66eef72009-06-24 12:29:16 +00002052 return fXfermode;
2053}
2054
reed@google.com82065d62011-02-07 15:30:46 +00002055SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002056 GEN_ID_INC_EVAL(effect != fPathEffect);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057 SkRefCnt_SafeAssign(fPathEffect, effect);
2058 return effect;
2059}
2060
reed@google.com82065d62011-02-07 15:30:46 +00002061SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) {
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00002062 GEN_ID_INC_EVAL(filter != fMaskFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 SkRefCnt_SafeAssign(fMaskFilter, filter);
2064 return filter;
2065}
2066
reed@google.com82065d62011-02-07 15:30:46 +00002067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002068
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002069bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
reed@google.comfd4be262012-05-25 01:04:12 +00002070 SkStrokeRec rec(*this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071
reed@google.comfd4be262012-05-25 01:04:12 +00002072 const SkPath* srcPtr = &src;
2073 SkPath tmpPath;
reed@google.com72cf4922011-01-04 19:58:20 +00002074
reed@google.comfd4be262012-05-25 01:04:12 +00002075 if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec)) {
2076 srcPtr = &tmpPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 }
2078
reed@google.comfd4be262012-05-25 01:04:12 +00002079 if (!rec.applyToPath(dst, *srcPtr)) {
2080 if (srcPtr == &tmpPath) {
2081 // If path's were copy-on-write, this trick would not be needed.
2082 // As it is, we want to save making a deep-copy from tmpPath -> dst
2083 // since we know we're just going to delete tmpPath when we return,
2084 // so the swap saves that copy.
2085 dst->swap(tmpPath);
2086 } else {
2087 *dst = *srcPtr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 }
2089 }
reed@google.comfd4be262012-05-25 01:04:12 +00002090 return !rec.isHairlineStyle();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091}
2092
reed@google.come4f10a72012-05-15 20:47:50 +00002093const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
reed@google.coma584aed2012-05-16 14:06:02 +00002094 SkRect* storage,
2095 Style style) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096 SkASSERT(storage);
reed@android.comd252db02009-04-01 18:31:44 +00002097
reed@google.come4f10a72012-05-15 20:47:50 +00002098 const SkRect* src = &origSrc;
2099
reed@google.com9efd9a02012-01-30 15:41:43 +00002100 if (this->getLooper()) {
2101 SkASSERT(this->getLooper()->canComputeFastBounds(*this));
reed@google.come4f10a72012-05-15 20:47:50 +00002102 this->getLooper()->computeFastBounds(*this, *src, storage);
reed@google.com9efd9a02012-01-30 15:41:43 +00002103 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 }
reed@google.com9efd9a02012-01-30 15:41:43 +00002105
reed@google.come4f10a72012-05-15 20:47:50 +00002106 SkRect tmpSrc;
2107 if (this->getPathEffect()) {
2108 this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
2109 src = &tmpSrc;
2110 }
2111
reed@google.coma584aed2012-05-16 14:06:02 +00002112 if (kFill_Style != style) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002113 // since we're stroked, outset the rect by the radius (and join type)
2114 SkScalar radius = SkScalarHalf(this->getStrokeWidth());
2115 if (0 == radius) { // hairline
2116 radius = SK_Scalar1;
2117 } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
2118 SkScalar scale = this->getStrokeMiter();
2119 if (scale > SK_Scalar1) {
2120 radius = SkScalarMul(radius, scale);
2121 }
2122 }
reed@google.come4f10a72012-05-15 20:47:50 +00002123 storage->set(src->fLeft - radius, src->fTop - radius,
2124 src->fRight + radius, src->fBottom + radius);
reed@google.com9efd9a02012-01-30 15:41:43 +00002125 } else {
reed@google.come4f10a72012-05-15 20:47:50 +00002126 *storage = *src;
reed@google.com9efd9a02012-01-30 15:41:43 +00002127 }
2128
reed@google.com9efd9a02012-01-30 15:41:43 +00002129 if (this->getMaskFilter()) {
2130 this->getMaskFilter()->computeFastBounds(*storage, storage);
2131 }
2132
reed@android.comd252db02009-04-01 18:31:44 +00002133 return *storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134}
2135
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002136///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002138static bool has_thick_frame(const SkPaint& paint) {
2139 return paint.getStrokeWidth() > 0 &&
2140 paint.getStyle() != SkPaint::kFill_Style;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141}
2142
2143SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
2144 const SkPaint& paint,
djsollen@google.com166e6532012-03-20 14:24:38 +00002145 bool applyStrokeAndPathEffects)
2146 : fPaint(paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
2148 true);
2149
djsollen@google.com166e6532012-03-20 14:24:38 +00002150 fPaint.setLinearText(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2152
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002153 if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154 applyStrokeAndPathEffects = false;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002155 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156
djsollen@google.com166e6532012-03-20 14:24:38 +00002157 // can't use our canonical size if we need to apply patheffects
2158 if (fPaint.getPathEffect() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
2160 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
djsollen@google.com166e6532012-03-20 14:24:38 +00002161 if (has_thick_frame(fPaint)) {
2162 fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
2163 }
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002164 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165 fScale = SK_Scalar1;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002166 }
reed@google.com72cf4922011-01-04 19:58:20 +00002167
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002168 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169 fPaint.setStyle(SkPaint::kFill_Style);
2170 fPaint.setPathEffect(NULL);
2171 }
2172
2173 fCache = fPaint.detachCache(NULL);
2174
2175 SkPaint::Style style = SkPaint::kFill_Style;
2176 SkPathEffect* pe = NULL;
2177
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002178 if (!applyStrokeAndPathEffects) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 style = paint.getStyle(); // restore
2180 pe = paint.getPathEffect(); // restore
2181 }
2182 fPaint.setStyle(style);
2183 fPaint.setPathEffect(pe);
2184 fPaint.setMaskFilter(paint.getMaskFilter()); // restore
2185
2186 // now compute fXOffset if needed
2187
2188 SkScalar xOffset = 0;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002189 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 int count;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002191 SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length,
2192 &count, NULL), fScale);
2193 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 width = SkScalarHalf(width);
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002195 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 xOffset = -width;
2197 }
2198 fXPos = xOffset;
2199 fPrevAdvance = 0;
2200
2201 fText = text;
2202 fStop = text + length;
reed@google.com44da42e2011-11-10 20:04:47 +00002203
2204 fXYIndex = paint.isVerticalText() ? 1 : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205}
2206
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002207SkTextToPathIter::~SkTextToPathIter() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 SkGlyphCache::AttachCache(fCache);
2209}
2210
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002211const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
2212 while (fText < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
2214
2215 fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
reed@google.com44da42e2011-11-10 20:04:47 +00002216 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002218 if (glyph.fWidth) {
2219 if (xpos) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 *xpos = fXPos;
reed@google.com6fb7e2e2011-02-08 22:22:52 +00002221 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 return fCache->findPath(glyph);
2223 }
2224 }
2225 return NULL;
2226}
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002227
2228///////////////////////////////////////////////////////////////////////////////
2229
2230bool SkPaint::nothingToDraw() const {
reed@google.com733e3022011-10-06 15:11:03 +00002231 if (fLooper) {
2232 return false;
2233 }
reed@google.comdcd0f3a2011-10-04 01:17:15 +00002234 SkXfermode::Mode mode;
2235 if (SkXfermode::AsMode(fXfermode, &mode)) {
2236 switch (mode) {
2237 case SkXfermode::kSrcOver_Mode:
2238 case SkXfermode::kSrcATop_Mode:
2239 case SkXfermode::kDstOut_Mode:
2240 case SkXfermode::kDstOver_Mode:
2241 case SkXfermode::kPlus_Mode:
2242 return 0 == this->getAlpha();
2243 case SkXfermode::kDst_Mode:
2244 return true;
2245 default:
2246 break;
2247 }
2248 }
2249 return false;
2250}
2251
2252
reed@google.com15356a62011-11-03 19:29:08 +00002253//////////// Move these to their own file soon.
2254
robertphillips@google.com0456e0b2012-06-27 14:03:26 +00002255SK_DEFINE_INST_COUNT(SkImageFilter)
2256
reed@google.com76dd2772012-01-05 21:15:07 +00002257bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
2258 const SkMatrix& ctm,
reed@google.com32d25b62011-12-20 16:19:00 +00002259 SkBitmap* result, SkIPoint* loc) {
reed@google.com76dd2772012-01-05 21:15:07 +00002260 SkASSERT(proxy);
reed@google.com15356a62011-11-03 19:29:08 +00002261 SkASSERT(result);
reed@google.com32d25b62011-12-20 16:19:00 +00002262 SkASSERT(loc);
reed@google.com76dd2772012-01-05 21:15:07 +00002263 /*
2264 * Give the proxy first shot at the filter. If it returns false, ask
2265 * the filter to do it.
2266 */
2267 return proxy->filterImage(this, src, ctm, result, loc) ||
2268 this->onFilterImage(proxy, src, ctm, result, loc);
reed@google.com32d25b62011-12-20 16:19:00 +00002269}
2270
2271bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
2272 SkIRect* dst) {
2273 SkASSERT(&src);
2274 SkASSERT(dst);
2275 return this->onFilterBounds(src, ctm, dst);
reed@google.com15356a62011-11-03 19:29:08 +00002276}
2277
reed@google.com76dd2772012-01-05 21:15:07 +00002278bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
reed@google.com32d25b62011-12-20 16:19:00 +00002279 SkBitmap*, SkIPoint*) {
reed@google.com15356a62011-11-03 19:29:08 +00002280 return false;
2281}
2282
reed@google.com32d25b62011-12-20 16:19:00 +00002283bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
2284 SkIRect* dst) {
2285 *dst = src;
2286 return true;
2287}
2288
tomhudson@google.comd0c1a062012-07-12 17:23:52 +00002289bool SkImageFilter::asNewCustomStage(GrCustomStage**, GrTexture*) const {
senorblanco@chromium.org894790d2012-07-11 16:01:22 +00002290 return false;
2291}
2292
reed@google.com15356a62011-11-03 19:29:08 +00002293bool SkImageFilter::asABlur(SkSize* sigma) const {
2294 return false;
2295}
2296
senorblanco@chromium.org05054f12012-03-02 21:05:45 +00002297bool SkImageFilter::asAnErode(SkISize* radius) const {
2298 return false;
2299}
2300
2301bool SkImageFilter::asADilate(SkISize* radius) const {
2302 return false;
2303}
2304
reed@google.comb0a34d82012-07-11 19:57:55 +00002305////////////////////
reed@google.com9efd9a02012-01-30 15:41:43 +00002306
robertphillips@google.com0456e0b2012-06-27 14:03:26 +00002307SK_DEFINE_INST_COUNT(SkDrawLooper)
2308
reed@google.com9efd9a02012-01-30 15:41:43 +00002309bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
2310 SkCanvas canvas;
2311
2312 this->init(&canvas);
2313 for (;;) {
2314 SkPaint p(paint);
2315 if (this->next(&canvas, &p)) {
2316 p.setLooper(NULL);
2317 if (!p.canComputeFastBounds()) {
2318 return false;
2319 }
2320 } else {
2321 break;
2322 }
2323 }
2324 return true;
2325}
2326
2327void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
2328 SkRect* dst) {
2329 SkCanvas canvas;
2330
2331 this->init(&canvas);
2332 for (bool firstTime = true;; firstTime = false) {
2333 SkPaint p(paint);
2334 if (this->next(&canvas, &p)) {
2335 SkRect r(src);
2336
2337 p.setLooper(NULL);
2338 p.computeFastBounds(r, &r);
2339 canvas.getTotalMatrix().mapRect(&r);
2340
2341 if (firstTime) {
2342 *dst = r;
2343 } else {
2344 dst->join(r);
2345 }
2346 } else {
2347 break;
2348 }
2349 }
2350}
2351