aboutsummaryrefslogtreecommitdiffstats
path: root/src/core/SkPaint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/SkPaint.cpp')
-rw-r--r--src/core/SkPaint.cpp1551
1 files changed, 1551 insertions, 0 deletions
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
new file mode 100644
index 0000000..691da41
--- /dev/null
+++ b/src/core/SkPaint.cpp
@@ -0,0 +1,1551 @@
+/* libs/graphics/sgl/SkPaint.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPaint.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkFontHost.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkScalerContext.h"
+#include "SkStroke.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+#include "SkAutoKern.h"
+
+#define SK_DefaultTextSize SkIntToScalar(12)
+
+#define SK_DefaultFlags 0 //(kNativeHintsText_Flag)
+
+SkPaint::SkPaint()
+{
+ fTypeface = NULL;
+ fTextSize = SK_DefaultTextSize;
+ fTextScaleX = SK_Scalar1;
+ fTextSkewX = 0;
+
+ fPathEffect = NULL;
+ fShader = NULL;
+ fXfermode = NULL;
+ fMaskFilter = NULL;
+ fColorFilter = NULL;
+ fRasterizer = NULL;
+ fLooper = NULL;
+
+ fColor = SK_ColorBLACK;
+ fWidth = 0;
+ fMiterLimit = SK_DefaultMiterLimit;
+ fFlags = SK_DefaultFlags;
+ fCapType = kDefault_Cap;
+ fJoinType = kDefault_Join;
+ fTextAlign = kLeft_Align;
+ fStyle = kFill_Style;
+ fTextEncoding = kUTF8_TextEncoding;
+}
+
+SkPaint::SkPaint(const SkPaint& src)
+{
+ memcpy(this, &src, sizeof(src));
+
+ fTypeface->safeRef();
+ fPathEffect->safeRef();
+ fShader->safeRef();
+ fXfermode->safeRef();
+ fMaskFilter->safeRef();
+ fColorFilter->safeRef();
+ fRasterizer->safeRef();
+ fLooper->safeRef();
+}
+
+SkPaint::~SkPaint()
+{
+ fTypeface->safeUnref();
+ fPathEffect->safeUnref();
+ fShader->safeUnref();
+ fXfermode->safeUnref();
+ fMaskFilter->safeUnref();
+ fColorFilter->safeUnref();
+ fRasterizer->safeUnref();
+ fLooper->safeUnref();
+}
+
+SkPaint& SkPaint::operator=(const SkPaint& src)
+{
+ SkASSERT(&src);
+
+ src.fTypeface->safeRef();
+ src.fPathEffect->safeRef();
+ src.fShader->safeRef();
+ src.fXfermode->safeRef();
+ src.fMaskFilter->safeRef();
+ src.fColorFilter->safeRef();
+ src.fRasterizer->safeRef();
+ src.fLooper->safeRef();
+
+ fTypeface->safeUnref();
+ fPathEffect->safeUnref();
+ fShader->safeUnref();
+ fXfermode->safeUnref();
+ fMaskFilter->safeUnref();
+ fColorFilter->safeUnref();
+ fRasterizer->safeUnref();
+ fLooper->safeUnref();
+
+ memcpy(this, &src, sizeof(src));
+
+ return *this;
+}
+
+int operator==(const SkPaint& a, const SkPaint& b)
+{
+ return memcmp(&a, &b, sizeof(a)) == 0;
+}
+
+void SkPaint::reset()
+{
+ SkPaint init;
+
+ *this = init;
+}
+
+void SkPaint::setFlags(uint32_t flags)
+{
+ fFlags = flags;
+}
+
+void SkPaint::setAntiAlias(bool doAA)
+{
+ this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+}
+
+void SkPaint::setDither(bool doDither)
+{
+ this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+}
+
+void SkPaint::setSubpixelText(bool doSubpixel)
+{
+ this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+}
+
+void SkPaint::setLinearText(bool doLinearText)
+{
+ this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+}
+
+void SkPaint::setUnderlineText(bool doUnderline)
+{
+ this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+}
+
+void SkPaint::setStrikeThruText(bool doStrikeThru)
+{
+ this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+}
+
+void SkPaint::setFakeBoldText(bool doFakeBold)
+{
+ this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+}
+
+void SkPaint::setDevKernText(bool doDevKern)
+{
+ this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+}
+
+void SkPaint::setFilterBitmap(bool doFilter)
+{
+ this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+}
+
+void SkPaint::setStyle(Style style)
+{
+ if ((unsigned)style < kStyleCount)
+ fStyle = style;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
+#endif
+}
+
+void SkPaint::setColor(SkColor color)
+{
+ fColor = color;
+}
+
+void SkPaint::setAlpha(U8CPU a)
+{
+ fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor));
+}
+
+void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+ fColor = SkColorSetARGB(a, r, g, b);
+}
+
+void SkPaint::setStrokeWidth(SkScalar width)
+{
+ if (width >= 0)
+ fWidth = width;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeMiter(SkScalar limit)
+{
+ if (limit >= 0)
+ fMiterLimit = limit;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeCap(Cap ct)
+{
+ if ((unsigned)ct < kCapCount)
+ fCapType = SkToU8(ct);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
+#endif
+}
+
+void SkPaint::setStrokeJoin(Join jt)
+{
+ if ((unsigned)jt < kJoinCount)
+ fJoinType = SkToU8(jt);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////
+
+void SkPaint::setTextAlign(Align align)
+{
+ if ((unsigned)align < kAlignCount)
+ fTextAlign = SkToU8(align);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
+#endif
+}
+
+void SkPaint::setTextSize(SkScalar ts)
+{
+ if (ts > 0)
+ fTextSize = ts;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextSize() called with non-positive value\n");
+#endif
+}
+
+void SkPaint::setTextScaleX(SkScalar scaleX)
+{
+ fTextScaleX = scaleX;
+}
+
+void SkPaint::setTextSkewX(SkScalar skewX)
+{
+ fTextSkewX = skewX;
+}
+
+void SkPaint::setTextEncoding(TextEncoding encoding)
+{
+ if ((unsigned)encoding <= kGlyphID_TextEncoding)
+ fTextEncoding = encoding;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkPaint::setTypeface(SkTypeface* font)
+{
+ SkRefCnt_SafeAssign(fTypeface, font);
+ return font;
+}
+
+SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r)
+{
+ SkRefCnt_SafeAssign(fRasterizer, r);
+ return r;
+}
+
+SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper)
+{
+ SkRefCnt_SafeAssign(fLooper, looper);
+ return looper;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
+ uint16_t glyphs[]) const {
+ if (byteLength == 0) {
+ return 0;
+ }
+
+ SkASSERT(textData != NULL);
+
+ if (NULL == glyphs) {
+ switch (this->getTextEncoding()) {
+ case kUTF8_TextEncoding:
+ return SkUTF8_CountUnichars((const char*)textData, byteLength);
+ case kUTF16_TextEncoding:
+ return SkUTF16_CountUnichars((const uint16_t*)textData,
+ byteLength >> 1);
+ case kGlyphID_TextEncoding:
+ return byteLength >> 1;
+ default:
+ SkASSERT(!"unknown text encoding");
+ }
+ return 0;
+ }
+
+ // if we get here, we have a valid glyphs[] array, so time to fill it in
+
+ // handle this encoding before the setup for the glyphcache
+ if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+ // we want to ignore the low bit of byteLength
+ memcpy(glyphs, textData, byteLength >> 1 << 1);
+ return byteLength >> 1;
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ const char* text = (const char*)textData;
+ const char* stop = text + byteLength;
+ uint16_t* gptr = glyphs;
+
+ switch (this->getTextEncoding()) {
+ case SkPaint::kUTF8_TextEncoding:
+ while (text < stop) {
+ *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
+ }
+ break;
+ case SkPaint::kUTF16_TextEncoding: {
+ const uint16_t* text16 = (const uint16_t*)text;
+ const uint16_t* stop16 = (const uint16_t*)stop;
+ while (text16 < stop16) {
+ *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
+ }
+ break;
+ }
+ default:
+ SkASSERT(!"unknown text encoding");
+ }
+ return gptr - glyphs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ ptr -= 1;
+ unsigned glyphID = *ptr;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+///
+
+static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDAdvance(glyphID);
+}
+
+static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ ptr -= 1;
+ unsigned glyphID = *ptr;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDAdvance(glyphID);
+}
+
+SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
+ bool needFullMetrics) const
+{
+ static const SkMeasureCacheProc gMeasureCacheProcs[] = {
+ sk_getMetrics_utf8_next,
+ sk_getMetrics_utf16_next,
+ sk_getMetrics_glyph_next,
+
+ sk_getMetrics_utf8_prev,
+ sk_getMetrics_utf16_prev,
+ sk_getMetrics_glyph_prev,
+
+ sk_getAdvance_utf8_next,
+ sk_getAdvance_utf16_next,
+ sk_getAdvance_glyph_next,
+
+ sk_getAdvance_utf8_prev,
+ sk_getAdvance_utf16_prev,
+ sk_getAdvance_glyph_prev
+ };
+
+ unsigned index = this->getTextEncoding();
+
+ if (kBackward_TextBufferDirection == tbd)
+ index += 3;
+ if (!needFullMetrics && !this->isDevKernText())
+ index += 6;
+
+ SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
+ return gMeasureCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
+ const char** text, SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
+}
+
+static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, const char** text,
+ SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
+ x, y);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** text,
+ SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID, x, y);
+}
+
+SkDrawCacheProc SkPaint::getDrawCacheProc() const
+{
+ static const SkDrawCacheProc gDrawCacheProcs[] = {
+ sk_getMetrics_utf8_00,
+ sk_getMetrics_utf16_00,
+ sk_getMetrics_glyph_00,
+
+ sk_getMetrics_utf8_xy,
+ sk_getMetrics_utf16_xy,
+ sk_getMetrics_glyph_xy
+ };
+
+ unsigned index = this->getTextEncoding();
+ if (fFlags & kSubpixelText_Flag)
+ index += 3;
+
+ SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
+ return gDrawCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoRestorePaintTextSizeAndFrame {
+public:
+ SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) : fPaint((SkPaint*)paint)
+ {
+ fTextSize = paint->getTextSize();
+ fStyle = paint->getStyle();
+ fPaint->setStyle(SkPaint::kFill_Style);
+ }
+ ~SkAutoRestorePaintTextSizeAndFrame()
+ {
+ fPaint->setStyle(fStyle);
+ fPaint->setTextSize(fTextSize);
+ }
+
+private:
+ SkPaint* fPaint;
+ SkScalar fTextSize;
+ SkPaint::Style fStyle;
+};
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds)
+{
+ bounds->set(SkIntToScalar(g.fLeft),
+ SkIntToScalar(g.fTop),
+ SkIntToScalar(g.fLeft + g.fWidth),
+ SkIntToScalar(g.fTop + g.fHeight));
+}
+
+static void join_bounds(const SkGlyph& g, SkRect* bounds, SkFixed dx)
+{
+ SkScalar sx = SkFixedToScalar(dx);
+ bounds->join(SkIntToScalar(g.fLeft) + sx,
+ SkIntToScalar(g.fTop),
+ SkIntToScalar(g.fLeft + g.fWidth) + sx,
+ SkIntToScalar(g.fTop + g.fHeight));
+}
+
+SkScalar SkPaint::measure_text(SkGlyphCache* cache,
+ const char* text, size_t byteLength,
+ int* count, SkRect* bounds) const
+{
+ SkASSERT(count);
+ if (byteLength == 0)
+ {
+ *count = 0;
+ if (bounds)
+ bounds->setEmpty();
+ return 0;
+ }
+
+ SkMeasureCacheProc glyphCacheProc;
+ glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+ NULL != bounds);
+
+ int n = 1;
+ const char* stop = (const char*)text + byteLength;
+ const SkGlyph* g = &glyphCacheProc(cache, &text);
+ SkFixed x = g->fAdvanceX;
+
+ SkAutoKern autokern;
+
+ if (NULL == bounds)
+ {
+ if (this->isDevKernText())
+ {
+ int rsb;
+ for (; text < stop; n++) {
+ rsb = g->fRsbDelta;
+ g = &glyphCacheProc(cache, &text);
+ x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
+ }
+ }
+ else
+ {
+ for (; text < stop; n++) {
+ x += glyphCacheProc(cache, &text).fAdvanceX;
+ }
+ }
+ }
+ else
+ {
+ set_bounds(*g, bounds);
+ if (this->isDevKernText())
+ {
+ int rsb;
+ for (; text < stop; n++) {
+ rsb = g->fRsbDelta;
+ g = &glyphCacheProc(cache, &text);
+ x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
+ join_bounds(*g, bounds, x);
+ x += g->fAdvanceX;
+ }
+ }
+ else
+ {
+ for (; text < stop; n++) {
+ g = &glyphCacheProc(cache, &text);
+ join_bounds(*g, bounds, x);
+ x += g->fAdvanceX;
+ }
+ }
+ }
+ SkASSERT(text == stop);
+
+ *count = n;
+ return SkFixedToScalar(x);
+}
+
+SkScalar SkPaint::measureText(const void* textData, size_t length,
+ SkRect* bounds, SkScalar zoom) const
+{
+ const char* text = (const char*)textData;
+ SkASSERT(text != NULL || length == 0);
+
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkMatrix zoomMatrix, *zoomPtr = NULL;
+ if (zoom)
+ {
+ zoomMatrix.setScale(zoom, zoom);
+ zoomPtr = &zoomMatrix;
+ }
+
+ SkAutoGlyphCache autoCache(*this, zoomPtr);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ SkScalar width = 0;
+
+ if (length > 0)
+ {
+ int tempCount;
+
+ width = this->measure_text(cache, text, length, &tempCount, bounds);
+ if (scale)
+ {
+ width = SkScalarMul(width, scale);
+ if (bounds)
+ {
+ bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
+ bounds->fTop = SkScalarMul(bounds->fTop, scale);
+ bounds->fRight = SkScalarMul(bounds->fRight, scale);
+ bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
+ }
+ }
+ }
+ return width;
+}
+
+typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
+
+static bool forward_textBufferPred(const char* text, const char* stop)
+{
+ return text < stop;
+}
+
+static bool backward_textBufferPred(const char* text, const char* stop)
+{
+ return text > stop;
+}
+
+static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
+ const char** text, size_t length, const char** stop)
+{
+ if (SkPaint::kForward_TextBufferDirection == tbd)
+ {
+ *stop = *text + length;
+ return forward_textBufferPred;
+ }
+ else
+ {
+ // text should point to the end of the buffer, and stop to the beginning
+ *stop = *text;
+ *text += length;
+ return backward_textBufferPred;
+ }
+}
+
+size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
+ SkScalar* measuredWidth,
+ TextBufferDirection tbd) const
+{
+ if (0 == length || 0 >= maxWidth)
+ {
+ if (measuredWidth)
+ *measuredWidth = 0;
+ return 0;
+ }
+
+ SkASSERT(textD != NULL);
+ const char* text = (const char*)textD;
+
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
+ const char* stop;
+ SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
+ SkFixed max = SkScalarToFixed(maxWidth);
+ SkFixed width = 0;
+
+ SkAutoKern autokern;
+
+ if (this->isDevKernText())
+ {
+ int rsb = 0;
+ while (pred(text, stop))
+ {
+ const char* curr = text;
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
+ if ((width += x) > max)
+ {
+ width -= x;
+ text = curr;
+ break;
+ }
+ rsb = g.fRsbDelta;
+ }
+ }
+ else
+ {
+ while (pred(text, stop))
+ {
+ const char* curr = text;
+ SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
+ if ((width += x) > max)
+ {
+ width -= x;
+ text = curr;
+ break;
+ }
+ }
+ }
+
+ if (measuredWidth)
+ {
+
+ SkScalar scalarWidth = SkFixedToScalar(width);
+ if (scale)
+ scalarWidth = SkScalarMul(scalarWidth, scale);
+ *measuredWidth = scalarWidth;
+ }
+
+ // return the number of bytes measured
+ return (kForward_TextBufferDirection == tbd) ?
+ text - stop + length : stop - text + length;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context)
+{
+ *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
+ return false; // don't detach the cache
+}
+
+static void FontMetricsDescProc(const SkDescriptor* desc, void* context)
+{
+ SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
+}
+
+SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const
+{
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkMatrix zoomMatrix, *zoomPtr = NULL;
+ if (zoom)
+ {
+ zoomMatrix.setScale(zoom, zoom);
+ zoomPtr = &zoomMatrix;
+ }
+
+#if 0
+ SkAutoGlyphCache autoCache(*this, zoomPtr);
+ SkGlyphCache* cache = autoCache.getCache();
+ const FontMetrics& my = cache->getFontMetricsY();
+#endif
+ FontMetrics storage;
+ if (NULL == metrics)
+ metrics = &storage;
+
+ this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
+
+ if (scale)
+ {
+ metrics->fTop = SkScalarMul(metrics->fTop, scale);
+ metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
+ metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
+ metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
+ metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
+ }
+ return metrics->fDescent - metrics->fAscent + metrics->fLeading;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale)
+{
+ bounds->set(g.fLeft * scale,
+ g.fTop * scale,
+ (g.fLeft + g.fWidth) * scale,
+ (g.fTop + g.fHeight) * scale);
+}
+
+int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar widths[],
+ SkRect bounds[]) const
+{
+ if (0 == byteLength)
+ return 0;
+
+ SkASSERT(NULL != textData);
+
+ if (NULL == widths && NULL == bounds)
+ return this->countText(textData, byteLength);
+
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+ SkScalar scale = 0;
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+ SkMeasureCacheProc glyphCacheProc;
+ glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+ NULL != bounds);
+
+ const char* text = (const char*)textData;
+ const char* stop = text + byteLength;
+ int count = 0;
+
+ if (this->isDevKernText())
+ {
+ // we adjust the widths returned here through auto-kerning
+ SkAutoKern autokern;
+ SkFixed prevWidth = 0;
+
+ if (scale) {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ SkFixed adjust = autokern.adjust(g);
+
+ if (count > 0) {
+ SkScalar w = SkFixedToScalar(prevWidth + adjust);
+ *widths++ = SkScalarMul(w, scale);
+ }
+ prevWidth = g.fAdvanceX;
+ }
+ if (bounds) {
+ set_bounds(g, bounds++, scale);
+ }
+ ++count;
+ }
+ if (count > 0 && widths) {
+ *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ SkFixed adjust = autokern.adjust(g);
+
+ if (count > 0) {
+ *widths++ = SkFixedToScalar(prevWidth + adjust);
+ }
+ prevWidth = g.fAdvanceX;
+ }
+ if (bounds) {
+ set_bounds(g, bounds++);
+ }
+ ++count;
+ }
+ if (count > 0 && widths) {
+ *widths = SkFixedToScalar(prevWidth);
+ }
+ }
+ } else { // no devkern
+ if (scale) {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
+ scale);
+ }
+ if (bounds) {
+ set_bounds(g, bounds++, scale);
+ }
+ ++count;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ *widths++ = SkFixedToScalar(g.fAdvanceX);
+ }
+ if (bounds) {
+ set_bounds(g, bounds++);
+ }
+ ++count;
+ }
+ }
+ }
+
+ SkASSERT(text == stop);
+ return count;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkScalar y, SkPath* path) const
+{
+ const char* text = (const char*)textData;
+ SkASSERT(length == 0 || text != NULL);
+ if (text == NULL || length == 0 || path == NULL)
+ return;
+
+ SkTextToPathIter iter(text, length, *this, false, true);
+ SkMatrix matrix;
+ SkScalar prevXPos = 0;
+
+ matrix.setScale(iter.getPathScale(), iter.getPathScale());
+ matrix.postTranslate(x, y);
+ path->reset();
+
+ SkScalar xpos;
+ const SkPath* iterPath;
+ while ((iterPath = iter.next(&xpos)) != NULL)
+ {
+ matrix.postTranslate(xpos - prevXPos, 0);
+ path->addPath(*iterPath, matrix);
+ prevXPos = xpos;
+ }
+}
+
+static void add_flattenable(SkDescriptor* desc, uint32_t tag,
+ SkFlattenableWriteBuffer* buffer) {
+ buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
+}
+
+/*
+ * interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i])
+ inspired by a desire to change the multiplier for thickness in fakebold
+ therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient
+ repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if
+ key is the value of a repeated scalar in keys, the first one will be used
+ - this may change if a binary search is used
+ - also, this ensures that there is no divide by zero (an assert also checks for that)
+*/
+static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length)
+{
+
+ SkASSERT(length > 0);
+ SkASSERT(keys != NULL);
+ SkASSERT(values != NULL);
+#ifdef SK_DEBUG
+ for (int i = 1; i < length; i++)
+ SkASSERT(keys[i] >= keys[i-1]);
+#endif
+ int right = 0;
+ while (right < length && key > keys[right])
+ right++;
+ //could use sentinal values to eliminate conditionals
+ //i assume i am not in control of input values, so i want to make it simple
+ if (length == right)
+ return values[length-1];
+ if (0 == right)
+ return values[0];
+ //otherwise, we interpolate between right-1 and right
+ SkScalar rVal = values[right];
+ SkScalar lVal = values[right-1];
+ SkScalar rightKey = keys[right];
+ SkScalar leftKey = keys[right-1];
+ SkASSERT(rightKey != leftKey);
+ //fractional amount which we will multiply by the difference in the left value and right value
+ SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey);
+ return lVal + SkScalarMul(fract, rVal-lVal);
+}
+
+//used for interpolating in fakeBold
+static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) };
+static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 };
+
+static SkMask::Format computeMaskFormat(const SkPaint& paint)
+{
+ uint32_t flags = paint.getFlags();
+
+ return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format;
+}
+
+static SkScalerContext::Hints computeScalerHints(const SkPaint& paint)
+{
+ uint32_t flags = paint.getFlags();
+
+ if (flags & SkPaint::kLinearText_Flag)
+ return SkScalerContext::kNo_Hints;
+ else if (flags & SkPaint::kSubpixelText_Flag)
+ return SkScalerContext::kSubpixel_Hints;
+ else
+ return SkScalerContext::kNormal_Hints;
+}
+
+void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec)
+{
+ SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0);
+
+ rec->fFontID = SkTypeface::UniqueID(paint.getTypeface());
+ rec->fTextSize = paint.getTextSize();
+ rec->fPreScaleX = paint.getTextScaleX();
+ rec->fPreSkewX = paint.getTextSkewX();
+
+ if (deviceMatrix)
+ {
+ rec->fPost2x2[0][0] = deviceMatrix->getScaleX();
+ rec->fPost2x2[0][1] = deviceMatrix->getSkewX();
+ rec->fPost2x2[1][0] = deviceMatrix->getSkewY();
+ rec->fPost2x2[1][1] = deviceMatrix->getScaleY();
+ }
+ else
+ {
+ rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
+ rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
+ }
+
+ SkPaint::Style style = paint.getStyle();
+ SkScalar strokeWidth = paint.getStrokeWidth();
+
+ if (paint.isFakeBoldText())
+ {
+ SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2);
+ SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
+
+ if (style == SkPaint::kFill_Style)
+ {
+ style = SkPaint::kStrokeAndFill_Style;
+ strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
+ }
+ else
+ strokeWidth += extra;
+ }
+
+ unsigned flags = SkFontHost::ComputeGammaFlag(paint);
+
+ if (paint.isDevKernText())
+ flags |= SkScalerContext::kDevKernText_Flag;
+
+ if (style != SkPaint::kFill_Style && strokeWidth > 0)
+ {
+ rec->fFrameWidth = strokeWidth;
+ rec->fMiterLimit = paint.getStrokeMiter();
+ rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
+
+ if (style == SkPaint::kStrokeAndFill_Style)
+ flags |= SkScalerContext::kFrameAndFill_Flag;
+ }
+ else
+ {
+ rec->fFrameWidth = 0;
+ rec->fMiterLimit = 0;
+ rec->fStrokeJoin = 0;
+ }
+
+ rec->fHints = SkToU8(computeScalerHints(paint));
+ rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
+ rec->fFlags = SkToU8(flags);
+}
+
+#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
+
+void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
+ void (*proc)(const SkDescriptor*, void*),
+ void* context) const
+{
+ SkScalerContext::Rec rec;
+
+ SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+
+ size_t descSize = sizeof(rec);
+ int entryCount = 1;
+ SkPathEffect* pe = this->getPathEffect();
+ SkMaskFilter* mf = this->getMaskFilter();
+ SkRasterizer* ra = this->getRasterizer();
+
+ SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+ SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+ SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+
+ if (pe) {
+ peBuffer.writeFlattenable(pe);
+ descSize += peBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
+ // seems like we could support kLCD as well at this point...
+ }
+ if (mf) {
+ mfBuffer.writeFlattenable(mf);
+ descSize += mfBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
+ }
+ if (ra) {
+ raBuffer.writeFlattenable(ra);
+ descSize += raBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
+ }
+ descSize += SkDescriptor::ComputeOverhead(entryCount);
+
+ SkAutoDescriptor ad(descSize);
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ if (pe) {
+ add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
+ }
+ if (mf) {
+ add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
+ }
+ if (ra) {
+ add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
+ }
+
+ SkASSERT(descSize == desc->getLength());
+ desc->computeChecksum();
+
+ proc(desc, context);
+}
+
+static void DetachDescProc(const SkDescriptor* desc, void* context)
+{
+ *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
+}
+
+SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const
+{
+ SkGlyphCache* cache;
+ this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+ return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
+ buffer.writeTypeface(this->getTypeface());
+ buffer.writeScalar(this->getTextSize());
+ buffer.writeScalar(this->getTextScaleX());
+ buffer.writeScalar(this->getTextSkewX());
+ buffer.writeFlattenable(this->getPathEffect());
+ buffer.writeFlattenable(this->getShader());
+ buffer.writeFlattenable(this->getXfermode());
+ buffer.writeFlattenable(this->getMaskFilter());
+ buffer.writeFlattenable(this->getColorFilter());
+ buffer.writeFlattenable(this->getRasterizer());
+ buffer.writeFlattenable(this->getLooper());
+ buffer.write32(this->getColor());
+ buffer.writeScalar(this->getStrokeWidth());
+ buffer.writeScalar(this->getStrokeMiter());
+ buffer.write16(this->getFlags());
+ buffer.write8(this->getTextAlign());
+ buffer.write8(this->getStrokeCap());
+ buffer.write8(this->getStrokeJoin());
+ buffer.write8(this->getStyle());
+ buffer.write8(this->getTextEncoding());
+}
+
+void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
+ this->setTypeface(buffer.readTypeface());
+ this->setTextSize(buffer.readScalar());
+ this->setTextScaleX(buffer.readScalar());
+ this->setTextSkewX(buffer.readScalar());
+ this->setPathEffect((SkPathEffect*) buffer.readFlattenable())->safeUnref();
+ this->setShader((SkShader*) buffer.readFlattenable())->safeUnref();
+ this->setXfermode((SkXfermode*) buffer.readFlattenable())->safeUnref();
+ this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())->safeUnref();
+ this->setColorFilter((SkColorFilter*) buffer.readFlattenable())->safeUnref();
+ this->setRasterizer((SkRasterizer*) buffer.readFlattenable())->safeUnref();
+ this->setLooper((SkDrawLooper*) buffer.readFlattenable())->safeUnref();
+ this->setColor(buffer.readU32());
+ this->setStrokeWidth(buffer.readScalar());
+ this->setStrokeMiter(buffer.readScalar());
+ this->setFlags(buffer.readU16());
+ this->setTextAlign((SkPaint::Align) buffer.readU8());
+ this->setStrokeCap((SkPaint::Cap) buffer.readU8());
+ this->setStrokeJoin((SkPaint::Join) buffer.readU8());
+ this->setStyle((SkPaint::Style) buffer.readU8());
+ this->setTextEncoding((SkPaint::TextEncoding) buffer.readU8());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkShader* SkPaint::setShader(SkShader* shader)
+{
+ SkRefCnt_SafeAssign(fShader, shader);
+ return shader;
+}
+
+SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter)
+{
+ SkRefCnt_SafeAssign(fColorFilter, filter);
+ return filter;
+}
+
+SkXfermode* SkPaint::setXfermode(SkXfermode* mode)
+{
+ SkRefCnt_SafeAssign(fXfermode, mode);
+ return mode;
+}
+
+SkXfermode* SkPaint::setPorterDuffXfermode(SkPorterDuff::Mode mode)
+{
+ fXfermode->safeUnref();
+ fXfermode = SkPorterDuff::CreateXfermode(mode);
+ return fXfermode;
+}
+
+SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect)
+{
+ SkRefCnt_SafeAssign(fPathEffect, effect);
+ return effect;
+}
+
+SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter)
+{
+ SkRefCnt_SafeAssign(fMaskFilter, filter);
+ return filter;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const
+{
+ SkPath effectPath, strokePath;
+ const SkPath* path = &src;
+
+ SkScalar width = this->getStrokeWidth();
+
+ switch (this->getStyle()) {
+ case SkPaint::kFill_Style:
+ width = -1; // mark it as no-stroke
+ break;
+ case SkPaint::kStrokeAndFill_Style:
+ if (width == 0)
+ width = -1; // mark it as no-stroke
+ break;
+ case SkPaint::kStroke_Style:
+ break;
+ default:
+ SkASSERT(!"unknown paint style");
+ }
+
+ if (this->getPathEffect())
+ {
+ // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill
+ if (this->getStyle() == SkPaint::kStrokeAndFill_Style)
+ width = -1; // mark it as no-stroke
+
+ if (this->getPathEffect()->filterPath(&effectPath, src, &width))
+ path = &effectPath;
+
+ // restore the width if we earlier had to lie, and if we're still set to no-stroke
+ // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
+ // and we want to respect that (i.e. don't overwrite their setting for width)
+ if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0)
+ {
+ width = this->getStrokeWidth();
+ if (width == 0)
+ width = -1;
+ }
+ }
+
+ if (width > 0 && !path->isEmpty())
+ {
+ SkStroke stroker(*this, width);
+ stroker.strokePath(*path, &strokePath);
+ path = &strokePath;
+ }
+
+ if (path == &src)
+ *dst = src;
+ else
+ {
+ SkASSERT(path == &effectPath || path == &strokePath);
+ dst->swap(*(SkPath*)path);
+ }
+
+ return width != 0; // return true if we're filled, or false if we're hairline (width == 0)
+}
+
+bool SkPaint::canComputeFastBounds() const {
+ // use bit-or since no need for early exit
+ return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
+ reinterpret_cast<uintptr_t>(this->getLooper()) |
+ reinterpret_cast<uintptr_t>(this->getRasterizer()) |
+ reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
+}
+
+const SkRect& SkPaint::computeFastBounds(const SkRect& src,
+ SkRect* storage) const {
+ SkASSERT(storage);
+
+ if (this->getStyle() != SkPaint::kFill_Style) {
+ // if we're stroked, outset the rect by the radius (and join type)
+ SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+
+ if (0 == radius) { // hairline
+ radius = SK_Scalar1;
+ } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+ SkScalar scale = this->getStrokeMiter();
+ if (scale > SK_Scalar1) {
+ radius = SkScalarMul(radius, scale);
+ }
+ }
+ storage->set(src.fLeft - radius, src.fTop - radius,
+ src.fRight + radius, src.fBottom + radius);
+ return *storage;
+ }
+ // no adjustments needed, just return the original rect
+ return src;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static bool has_thick_frame(const SkPaint& paint)
+{
+ return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style;
+}
+
+SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
+ const SkPaint& paint,
+ bool applyStrokeAndPathEffects,
+ bool forceLinearTextOn)
+ : fPaint(paint) /* make a copy of the paint */
+{
+ fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
+ true);
+
+ if (forceLinearTextOn)
+ fPaint.setLinearText(true);
+ fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
+
+ if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint))
+ applyStrokeAndPathEffects = false;
+
+ // can't use our canonical size if we need to apply patheffects/strokes
+ if (fPaint.isLinearText() && !applyStrokeAndPathEffects)
+ {
+ fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
+ fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+ }
+ else
+ fScale = SK_Scalar1;
+
+ if (!applyStrokeAndPathEffects)
+ {
+ fPaint.setStyle(SkPaint::kFill_Style);
+ fPaint.setPathEffect(NULL);
+ }
+
+ fCache = fPaint.detachCache(NULL);
+
+ SkPaint::Style style = SkPaint::kFill_Style;
+ SkPathEffect* pe = NULL;
+
+ if (!applyStrokeAndPathEffects)
+ {
+ style = paint.getStyle(); // restore
+ pe = paint.getPathEffect(); // restore
+ }
+ fPaint.setStyle(style);
+ fPaint.setPathEffect(pe);
+ fPaint.setMaskFilter(paint.getMaskFilter()); // restore
+
+ // now compute fXOffset if needed
+
+ SkScalar xOffset = 0;
+ if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first
+ {
+ int count;
+ SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length, &count, NULL), fScale);
+ if (paint.getTextAlign() == SkPaint::kCenter_Align)
+ width = SkScalarHalf(width);
+ xOffset = -width;
+ }
+ fXPos = xOffset;
+ fPrevAdvance = 0;
+
+ fText = text;
+ fStop = text + length;
+}
+
+SkTextToPathIter::~SkTextToPathIter()
+{
+ SkGlyphCache::AttachCache(fCache);
+}
+
+const SkPath* SkTextToPathIter::next(SkScalar* xpos)
+{
+ while (fText < fStop)
+ {
+ const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
+
+ fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
+ fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking();
+
+ if (glyph.fWidth)
+ {
+ if (xpos)
+ *xpos = fXPos;
+ return fCache->findPath(glyph);
+ }
+ }
+ return NULL;
+}