michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkPaint.h" michael@0: #include "SkAnnotation.h" michael@0: #include "SkAutoKern.h" michael@0: #include "SkColorFilter.h" michael@0: #include "SkData.h" michael@0: #include "SkDeviceProperties.h" michael@0: #include "SkFontDescriptor.h" michael@0: #include "SkFontHost.h" michael@0: #include "SkGlyphCache.h" michael@0: #include "SkImageFilter.h" michael@0: #include "SkMaskFilter.h" michael@0: #include "SkMaskGamma.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkPaintDefaults.h" michael@0: #include "SkPaintOptionsAndroid.h" michael@0: #include "SkPathEffect.h" michael@0: #include "SkRasterizer.h" michael@0: #include "SkScalar.h" michael@0: #include "SkScalerContext.h" michael@0: #include "SkShader.h" michael@0: #include "SkStringUtils.h" michael@0: #include "SkStroke.h" michael@0: #include "SkTextFormatParams.h" michael@0: #include "SkTextToPathIter.h" michael@0: #include "SkTLazy.h" michael@0: #include "SkTypeface.h" michael@0: #include "SkXfermode.h" michael@0: michael@0: enum { michael@0: kColor_DirtyBit = 1 << 0, michael@0: kBitfields_DirtyBit = 1 << 1, michael@0: kTextSize_DirtyBit = 1 << 2, michael@0: kTextScaleX_DirtyBit = 1 << 3, michael@0: kTextSkewX_DirtyBit = 1 << 4, michael@0: kStrokeWidth_DirtyBit = 1 << 5, michael@0: kStrokeMiter_DirtyBit = 1 << 6, michael@0: kPathEffect_DirtyBit = 1 << 7, michael@0: kShader_DirtyBit = 1 << 8, michael@0: kXfermode_DirtyBit = 1 << 9, michael@0: kMaskFilter_DirtyBit = 1 << 10, michael@0: kColorFilter_DirtyBit = 1 << 11, michael@0: kRasterizer_DirtyBit = 1 << 12, michael@0: kLooper_DirtyBit = 1 << 13, michael@0: kImageFilter_DirtyBit = 1 << 14, michael@0: kTypeface_DirtyBit = 1 << 15, michael@0: kAnnotation_DirtyBit = 1 << 16, michael@0: kPaintOptionsAndroid_DirtyBit = 1 << 17, michael@0: }; michael@0: michael@0: // define this to get a printf for out-of-range parameter in setters michael@0: // e.g. setTextSize(-1) michael@0: //#define SK_REPORT_API_RANGE_CHECK michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: #define GEN_ID_INC fGenerationID++ michael@0: #define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; } michael@0: #else michael@0: #define GEN_ID_INC michael@0: #define GEN_ID_INC_EVAL(expression) michael@0: #endif michael@0: michael@0: SkPaint::SkPaint() { michael@0: // since we may have padding, we zero everything so that our memcmp() call michael@0: // in operator== will work correctly. michael@0: // with this, we can skip 0 and null individual initializations michael@0: sk_bzero(this, sizeof(*this)); michael@0: michael@0: #if 0 // not needed with the bzero call above michael@0: fTypeface = NULL; michael@0: fTextSkewX = 0; michael@0: fPathEffect = NULL; michael@0: fShader = NULL; michael@0: fXfermode = NULL; michael@0: fMaskFilter = NULL; michael@0: fColorFilter = NULL; michael@0: fRasterizer = NULL; michael@0: fLooper = NULL; michael@0: fImageFilter = NULL; michael@0: fAnnotation = NULL; michael@0: fWidth = 0; michael@0: fDirtyBits = 0; michael@0: #endif michael@0: michael@0: fTextSize = SkPaintDefaults_TextSize; michael@0: fTextScaleX = SK_Scalar1; michael@0: fColor = SK_ColorBLACK; michael@0: fMiterLimit = SkPaintDefaults_MiterLimit; michael@0: fFlags = SkPaintDefaults_Flags; michael@0: fCapType = kDefault_Cap; michael@0: fJoinType = kDefault_Join; michael@0: fTextAlign = kLeft_Align; michael@0: fStyle = kFill_Style; michael@0: fTextEncoding = kUTF8_TextEncoding; michael@0: fHinting = SkPaintDefaults_Hinting; michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: new (&fPaintOptionsAndroid) SkPaintOptionsAndroid; michael@0: fGenerationID = 0; michael@0: #endif michael@0: } michael@0: michael@0: SkPaint::SkPaint(const SkPaint& src) { michael@0: memcpy(this, &src, sizeof(src)); michael@0: michael@0: SkSafeRef(fTypeface); michael@0: SkSafeRef(fPathEffect); michael@0: SkSafeRef(fShader); michael@0: SkSafeRef(fXfermode); michael@0: SkSafeRef(fMaskFilter); michael@0: SkSafeRef(fColorFilter); michael@0: SkSafeRef(fRasterizer); michael@0: SkSafeRef(fLooper); michael@0: SkSafeRef(fImageFilter); michael@0: SkSafeRef(fAnnotation); michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid); michael@0: #endif michael@0: } michael@0: michael@0: SkPaint::~SkPaint() { michael@0: SkSafeUnref(fTypeface); michael@0: SkSafeUnref(fPathEffect); michael@0: SkSafeUnref(fShader); michael@0: SkSafeUnref(fXfermode); michael@0: SkSafeUnref(fMaskFilter); michael@0: SkSafeUnref(fColorFilter); michael@0: SkSafeUnref(fRasterizer); michael@0: SkSafeUnref(fLooper); michael@0: SkSafeUnref(fImageFilter); michael@0: SkSafeUnref(fAnnotation); michael@0: } michael@0: michael@0: SkPaint& SkPaint::operator=(const SkPaint& src) { michael@0: SkASSERT(&src); michael@0: michael@0: SkSafeRef(src.fTypeface); michael@0: SkSafeRef(src.fPathEffect); michael@0: SkSafeRef(src.fShader); michael@0: SkSafeRef(src.fXfermode); michael@0: SkSafeRef(src.fMaskFilter); michael@0: SkSafeRef(src.fColorFilter); michael@0: SkSafeRef(src.fRasterizer); michael@0: SkSafeRef(src.fLooper); michael@0: SkSafeRef(src.fImageFilter); michael@0: SkSafeRef(src.fAnnotation); michael@0: michael@0: SkSafeUnref(fTypeface); michael@0: SkSafeUnref(fPathEffect); michael@0: SkSafeUnref(fShader); michael@0: SkSafeUnref(fXfermode); michael@0: SkSafeUnref(fMaskFilter); michael@0: SkSafeUnref(fColorFilter); michael@0: SkSafeUnref(fRasterizer); michael@0: SkSafeUnref(fLooper); michael@0: SkSafeUnref(fImageFilter); michael@0: SkSafeUnref(fAnnotation); michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: fPaintOptionsAndroid.~SkPaintOptionsAndroid(); michael@0: michael@0: uint32_t oldGenerationID = fGenerationID; michael@0: #endif michael@0: memcpy(this, &src, sizeof(src)); michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: fGenerationID = oldGenerationID + 1; michael@0: michael@0: new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid); michael@0: #endif michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: bool operator==(const SkPaint& a, const SkPaint& b) { michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: //assumes that fGenerationID is the last field in the struct michael@0: return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID)); michael@0: #else michael@0: return !memcmp(&a, &b, sizeof(a)); michael@0: #endif michael@0: } michael@0: michael@0: void SkPaint::reset() { michael@0: SkPaint init; michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: uint32_t oldGenerationID = fGenerationID; michael@0: #endif michael@0: *this = init; michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: fGenerationID = oldGenerationID + 1; michael@0: #endif michael@0: } michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: uint32_t SkPaint::getGenerationID() const { michael@0: return fGenerationID; michael@0: } michael@0: michael@0: void SkPaint::setGenerationID(uint32_t generationID) { michael@0: fGenerationID = generationID; michael@0: } michael@0: michael@0: unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const { michael@0: SkAutoGlyphCache autoCache(*this, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: return cache->getBaseGlyphCount(text); michael@0: } michael@0: michael@0: void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) { michael@0: if (options != fPaintOptionsAndroid) { michael@0: fPaintOptionsAndroid = options; michael@0: GEN_ID_INC; michael@0: fDirtyBits |= kPaintOptionsAndroid_DirtyBit; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: SkPaint::FilterLevel SkPaint::getFilterLevel() const { michael@0: int level = 0; michael@0: if (fFlags & kFilterBitmap_Flag) { michael@0: level |= 1; michael@0: } michael@0: if (fFlags & kHighQualityFilterBitmap_Flag) { michael@0: level |= 2; michael@0: } michael@0: return (FilterLevel)level; michael@0: } michael@0: michael@0: void SkPaint::setFilterLevel(FilterLevel level) { michael@0: unsigned mask = kFilterBitmap_Flag | kHighQualityFilterBitmap_Flag; michael@0: unsigned flags = 0; michael@0: if (level & 1) { michael@0: flags |= kFilterBitmap_Flag; michael@0: } michael@0: if (level & 2) { michael@0: flags |= kHighQualityFilterBitmap_Flag; michael@0: } michael@0: this->setFlags((fFlags & ~mask) | flags); michael@0: } michael@0: michael@0: void SkPaint::setHinting(Hinting hintingLevel) { michael@0: GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting); michael@0: fHinting = hintingLevel; michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } michael@0: michael@0: void SkPaint::setFlags(uint32_t flags) { michael@0: GEN_ID_INC_EVAL(fFlags != flags); michael@0: fFlags = flags; michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } michael@0: michael@0: void SkPaint::setAntiAlias(bool doAA) { michael@0: this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setDither(bool doDither) { michael@0: this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setSubpixelText(bool doSubpixel) { michael@0: this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setLCDRenderText(bool doLCDRender) { michael@0: this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) { michael@0: this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setAutohinted(bool useAutohinter) { michael@0: this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setLinearText(bool doLinearText) { michael@0: this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setVerticalText(bool doVertical) { michael@0: this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setUnderlineText(bool doUnderline) { michael@0: this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setStrikeThruText(bool doStrikeThru) { michael@0: this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setFakeBoldText(bool doFakeBold) { michael@0: this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setDevKernText(bool doDevKern) { michael@0: this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setDistanceFieldTextTEMP(bool doDistanceFieldText) { michael@0: this->setFlags(SkSetClearMask(fFlags, doDistanceFieldText, kDistanceFieldTextTEMP_Flag)); michael@0: } michael@0: michael@0: void SkPaint::setStyle(Style style) { michael@0: if ((unsigned)style < kStyleCount) { michael@0: GEN_ID_INC_EVAL((unsigned)style != fStyle); michael@0: fStyle = style; michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setStyle(%d) out of range\n", style); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setColor(SkColor color) { michael@0: GEN_ID_INC_EVAL(color != fColor); michael@0: fColor = color; michael@0: fDirtyBits |= kColor_DirtyBit; michael@0: } michael@0: michael@0: void SkPaint::setAlpha(U8CPU a) { michael@0: this->setColor(SkColorSetARGB(a, SkColorGetR(fColor), michael@0: SkColorGetG(fColor), SkColorGetB(fColor))); michael@0: } michael@0: michael@0: void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { michael@0: this->setColor(SkColorSetARGB(a, r, g, b)); michael@0: } michael@0: michael@0: void SkPaint::setStrokeWidth(SkScalar width) { michael@0: if (width >= 0) { michael@0: GEN_ID_INC_EVAL(width != fWidth); michael@0: fWidth = width; michael@0: fDirtyBits |= kStrokeWidth_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setStrokeWidth() called with negative value\n"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setStrokeMiter(SkScalar limit) { michael@0: if (limit >= 0) { michael@0: GEN_ID_INC_EVAL(limit != fMiterLimit); michael@0: fMiterLimit = limit; michael@0: fDirtyBits |= kStrokeMiter_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setStrokeMiter() called with negative value\n"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setStrokeCap(Cap ct) { michael@0: if ((unsigned)ct < kCapCount) { michael@0: GEN_ID_INC_EVAL((unsigned)ct != fCapType); michael@0: fCapType = SkToU8(ct); michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setStrokeJoin(Join jt) { michael@0: if ((unsigned)jt < kJoinCount) { michael@0: GEN_ID_INC_EVAL((unsigned)jt != fJoinType); michael@0: fJoinType = SkToU8(jt); michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkPaint::setTextAlign(Align align) { michael@0: if ((unsigned)align < kAlignCount) { michael@0: GEN_ID_INC_EVAL((unsigned)align != fTextAlign); michael@0: fTextAlign = SkToU8(align); michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setTextSize(SkScalar ts) { michael@0: if (ts >= 0) { michael@0: GEN_ID_INC_EVAL(ts != fTextSize); michael@0: fTextSize = ts; michael@0: fDirtyBits |= kTextSize_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setTextSize() called with negative value\n"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void SkPaint::setTextScaleX(SkScalar scaleX) { michael@0: GEN_ID_INC_EVAL(scaleX != fTextScaleX); michael@0: fTextScaleX = scaleX; michael@0: fDirtyBits |= kTextScaleX_DirtyBit; michael@0: } michael@0: michael@0: void SkPaint::setTextSkewX(SkScalar skewX) { michael@0: GEN_ID_INC_EVAL(skewX != fTextSkewX); michael@0: fTextSkewX = skewX; michael@0: fDirtyBits |= kTextSkewX_DirtyBit; michael@0: } michael@0: michael@0: void SkPaint::setTextEncoding(TextEncoding encoding) { michael@0: if ((unsigned)encoding <= kGlyphID_TextEncoding) { michael@0: GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding); michael@0: fTextEncoding = encoding; michael@0: fDirtyBits |= kBitfields_DirtyBit; michael@0: } else { michael@0: #ifdef SK_REPORT_API_RANGE_CHECK michael@0: SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Returns dst with the given bitmask enabled or disabled, depending on value. michael@0: inline static uint32_t set_mask(uint32_t dst, uint32_t bitmask, bool value) { michael@0: return value ? (dst | bitmask) : (dst & ~bitmask); michael@0: } michael@0: michael@0: SkTypeface* SkPaint::setTypeface(SkTypeface* font) { michael@0: SkRefCnt_SafeAssign(fTypeface, font); michael@0: GEN_ID_INC; michael@0: fDirtyBits = set_mask(fDirtyBits, kTypeface_DirtyBit, font != NULL); michael@0: return font; michael@0: } michael@0: michael@0: SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) { michael@0: SkRefCnt_SafeAssign(fRasterizer, r); michael@0: GEN_ID_INC; michael@0: fDirtyBits = set_mask(fDirtyBits, kRasterizer_DirtyBit, r != NULL); michael@0: return r; michael@0: } michael@0: michael@0: SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) { michael@0: SkRefCnt_SafeAssign(fLooper, looper); michael@0: GEN_ID_INC; michael@0: fDirtyBits = set_mask(fDirtyBits, kLooper_DirtyBit, looper != NULL); michael@0: return looper; michael@0: } michael@0: michael@0: SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) { michael@0: SkRefCnt_SafeAssign(fImageFilter, imageFilter); michael@0: GEN_ID_INC; michael@0: fDirtyBits = set_mask(fDirtyBits, kImageFilter_DirtyBit, imageFilter != NULL); michael@0: return imageFilter; michael@0: } michael@0: michael@0: SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) { michael@0: SkRefCnt_SafeAssign(fAnnotation, annotation); michael@0: GEN_ID_INC; michael@0: fDirtyBits = set_mask(fDirtyBits, kAnnotation_DirtyBit, annotation != NULL); michael@0: return annotation; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static SkScalar mag2(SkScalar x, SkScalar y) { michael@0: return x * x + y * y; michael@0: } michael@0: michael@0: static bool tooBig(const SkMatrix& m, SkScalar ma2max) { michael@0: return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max michael@0: || michael@0: mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max; michael@0: } michael@0: michael@0: bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM) { michael@0: SkASSERT(!ctm.hasPerspective()); michael@0: SkASSERT(!textM.hasPerspective()); michael@0: michael@0: SkMatrix matrix; michael@0: matrix.setConcat(ctm, textM); michael@0: return tooBig(matrix, MaxCacheSize2()); michael@0: } michael@0: michael@0: bool SkPaint::tooBigToUseCache(const SkMatrix& ctm) const { michael@0: SkMatrix textM; michael@0: return TooBigToUseCache(ctm, *this->setTextMatrix(&textM)); michael@0: } michael@0: michael@0: bool SkPaint::tooBigToUseCache() const { michael@0: SkMatrix textM; michael@0: return tooBig(*this->setTextMatrix(&textM), MaxCacheSize2()); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkGlyphCache.h" michael@0: #include "SkUtils.h" michael@0: michael@0: static void DetachDescProc(SkTypeface* typeface, const SkDescriptor* desc, michael@0: void* context) { michael@0: *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(typeface, desc); michael@0: } michael@0: michael@0: int SkPaint::textToGlyphs(const void* textData, size_t byteLength, michael@0: uint16_t glyphs[]) const { michael@0: if (byteLength == 0) { michael@0: return 0; michael@0: } michael@0: michael@0: SkASSERT(textData != NULL); michael@0: michael@0: if (NULL == glyphs) { michael@0: switch (this->getTextEncoding()) { michael@0: case kUTF8_TextEncoding: michael@0: return SkUTF8_CountUnichars((const char*)textData, byteLength); michael@0: case kUTF16_TextEncoding: michael@0: return SkUTF16_CountUnichars((const uint16_t*)textData, michael@0: byteLength >> 1); michael@0: case kUTF32_TextEncoding: michael@0: return byteLength >> 2; michael@0: case kGlyphID_TextEncoding: michael@0: return byteLength >> 1; michael@0: default: michael@0: SkDEBUGFAIL("unknown text encoding"); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // if we get here, we have a valid glyphs[] array, so time to fill it in michael@0: michael@0: // handle this encoding before the setup for the glyphcache michael@0: if (this->getTextEncoding() == kGlyphID_TextEncoding) { michael@0: // we want to ignore the low bit of byteLength michael@0: memcpy(glyphs, textData, byteLength >> 1 << 1); michael@0: return byteLength >> 1; michael@0: } michael@0: michael@0: SkAutoGlyphCache autoCache(*this, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: const char* text = (const char*)textData; michael@0: const char* stop = text + byteLength; michael@0: uint16_t* gptr = glyphs; michael@0: michael@0: switch (this->getTextEncoding()) { michael@0: case SkPaint::kUTF8_TextEncoding: michael@0: while (text < stop) { michael@0: *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text)); michael@0: } michael@0: break; michael@0: case SkPaint::kUTF16_TextEncoding: { michael@0: const uint16_t* text16 = (const uint16_t*)text; michael@0: const uint16_t* stop16 = (const uint16_t*)stop; michael@0: while (text16 < stop16) { michael@0: *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16)); michael@0: } michael@0: break; michael@0: } michael@0: case kUTF32_TextEncoding: { michael@0: const int32_t* text32 = (const int32_t*)text; michael@0: const int32_t* stop32 = (const int32_t*)stop; michael@0: while (text32 < stop32) { michael@0: *gptr++ = cache->unicharToGlyph(*text32++); michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: SkDEBUGFAIL("unknown text encoding"); michael@0: } michael@0: return gptr - glyphs; michael@0: } michael@0: michael@0: bool SkPaint::containsText(const void* textData, size_t byteLength) const { michael@0: if (0 == byteLength) { michael@0: return true; michael@0: } michael@0: michael@0: SkASSERT(textData != NULL); michael@0: michael@0: // handle this encoding before the setup for the glyphcache michael@0: if (this->getTextEncoding() == kGlyphID_TextEncoding) { michael@0: const uint16_t* glyphID = static_cast(textData); michael@0: size_t count = byteLength >> 1; michael@0: for (size_t i = 0; i < count; i++) { michael@0: if (0 == glyphID[i]) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkAutoGlyphCache autoCache(*this, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: switch (this->getTextEncoding()) { michael@0: case SkPaint::kUTF8_TextEncoding: { michael@0: const char* text = static_cast(textData); michael@0: const char* stop = text + byteLength; michael@0: while (text < stop) { michael@0: if (0 == cache->unicharToGlyph(SkUTF8_NextUnichar(&text))) { michael@0: return false; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case SkPaint::kUTF16_TextEncoding: { michael@0: const uint16_t* text = static_cast(textData); michael@0: const uint16_t* stop = text + (byteLength >> 1); michael@0: while (text < stop) { michael@0: if (0 == cache->unicharToGlyph(SkUTF16_NextUnichar(&text))) { michael@0: return false; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case SkPaint::kUTF32_TextEncoding: { michael@0: const int32_t* text = static_cast(textData); michael@0: const int32_t* stop = text + (byteLength >> 2); michael@0: while (text < stop) { michael@0: if (0 == cache->unicharToGlyph(*text++)) { michael@0: return false; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: SkDEBUGFAIL("unknown text encoding"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, michael@0: SkUnichar textData[]) const { michael@0: if (count <= 0) { michael@0: return; michael@0: } michael@0: michael@0: SkASSERT(glyphs != NULL); michael@0: SkASSERT(textData != NULL); michael@0: michael@0: SkAutoGlyphCache autoCache(*this, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: for (int index = 0; index < count; index++) { michael@0: textData[index] = cache->glyphToUnichar(glyphs[index]); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *ptr++; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharMetrics(uni); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *--ptr; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharMetrics(uni); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: unsigned glyphID = *ptr; michael@0: ptr += 1; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDMetrics(glyphID); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: ptr -= 1; michael@0: unsigned glyphID = *ptr; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDMetrics(glyphID); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharAdvance(SkUTF8_NextUnichar(text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *ptr++; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharAdvance(uni); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *--ptr; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharAdvance(uni); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: unsigned glyphID = *ptr; michael@0: ptr += 1; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDAdvance(glyphID); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, michael@0: const char** text) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: ptr -= 1; michael@0: unsigned glyphID = *ptr; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDAdvance(glyphID); michael@0: } michael@0: michael@0: SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd, michael@0: bool needFullMetrics) const { michael@0: static const SkMeasureCacheProc gMeasureCacheProcs[] = { michael@0: sk_getMetrics_utf8_next, michael@0: sk_getMetrics_utf16_next, michael@0: sk_getMetrics_utf32_next, michael@0: sk_getMetrics_glyph_next, michael@0: michael@0: sk_getMetrics_utf8_prev, michael@0: sk_getMetrics_utf16_prev, michael@0: sk_getMetrics_utf32_prev, michael@0: sk_getMetrics_glyph_prev, michael@0: michael@0: sk_getAdvance_utf8_next, michael@0: sk_getAdvance_utf16_next, michael@0: sk_getAdvance_utf32_next, michael@0: sk_getAdvance_glyph_next, michael@0: michael@0: sk_getAdvance_utf8_prev, michael@0: sk_getAdvance_utf16_prev, michael@0: sk_getAdvance_utf32_prev, michael@0: sk_getAdvance_glyph_prev michael@0: }; michael@0: michael@0: unsigned index = this->getTextEncoding(); michael@0: michael@0: if (kBackward_TextBufferDirection == tbd) { michael@0: index += 4; michael@0: } michael@0: if (!needFullMetrics && !this->isDevKernText()) { michael@0: index += 8; michael@0: } michael@0: michael@0: SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs)); michael@0: return gMeasureCacheProcs[index]; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache, michael@0: const char** text, SkFixed, SkFixed) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache, michael@0: const char** text, SkFixed x, SkFixed y) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, michael@0: const char** text, SkFixed, SkFixed) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache, michael@0: const char** text, SkFixed x, SkFixed y) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text), michael@0: x, y); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache, michael@0: const char** text, SkFixed, SkFixed) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *ptr++; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharMetrics(uni); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache, michael@0: const char** text, SkFixed x, SkFixed y) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const int32_t* ptr = *(const int32_t**)text; michael@0: SkUnichar uni = *--ptr; michael@0: *text = (const char*)ptr; michael@0: return cache->getUnicharMetrics(uni, x, y); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, michael@0: const char** text, SkFixed, SkFixed) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: unsigned glyphID = *ptr; michael@0: ptr += 1; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDMetrics(glyphID); michael@0: } michael@0: michael@0: static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache, michael@0: const char** text, SkFixed x, SkFixed y) { michael@0: SkASSERT(cache != NULL); michael@0: SkASSERT(text != NULL); michael@0: michael@0: const uint16_t* ptr = *(const uint16_t**)text; michael@0: unsigned glyphID = *ptr; michael@0: ptr += 1; michael@0: *text = (const char*)ptr; michael@0: return cache->getGlyphIDMetrics(glyphID, x, y); michael@0: } michael@0: michael@0: SkDrawCacheProc SkPaint::getDrawCacheProc() const { michael@0: static const SkDrawCacheProc gDrawCacheProcs[] = { michael@0: sk_getMetrics_utf8_00, michael@0: sk_getMetrics_utf16_00, michael@0: sk_getMetrics_utf32_00, michael@0: sk_getMetrics_glyph_00, michael@0: michael@0: sk_getMetrics_utf8_xy, michael@0: sk_getMetrics_utf16_xy, michael@0: sk_getMetrics_utf32_xy, michael@0: sk_getMetrics_glyph_xy michael@0: }; michael@0: michael@0: unsigned index = this->getTextEncoding(); michael@0: if (fFlags & kSubpixelText_Flag) { michael@0: index += 4; michael@0: } michael@0: michael@0: SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs)); michael@0: return gDrawCacheProcs[index]; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #define TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE ( \ michael@0: SkPaint::kDevKernText_Flag | \ michael@0: SkPaint::kLinearText_Flag | \ michael@0: SkPaint::kLCDRenderText_Flag | \ michael@0: SkPaint::kEmbeddedBitmapText_Flag | \ michael@0: SkPaint::kAutoHinting_Flag | \ michael@0: SkPaint::kGenA8FromLCD_Flag ) michael@0: michael@0: SkScalar SkPaint::setupForAsPaths() { michael@0: uint32_t flags = this->getFlags(); michael@0: // clear the flags we don't care about michael@0: flags &= ~TEXT_AS_PATHS_PAINT_FLAGS_TO_IGNORE; michael@0: // set the flags we do care about michael@0: flags |= SkPaint::kSubpixelText_Flag; michael@0: michael@0: this->setFlags(flags); michael@0: this->setHinting(SkPaint::kNo_Hinting); michael@0: michael@0: SkScalar textSize = fTextSize; michael@0: this->setTextSize(kCanonicalTextSizeForPaths); michael@0: return textSize / kCanonicalTextSizeForPaths; michael@0: } michael@0: michael@0: class SkCanonicalizePaint { michael@0: public: michael@0: SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) { michael@0: if (paint.isLinearText() || paint.tooBigToUseCache()) { michael@0: SkPaint* p = fLazy.set(paint); michael@0: fScale = p->setupForAsPaths(); michael@0: fPaint = p; michael@0: } michael@0: } michael@0: michael@0: const SkPaint& getPaint() const { return *fPaint; } michael@0: michael@0: /** michael@0: * Returns 0 if the paint was unmodified, or the scale factor need to michael@0: * the original textSize michael@0: */ michael@0: SkScalar getScale() const { return fScale; } michael@0: michael@0: private: michael@0: const SkPaint* fPaint; michael@0: SkScalar fScale; michael@0: SkTLazy fLazy; michael@0: }; michael@0: michael@0: static void set_bounds(const SkGlyph& g, SkRect* bounds) { michael@0: bounds->set(SkIntToScalar(g.fLeft), michael@0: SkIntToScalar(g.fTop), michael@0: SkIntToScalar(g.fLeft + g.fWidth), michael@0: SkIntToScalar(g.fTop + g.fHeight)); michael@0: } michael@0: michael@0: // 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so michael@0: // we don't overflow along the way michael@0: typedef int64_t Sk48Dot16; michael@0: michael@0: static inline float Sk48Dot16ToScalar(Sk48Dot16 x) { michael@0: return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f) michael@0: } michael@0: michael@0: static void join_bounds_x(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) { michael@0: SkScalar sx = Sk48Dot16ToScalar(dx); michael@0: bounds->join(SkIntToScalar(g.fLeft) + sx, michael@0: SkIntToScalar(g.fTop), michael@0: SkIntToScalar(g.fLeft + g.fWidth) + sx, michael@0: SkIntToScalar(g.fTop + g.fHeight)); michael@0: } michael@0: michael@0: static void join_bounds_y(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dy) { michael@0: SkScalar sy = Sk48Dot16ToScalar(dy); michael@0: bounds->join(SkIntToScalar(g.fLeft), michael@0: SkIntToScalar(g.fTop) + sy, michael@0: SkIntToScalar(g.fLeft + g.fWidth), michael@0: SkIntToScalar(g.fTop + g.fHeight) + sy); michael@0: } michael@0: michael@0: typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, Sk48Dot16); michael@0: michael@0: // xyIndex is 0 for fAdvanceX or 1 for fAdvanceY michael@0: static SkFixed advance(const SkGlyph& glyph, int xyIndex) { michael@0: SkASSERT(0 == xyIndex || 1 == xyIndex); michael@0: return (&glyph.fAdvanceX)[xyIndex]; michael@0: } michael@0: michael@0: SkScalar SkPaint::measure_text(SkGlyphCache* cache, michael@0: const char* text, size_t byteLength, michael@0: int* count, SkRect* bounds) const { michael@0: SkASSERT(count); michael@0: if (byteLength == 0) { michael@0: *count = 0; michael@0: if (bounds) { michael@0: bounds->setEmpty(); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: SkMeasureCacheProc glyphCacheProc; michael@0: glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection, michael@0: NULL != bounds); michael@0: michael@0: int xyIndex; michael@0: JoinBoundsProc joinBoundsProc; michael@0: if (this->isVerticalText()) { michael@0: xyIndex = 1; michael@0: joinBoundsProc = join_bounds_y; michael@0: } else { michael@0: xyIndex = 0; michael@0: joinBoundsProc = join_bounds_x; michael@0: } michael@0: michael@0: int n = 1; michael@0: const char* stop = (const char*)text + byteLength; michael@0: const SkGlyph* g = &glyphCacheProc(cache, &text); michael@0: // our accumulated fixed-point advances might overflow 16.16, so we use michael@0: // a 48.16 (64bit) accumulator, and then convert that to scalar at the michael@0: // very end. michael@0: Sk48Dot16 x = advance(*g, xyIndex); michael@0: michael@0: SkAutoKern autokern; michael@0: michael@0: if (NULL == bounds) { michael@0: if (this->isDevKernText()) { michael@0: int rsb; michael@0: for (; text < stop; n++) { michael@0: rsb = g->fRsbDelta; michael@0: g = &glyphCacheProc(cache, &text); michael@0: x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + advance(*g, xyIndex); michael@0: } michael@0: } else { michael@0: for (; text < stop; n++) { michael@0: x += advance(glyphCacheProc(cache, &text), xyIndex); michael@0: } michael@0: } michael@0: } else { michael@0: set_bounds(*g, bounds); michael@0: if (this->isDevKernText()) { michael@0: int rsb; michael@0: for (; text < stop; n++) { michael@0: rsb = g->fRsbDelta; michael@0: g = &glyphCacheProc(cache, &text); michael@0: x += SkAutoKern_AdjustF(rsb, g->fLsbDelta); michael@0: joinBoundsProc(*g, bounds, x); michael@0: x += advance(*g, xyIndex); michael@0: } michael@0: } else { michael@0: for (; text < stop; n++) { michael@0: g = &glyphCacheProc(cache, &text); michael@0: joinBoundsProc(*g, bounds, x); michael@0: x += advance(*g, xyIndex); michael@0: } michael@0: } michael@0: } michael@0: SkASSERT(text == stop); michael@0: michael@0: *count = n; michael@0: return Sk48Dot16ToScalar(x); michael@0: } michael@0: michael@0: SkScalar SkPaint::measureText(const void* textData, size_t length, michael@0: SkRect* bounds, SkScalar zoom) const { michael@0: const char* text = (const char*)textData; michael@0: SkASSERT(text != NULL || length == 0); michael@0: michael@0: SkCanonicalizePaint canon(*this); michael@0: const SkPaint& paint = canon.getPaint(); michael@0: SkScalar scale = canon.getScale(); michael@0: michael@0: SkMatrix zoomMatrix, *zoomPtr = NULL; michael@0: if (zoom) { michael@0: zoomMatrix.setScale(zoom, zoom); michael@0: zoomPtr = &zoomMatrix; michael@0: } michael@0: michael@0: SkAutoGlyphCache autoCache(paint, NULL, zoomPtr); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: SkScalar width = 0; michael@0: michael@0: if (length > 0) { michael@0: int tempCount; michael@0: michael@0: width = paint.measure_text(cache, text, length, &tempCount, bounds); michael@0: if (scale) { michael@0: width = SkScalarMul(width, scale); michael@0: if (bounds) { michael@0: bounds->fLeft = SkScalarMul(bounds->fLeft, scale); michael@0: bounds->fTop = SkScalarMul(bounds->fTop, scale); michael@0: bounds->fRight = SkScalarMul(bounds->fRight, scale); michael@0: bounds->fBottom = SkScalarMul(bounds->fBottom, scale); michael@0: } michael@0: } michael@0: } else if (bounds) { michael@0: // ensure that even if we don't measure_text we still update the bounds michael@0: bounds->setEmpty(); michael@0: } michael@0: return width; michael@0: } michael@0: michael@0: typedef bool (*SkTextBufferPred)(const char* text, const char* stop); michael@0: michael@0: static bool forward_textBufferPred(const char* text, const char* stop) { michael@0: return text < stop; michael@0: } michael@0: michael@0: static bool backward_textBufferPred(const char* text, const char* stop) { michael@0: return text > stop; michael@0: } michael@0: michael@0: static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd, michael@0: const char** text, size_t length, michael@0: const char** stop) { michael@0: if (SkPaint::kForward_TextBufferDirection == tbd) { michael@0: *stop = *text + length; michael@0: return forward_textBufferPred; michael@0: } else { michael@0: // text should point to the end of the buffer, and stop to the beginning michael@0: *stop = *text; michael@0: *text += length; michael@0: return backward_textBufferPred; michael@0: } michael@0: } michael@0: michael@0: size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, michael@0: SkScalar* measuredWidth, michael@0: TextBufferDirection tbd) const { michael@0: if (0 == length || 0 >= maxWidth) { michael@0: if (measuredWidth) { michael@0: *measuredWidth = 0; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: if (0 == fTextSize) { michael@0: if (measuredWidth) { michael@0: *measuredWidth = 0; michael@0: } michael@0: return length; michael@0: } michael@0: michael@0: SkASSERT(textD != NULL); michael@0: const char* text = (const char*)textD; michael@0: michael@0: SkCanonicalizePaint canon(*this); michael@0: const SkPaint& paint = canon.getPaint(); michael@0: SkScalar scale = canon.getScale(); michael@0: michael@0: // adjust max in case we changed the textSize in paint michael@0: if (scale) { michael@0: maxWidth /= scale; michael@0: } michael@0: michael@0: SkAutoGlyphCache autoCache(paint, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(tbd, false); michael@0: const char* stop; michael@0: SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop); michael@0: const int xyIndex = paint.isVerticalText() ? 1 : 0; michael@0: // use 64bits for our accumulator, to avoid overflowing 16.16 michael@0: Sk48Dot16 max = SkScalarToFixed(maxWidth); michael@0: Sk48Dot16 width = 0; michael@0: michael@0: SkAutoKern autokern; michael@0: michael@0: if (this->isDevKernText()) { michael@0: int rsb = 0; michael@0: while (pred(text, stop)) { michael@0: const char* curr = text; michael@0: const SkGlyph& g = glyphCacheProc(cache, &text); michael@0: SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + advance(g, xyIndex); michael@0: if ((width += x) > max) { michael@0: width -= x; michael@0: text = curr; michael@0: break; michael@0: } michael@0: rsb = g.fRsbDelta; michael@0: } michael@0: } else { michael@0: while (pred(text, stop)) { michael@0: const char* curr = text; michael@0: SkFixed x = advance(glyphCacheProc(cache, &text), xyIndex); michael@0: if ((width += x) > max) { michael@0: width -= x; michael@0: text = curr; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (measuredWidth) { michael@0: SkScalar scalarWidth = Sk48Dot16ToScalar(width); michael@0: if (scale) { michael@0: scalarWidth = SkScalarMul(scalarWidth, scale); michael@0: } michael@0: *measuredWidth = scalarWidth; michael@0: } michael@0: michael@0: // return the number of bytes measured michael@0: return (kForward_TextBufferDirection == tbd) ? michael@0: text - stop + length : stop - text + length; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) { michael@0: *(SkPaint::FontMetrics*)context = cache->getFontMetrics(); michael@0: return false; // don't detach the cache michael@0: } michael@0: michael@0: static void FontMetricsDescProc(SkTypeface* typeface, const SkDescriptor* desc, michael@0: void* context) { michael@0: SkGlyphCache::VisitCache(typeface, desc, FontMetricsCacheProc, context); michael@0: } michael@0: michael@0: SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { michael@0: SkCanonicalizePaint canon(*this); michael@0: const SkPaint& paint = canon.getPaint(); michael@0: SkScalar scale = canon.getScale(); michael@0: michael@0: SkMatrix zoomMatrix, *zoomPtr = NULL; michael@0: if (zoom) { michael@0: zoomMatrix.setScale(zoom, zoom); michael@0: zoomPtr = &zoomMatrix; michael@0: } michael@0: michael@0: FontMetrics storage; michael@0: if (NULL == metrics) { michael@0: metrics = &storage; michael@0: } michael@0: michael@0: paint.descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true); michael@0: michael@0: if (scale) { michael@0: metrics->fTop = SkScalarMul(metrics->fTop, scale); michael@0: metrics->fAscent = SkScalarMul(metrics->fAscent, scale); michael@0: metrics->fDescent = SkScalarMul(metrics->fDescent, scale); michael@0: metrics->fBottom = SkScalarMul(metrics->fBottom, scale); michael@0: metrics->fLeading = SkScalarMul(metrics->fLeading, scale); michael@0: metrics->fAvgCharWidth = SkScalarMul(metrics->fAvgCharWidth, scale); michael@0: metrics->fXMin = SkScalarMul(metrics->fXMin, scale); michael@0: metrics->fXMax = SkScalarMul(metrics->fXMax, scale); michael@0: metrics->fXHeight = SkScalarMul(metrics->fXHeight, scale); michael@0: metrics->fUnderlineThickness = SkScalarMul(metrics->fUnderlineThickness, scale); michael@0: metrics->fUnderlinePosition = SkScalarMul(metrics->fUnderlinePosition, scale); michael@0: } michael@0: return metrics->fDescent - metrics->fAscent + metrics->fLeading; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) { michael@0: bounds->set(g.fLeft * scale, michael@0: g.fTop * scale, michael@0: (g.fLeft + g.fWidth) * scale, michael@0: (g.fTop + g.fHeight) * scale); michael@0: } michael@0: michael@0: int SkPaint::getTextWidths(const void* textData, size_t byteLength, michael@0: SkScalar widths[], SkRect bounds[]) const { michael@0: if (0 == byteLength) { michael@0: return 0; michael@0: } michael@0: michael@0: SkASSERT(NULL != textData); michael@0: michael@0: if (NULL == widths && NULL == bounds) { michael@0: return this->countText(textData, byteLength); michael@0: } michael@0: michael@0: SkCanonicalizePaint canon(*this); michael@0: const SkPaint& paint = canon.getPaint(); michael@0: SkScalar scale = canon.getScale(); michael@0: michael@0: SkAutoGlyphCache autoCache(paint, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: SkMeasureCacheProc glyphCacheProc; michael@0: glyphCacheProc = paint.getMeasureCacheProc(kForward_TextBufferDirection, michael@0: NULL != bounds); michael@0: michael@0: const char* text = (const char*)textData; michael@0: const char* stop = text + byteLength; michael@0: int count = 0; michael@0: const int xyIndex = paint.isVerticalText() ? 1 : 0; michael@0: michael@0: if (this->isDevKernText()) { michael@0: // we adjust the widths returned here through auto-kerning michael@0: SkAutoKern autokern; michael@0: SkFixed prevWidth = 0; michael@0: michael@0: if (scale) { michael@0: while (text < stop) { michael@0: const SkGlyph& g = glyphCacheProc(cache, &text); michael@0: if (widths) { michael@0: SkFixed adjust = autokern.adjust(g); michael@0: michael@0: if (count > 0) { michael@0: SkScalar w = SkFixedToScalar(prevWidth + adjust); michael@0: *widths++ = SkScalarMul(w, scale); michael@0: } michael@0: prevWidth = advance(g, xyIndex); michael@0: } michael@0: if (bounds) { michael@0: set_bounds(g, bounds++, scale); michael@0: } michael@0: ++count; michael@0: } michael@0: if (count > 0 && widths) { michael@0: *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale); michael@0: } michael@0: } else { michael@0: while (text < stop) { michael@0: const SkGlyph& g = glyphCacheProc(cache, &text); michael@0: if (widths) { michael@0: SkFixed adjust = autokern.adjust(g); michael@0: michael@0: if (count > 0) { michael@0: *widths++ = SkFixedToScalar(prevWidth + adjust); michael@0: } michael@0: prevWidth = advance(g, xyIndex); michael@0: } michael@0: if (bounds) { michael@0: set_bounds(g, bounds++); michael@0: } michael@0: ++count; michael@0: } michael@0: if (count > 0 && widths) { michael@0: *widths = SkFixedToScalar(prevWidth); michael@0: } michael@0: } michael@0: } else { // no devkern michael@0: if (scale) { michael@0: while (text < stop) { michael@0: const SkGlyph& g = glyphCacheProc(cache, &text); michael@0: if (widths) { michael@0: *widths++ = SkScalarMul(SkFixedToScalar(advance(g, xyIndex)), michael@0: scale); michael@0: } michael@0: if (bounds) { michael@0: set_bounds(g, bounds++, scale); michael@0: } michael@0: ++count; michael@0: } michael@0: } else { michael@0: while (text < stop) { michael@0: const SkGlyph& g = glyphCacheProc(cache, &text); michael@0: if (widths) { michael@0: *widths++ = SkFixedToScalar(advance(g, xyIndex)); michael@0: } michael@0: if (bounds) { michael@0: set_bounds(g, bounds++); michael@0: } michael@0: ++count; michael@0: } michael@0: } michael@0: } michael@0: michael@0: SkASSERT(text == stop); michael@0: return count; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkDraw.h" michael@0: michael@0: void SkPaint::getTextPath(const void* textData, size_t length, michael@0: SkScalar x, SkScalar y, SkPath* path) const { michael@0: SkASSERT(length == 0 || textData != NULL); michael@0: michael@0: const char* text = (const char*)textData; michael@0: if (text == NULL || length == 0 || path == NULL) { michael@0: return; michael@0: } michael@0: michael@0: SkTextToPathIter iter(text, length, *this, false); michael@0: SkMatrix matrix; michael@0: SkScalar prevXPos = 0; michael@0: michael@0: matrix.setScale(iter.getPathScale(), iter.getPathScale()); michael@0: matrix.postTranslate(x, y); michael@0: path->reset(); michael@0: michael@0: SkScalar xpos; michael@0: const SkPath* iterPath; michael@0: while (iter.next(&iterPath, &xpos)) { michael@0: matrix.postTranslate(xpos - prevXPos, 0); michael@0: if (iterPath) { michael@0: path->addPath(*iterPath, matrix); michael@0: } michael@0: prevXPos = xpos; michael@0: } michael@0: } michael@0: michael@0: void SkPaint::getPosTextPath(const void* textData, size_t length, michael@0: const SkPoint pos[], SkPath* path) const { michael@0: SkASSERT(length == 0 || textData != NULL); michael@0: michael@0: const char* text = (const char*)textData; michael@0: if (text == NULL || length == 0 || path == NULL) { michael@0: return; michael@0: } michael@0: michael@0: SkTextToPathIter iter(text, length, *this, false); michael@0: SkMatrix matrix; michael@0: SkPoint prevPos; michael@0: prevPos.set(0, 0); michael@0: michael@0: matrix.setScale(iter.getPathScale(), iter.getPathScale()); michael@0: path->reset(); michael@0: michael@0: unsigned int i = 0; michael@0: const SkPath* iterPath; michael@0: while (iter.next(&iterPath, NULL)) { michael@0: matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY); michael@0: if (iterPath) { michael@0: path->addPath(*iterPath, matrix); michael@0: } michael@0: prevPos = pos[i]; michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: static void add_flattenable(SkDescriptor* desc, uint32_t tag, michael@0: SkWriteBuffer* buffer) { michael@0: buffer->writeToMemory(desc->addEntry(tag, buffer->bytesWritten(), NULL)); michael@0: } michael@0: michael@0: // SkFontHost can override this choice in FilterRec() michael@0: static SkMask::Format computeMaskFormat(const SkPaint& paint) { michael@0: uint32_t flags = paint.getFlags(); michael@0: michael@0: // Antialiasing being disabled trumps all other settings. michael@0: if (!(flags & SkPaint::kAntiAlias_Flag)) { michael@0: return SkMask::kBW_Format; michael@0: } michael@0: michael@0: if (flags & SkPaint::kLCDRenderText_Flag) { michael@0: return SkMask::kLCD16_Format; michael@0: } michael@0: michael@0: return SkMask::kA8_Format; michael@0: } michael@0: michael@0: // if linear-text is on, then we force hinting to be off (since that's sort of michael@0: // the point of linear-text. michael@0: static SkPaint::Hinting computeHinting(const SkPaint& paint) { michael@0: SkPaint::Hinting h = paint.getHinting(); michael@0: if (paint.isLinearText()) { michael@0: h = SkPaint::kNo_Hinting; michael@0: } michael@0: return h; michael@0: } michael@0: michael@0: // return true if the paint is just a single color (i.e. not a shader). If its michael@0: // a shader, then we can't compute a const luminance for it :( michael@0: static bool justAColor(const SkPaint& paint, SkColor* color) { michael@0: if (paint.getShader()) { michael@0: return false; michael@0: } michael@0: SkColor c = paint.getColor(); michael@0: if (paint.getColorFilter()) { michael@0: c = paint.getColorFilter()->filterColor(c); michael@0: } michael@0: if (color) { michael@0: *color = c; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static SkColor computeLuminanceColor(const SkPaint& paint) { michael@0: SkColor c; michael@0: if (!justAColor(paint, &c)) { michael@0: c = SkColorSetRGB(0x7F, 0x80, 0x7F); michael@0: } michael@0: return c; michael@0: } michael@0: michael@0: #define assert_byte(x) SkASSERT(0 == ((x) >> 8)) michael@0: michael@0: // Beyond this size, LCD doesn't appreciably improve quality, but it always michael@0: // cost more RAM and draws slower, so we set a cap. michael@0: #ifndef SK_MAX_SIZE_FOR_LCDTEXT michael@0: #define SK_MAX_SIZE_FOR_LCDTEXT 48 michael@0: #endif michael@0: michael@0: static bool tooBigForLCD(const SkScalerContext::Rec& rec) { michael@0: SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] - michael@0: rec.fPost2x2[1][0] * rec.fPost2x2[0][1]; michael@0: SkScalar size = SkScalarSqrt(SkScalarAbs(area)) * rec.fTextSize; michael@0: return size > SkIntToScalar(SK_MAX_SIZE_FOR_LCDTEXT); michael@0: } michael@0: michael@0: /* michael@0: * Return the scalar with only limited fractional precision. Used to consolidate matrices michael@0: * that vary only slightly when we create our key into the font cache, since the font scaler michael@0: * typically returns the same looking resuts for tiny changes in the matrix. michael@0: */ michael@0: static SkScalar sk_relax(SkScalar x) { michael@0: int n = sk_float_round2int(x * 1024); michael@0: return n / 1024.0f; michael@0: } michael@0: michael@0: void SkScalerContext::MakeRec(const SkPaint& paint, michael@0: const SkDeviceProperties* deviceProperties, michael@0: const SkMatrix* deviceMatrix, michael@0: Rec* rec) { michael@0: SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective()); michael@0: michael@0: SkTypeface* typeface = paint.getTypeface(); michael@0: if (NULL == typeface) { michael@0: typeface = SkTypeface::GetDefaultTypeface(); michael@0: } michael@0: rec->fOrigFontID = typeface->uniqueID(); michael@0: rec->fFontID = rec->fOrigFontID; michael@0: rec->fTextSize = paint.getTextSize(); michael@0: rec->fPreScaleX = paint.getTextScaleX(); michael@0: rec->fPreSkewX = paint.getTextSkewX(); michael@0: michael@0: if (deviceMatrix) { michael@0: rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX()); michael@0: rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX()); michael@0: rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY()); michael@0: rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY()); michael@0: } else { michael@0: rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1; michael@0: rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0; michael@0: } michael@0: michael@0: SkPaint::Style style = paint.getStyle(); michael@0: SkScalar strokeWidth = paint.getStrokeWidth(); michael@0: michael@0: unsigned flags = 0; michael@0: michael@0: if (paint.isFakeBoldText()) { michael@0: #ifdef SK_USE_FREETYPE_EMBOLDEN michael@0: flags |= SkScalerContext::kEmbolden_Flag; michael@0: #else michael@0: SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(), michael@0: kStdFakeBoldInterpKeys, michael@0: kStdFakeBoldInterpValues, michael@0: kStdFakeBoldInterpLength); michael@0: SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale); michael@0: michael@0: if (style == SkPaint::kFill_Style) { michael@0: style = SkPaint::kStrokeAndFill_Style; michael@0: strokeWidth = extra; // ignore paint's strokeWidth if it was "fill" michael@0: } else { michael@0: strokeWidth += extra; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: if (paint.isDevKernText()) { michael@0: flags |= SkScalerContext::kDevKernText_Flag; michael@0: } michael@0: michael@0: if (style != SkPaint::kFill_Style && strokeWidth > 0) { michael@0: rec->fFrameWidth = strokeWidth; michael@0: rec->fMiterLimit = paint.getStrokeMiter(); michael@0: rec->fStrokeJoin = SkToU8(paint.getStrokeJoin()); michael@0: michael@0: if (style == SkPaint::kStrokeAndFill_Style) { michael@0: flags |= SkScalerContext::kFrameAndFill_Flag; michael@0: } michael@0: } else { michael@0: rec->fFrameWidth = 0; michael@0: rec->fMiterLimit = 0; michael@0: rec->fStrokeJoin = 0; michael@0: } michael@0: michael@0: rec->fMaskFormat = SkToU8(computeMaskFormat(paint)); michael@0: michael@0: SkDeviceProperties::Geometry geometry = deviceProperties michael@0: ? deviceProperties->fGeometry michael@0: : SkDeviceProperties::Geometry::MakeDefault(); michael@0: if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) { michael@0: if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) { michael@0: // eeek, can't support LCD michael@0: rec->fMaskFormat = SkMask::kA8_Format; michael@0: } else { michael@0: if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) { michael@0: flags |= SkScalerContext::kLCD_Vertical_Flag; michael@0: } michael@0: if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) { michael@0: flags |= SkScalerContext::kLCD_BGROrder_Flag; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (paint.isEmbeddedBitmapText()) { michael@0: flags |= SkScalerContext::kEmbeddedBitmapText_Flag; michael@0: } michael@0: if (paint.isSubpixelText()) { michael@0: flags |= SkScalerContext::kSubpixelPositioning_Flag; michael@0: } michael@0: if (paint.isAutohinted()) { michael@0: flags |= SkScalerContext::kForceAutohinting_Flag; michael@0: } michael@0: if (paint.isVerticalText()) { michael@0: flags |= SkScalerContext::kVertical_Flag; michael@0: } michael@0: if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) { michael@0: flags |= SkScalerContext::kGenA8FromLCD_Flag; michael@0: } michael@0: rec->fFlags = SkToU16(flags); michael@0: michael@0: // these modify fFlags, so do them after assigning fFlags michael@0: rec->setHinting(computeHinting(paint)); michael@0: michael@0: rec->setLuminanceColor(computeLuminanceColor(paint)); michael@0: michael@0: if (NULL == deviceProperties) { michael@0: rec->setDeviceGamma(SK_GAMMA_EXPONENT); michael@0: rec->setPaintGamma(SK_GAMMA_EXPONENT); michael@0: } else { michael@0: rec->setDeviceGamma(deviceProperties->fGamma); michael@0: michael@0: //For now always set the paint gamma equal to the device gamma. michael@0: //The math in SkMaskGamma can handle them being different, michael@0: //but it requires superluminous masks when michael@0: //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large. michael@0: rec->setPaintGamma(deviceProperties->fGamma); michael@0: } michael@0: michael@0: #ifdef SK_GAMMA_CONTRAST michael@0: rec->setContrast(SK_GAMMA_CONTRAST); michael@0: #else michael@0: /** michael@0: * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise. michael@0: * With lower values small text appears washed out (though correctly so). michael@0: * With higher values lcd fringing is worse and the smoothing effect of michael@0: * partial coverage is diminished. michael@0: */ michael@0: rec->setContrast(0.5f); michael@0: #endif michael@0: michael@0: rec->fReservedAlign = 0; michael@0: michael@0: /* Allow the fonthost to modify our rec before we use it as a key into the michael@0: cache. This way if we're asking for something that they will ignore, michael@0: they can modify our rec up front, so we don't create duplicate cache michael@0: entries. michael@0: */ michael@0: typeface->onFilterRec(rec); michael@0: michael@0: // be sure to call PostMakeRec(rec) before you actually use it! michael@0: } michael@0: michael@0: /** michael@0: * In order to call cachedDeviceLuminance, cachedPaintLuminance, or michael@0: * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue michael@0: * to hold it until the returned pointer is refed or forgotten. michael@0: */ michael@0: SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex); michael@0: michael@0: static SkMaskGamma* gLinearMaskGamma = NULL; michael@0: static SkMaskGamma* gMaskGamma = NULL; michael@0: static SkScalar gContrast = SK_ScalarMin; michael@0: static SkScalar gPaintGamma = SK_ScalarMin; michael@0: static SkScalar gDeviceGamma = SK_ScalarMin; michael@0: /** michael@0: * The caller must hold the gMaskGammaCacheMutex and continue to hold it until michael@0: * the returned SkMaskGamma pointer is refed or forgotten. michael@0: */ michael@0: static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) { michael@0: if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) { michael@0: if (NULL == gLinearMaskGamma) { michael@0: gLinearMaskGamma = SkNEW(SkMaskGamma); michael@0: } michael@0: return *gLinearMaskGamma; michael@0: } michael@0: if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) { michael@0: SkSafeUnref(gMaskGamma); michael@0: gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma)); michael@0: gContrast = contrast; michael@0: gPaintGamma = paintGamma; michael@0: gDeviceGamma = deviceGamma; michael@0: } michael@0: return *gMaskGamma; michael@0: } michael@0: michael@0: /*static*/ void SkPaint::Term() { michael@0: SkAutoMutexAcquire ama(gMaskGammaCacheMutex); michael@0: michael@0: SkSafeUnref(gLinearMaskGamma); michael@0: gLinearMaskGamma = NULL; michael@0: SkSafeUnref(gMaskGamma); michael@0: gMaskGamma = NULL; michael@0: SkDEBUGCODE(gContrast = SK_ScalarMin;) michael@0: SkDEBUGCODE(gPaintGamma = SK_ScalarMin;) michael@0: SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;) michael@0: } michael@0: michael@0: /** michael@0: * We ensure that the rec is self-consistent and efficient (where possible) michael@0: */ michael@0: void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) { michael@0: /** michael@0: * If we're asking for A8, we force the colorlum to be gray, since that michael@0: * limits the number of unique entries, and the scaler will only look at michael@0: * the lum of one of them. michael@0: */ michael@0: switch (rec->fMaskFormat) { michael@0: case SkMask::kLCD16_Format: michael@0: case SkMask::kLCD32_Format: { michael@0: // filter down the luminance color to a finite number of bits michael@0: SkColor color = rec->getLuminanceColor(); michael@0: rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color)); michael@0: break; michael@0: } michael@0: case SkMask::kA8_Format: { michael@0: // filter down the luminance to a single component, since A8 can't michael@0: // use per-component information michael@0: michael@0: SkColor color = rec->getLuminanceColor(); michael@0: U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color); michael@0: //If we are asked to look like LCD, look like LCD. michael@0: if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) { michael@0: // HACK: Prevents green from being pre-blended as white. michael@0: lum -= ((255 - lum) * lum) / 255; michael@0: } michael@0: michael@0: // reduce to our finite number of bits michael@0: color = SkColorSetRGB(lum, lum, lum); michael@0: rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color)); michael@0: break; michael@0: } michael@0: case SkMask::kBW_Format: michael@0: // No need to differentiate gamma if we're BW michael@0: rec->ignorePreBlend(); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: #define MIN_SIZE_FOR_EFFECT_BUFFER 1024 michael@0: michael@0: #ifdef SK_DEBUG michael@0: #define TEST_DESC michael@0: #endif michael@0: michael@0: /* michael@0: * ignoreGamma tells us that the caller just wants metrics that are unaffected michael@0: * by gamma correction, so we jam the luminance field to 0 (most common value michael@0: * for black text) in hopes that we get a cache hit easier. A better solution michael@0: * would be for the fontcache lookup to know to ignore the luminance field michael@0: * entirely, but not sure how to do that and keep it fast. michael@0: */ michael@0: void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties, michael@0: const SkMatrix* deviceMatrix, michael@0: void (*proc)(SkTypeface*, const SkDescriptor*, void*), michael@0: void* context, bool ignoreGamma) const { michael@0: SkScalerContext::Rec rec; michael@0: michael@0: SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec); michael@0: if (ignoreGamma) { michael@0: rec.setLuminanceColor(0); michael@0: } michael@0: michael@0: size_t descSize = sizeof(rec); michael@0: int entryCount = 1; michael@0: SkPathEffect* pe = this->getPathEffect(); michael@0: SkMaskFilter* mf = this->getMaskFilter(); michael@0: SkRasterizer* ra = this->getRasterizer(); michael@0: michael@0: SkWriteBuffer peBuffer, mfBuffer, raBuffer; michael@0: michael@0: if (pe) { michael@0: peBuffer.writeFlattenable(pe); michael@0: descSize += peBuffer.bytesWritten(); michael@0: entryCount += 1; michael@0: rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion michael@0: // seems like we could support kLCD as well at this point... michael@0: } michael@0: if (mf) { michael@0: mfBuffer.writeFlattenable(mf); michael@0: descSize += mfBuffer.bytesWritten(); michael@0: entryCount += 1; michael@0: rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters michael@0: /* Pre-blend is not currently applied to filtered text. michael@0: The primary filter is blur, for which contrast makes no sense, michael@0: and for which the destination guess error is more visible. michael@0: Also, all existing users of blur have calibrated for linear. */ michael@0: rec.ignorePreBlend(); michael@0: } michael@0: if (ra) { michael@0: raBuffer.writeFlattenable(ra); michael@0: descSize += raBuffer.bytesWritten(); michael@0: entryCount += 1; michael@0: rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion michael@0: } michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: SkWriteBuffer androidBuffer; michael@0: fPaintOptionsAndroid.flatten(androidBuffer); michael@0: descSize += androidBuffer.bytesWritten(); michael@0: entryCount += 1; michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Now that we're done tweaking the rec, call the PostMakeRec cleanup michael@0: SkScalerContext::PostMakeRec(*this, &rec); michael@0: michael@0: descSize += SkDescriptor::ComputeOverhead(entryCount); michael@0: michael@0: SkAutoDescriptor ad(descSize); michael@0: SkDescriptor* desc = ad.getDesc(); michael@0: michael@0: desc->init(); michael@0: desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: add_flattenable(desc, kAndroidOpts_SkDescriptorTag, &androidBuffer); michael@0: #endif michael@0: michael@0: if (pe) { michael@0: add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer); michael@0: } michael@0: if (mf) { michael@0: add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer); michael@0: } michael@0: if (ra) { michael@0: add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer); michael@0: } michael@0: michael@0: SkASSERT(descSize == desc->getLength()); michael@0: desc->computeChecksum(); michael@0: michael@0: #ifdef TEST_DESC michael@0: { michael@0: // Check that we completely write the bytes in desc (our key), and that michael@0: // there are no uninitialized bytes. If there were, then we would get michael@0: // false-misses (or worse, false-hits) in our fontcache. michael@0: // michael@0: // We do this buy filling 2 others, one with 0s and the other with 1s michael@0: // and create those, and then check that all 3 are identical. michael@0: SkAutoDescriptor ad1(descSize); michael@0: SkAutoDescriptor ad2(descSize); michael@0: SkDescriptor* desc1 = ad1.getDesc(); michael@0: SkDescriptor* desc2 = ad2.getDesc(); michael@0: michael@0: memset(desc1, 0x00, descSize); michael@0: memset(desc2, 0xFF, descSize); michael@0: michael@0: desc1->init(); michael@0: desc2->init(); michael@0: desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); michael@0: desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: add_flattenable(desc1, kAndroidOpts_SkDescriptorTag, &androidBuffer); michael@0: add_flattenable(desc2, kAndroidOpts_SkDescriptorTag, &androidBuffer); michael@0: #endif michael@0: michael@0: if (pe) { michael@0: add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer); michael@0: add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer); michael@0: } michael@0: if (mf) { michael@0: add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer); michael@0: add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer); michael@0: } michael@0: if (ra) { michael@0: add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer); michael@0: add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer); michael@0: } michael@0: michael@0: SkASSERT(descSize == desc1->getLength()); michael@0: SkASSERT(descSize == desc2->getLength()); michael@0: desc1->computeChecksum(); michael@0: desc2->computeChecksum(); michael@0: SkASSERT(!memcmp(desc, desc1, descSize)); michael@0: SkASSERT(!memcmp(desc, desc2, descSize)); michael@0: } michael@0: #endif michael@0: michael@0: proc(fTypeface, desc, context); michael@0: } michael@0: michael@0: SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties, michael@0: const SkMatrix* deviceMatrix) const { michael@0: SkGlyphCache* cache; michael@0: this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false); michael@0: return cache; michael@0: } michael@0: michael@0: /** michael@0: * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend. michael@0: */ michael@0: //static michael@0: SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) { michael@0: SkAutoMutexAcquire ama(gMaskGammaCacheMutex); michael@0: const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(), michael@0: rec.getPaintGamma(), michael@0: rec.getDeviceGamma()); michael@0: return maskGamma.preBlend(rec.getLuminanceColor()); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkStream.h" michael@0: michael@0: static uintptr_t asint(const void* p) { michael@0: return reinterpret_cast(p); michael@0: } michael@0: michael@0: union Scalar32 { michael@0: SkScalar fScalar; michael@0: uint32_t f32; michael@0: }; michael@0: michael@0: static uint32_t* write_scalar(uint32_t* ptr, SkScalar value) { michael@0: SkASSERT(sizeof(SkScalar) == sizeof(uint32_t)); michael@0: Scalar32 tmp; michael@0: tmp.fScalar = value; michael@0: *ptr = tmp.f32; michael@0: return ptr + 1; michael@0: } michael@0: michael@0: static SkScalar read_scalar(const uint32_t*& ptr) { michael@0: SkASSERT(sizeof(SkScalar) == sizeof(uint32_t)); michael@0: Scalar32 tmp; michael@0: tmp.f32 = *ptr++; michael@0: return tmp.fScalar; michael@0: } michael@0: michael@0: static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) { michael@0: SkASSERT(a == (uint8_t)a); michael@0: SkASSERT(b == (uint8_t)b); michael@0: SkASSERT(c == (uint8_t)c); michael@0: SkASSERT(d == (uint8_t)d); michael@0: return (a << 24) | (b << 16) | (c << 8) | d; michael@0: } michael@0: michael@0: enum FlatFlags { michael@0: kHasTypeface_FlatFlag = 0x01, michael@0: kHasEffects_FlatFlag = 0x02, michael@0: kHasNonDefaultPaintOptionsAndroid_FlatFlag = 0x04, michael@0: }; michael@0: michael@0: // The size of a flat paint's POD fields michael@0: static const uint32_t kPODPaintSize = 5 * sizeof(SkScalar) + michael@0: 1 * sizeof(SkColor) + michael@0: 1 * sizeof(uint16_t) + michael@0: 6 * sizeof(uint8_t); michael@0: michael@0: /* To save space/time, we analyze the paint, and write a truncated version of michael@0: it if there are not tricky elements like shaders, etc. michael@0: */ michael@0: void SkPaint::flatten(SkWriteBuffer& buffer) const { michael@0: uint8_t flatFlags = 0; michael@0: if (this->getTypeface()) { michael@0: flatFlags |= kHasTypeface_FlatFlag; michael@0: } michael@0: if (asint(this->getPathEffect()) | michael@0: asint(this->getShader()) | michael@0: asint(this->getXfermode()) | michael@0: asint(this->getMaskFilter()) | michael@0: asint(this->getColorFilter()) | michael@0: asint(this->getRasterizer()) | michael@0: asint(this->getLooper()) | michael@0: asint(this->getAnnotation()) | michael@0: asint(this->getImageFilter())) { michael@0: flatFlags |= kHasEffects_FlatFlag; michael@0: } michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: if (this->getPaintOptionsAndroid() != SkPaintOptionsAndroid()) { michael@0: flatFlags |= kHasNonDefaultPaintOptionsAndroid_FlatFlag; michael@0: } michael@0: #endif michael@0: michael@0: SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize); michael@0: uint32_t* ptr = buffer.reserve(kPODPaintSize); michael@0: michael@0: ptr = write_scalar(ptr, this->getTextSize()); michael@0: ptr = write_scalar(ptr, this->getTextScaleX()); michael@0: ptr = write_scalar(ptr, this->getTextSkewX()); michael@0: ptr = write_scalar(ptr, this->getStrokeWidth()); michael@0: ptr = write_scalar(ptr, this->getStrokeMiter()); michael@0: *ptr++ = this->getColor(); michael@0: // previously flags:16, textAlign:8, flatFlags:8 michael@0: // now flags:16, hinting:4, textAlign:4, flatFlags:8 michael@0: *ptr++ = (this->getFlags() << 16) | michael@0: // hinting added later. 0 in this nibble means use the default. michael@0: ((this->getHinting()+1) << 12) | michael@0: (this->getTextAlign() << 8) | michael@0: flatFlags; michael@0: *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(), michael@0: this->getStyle(), this->getTextEncoding()); michael@0: michael@0: // now we're done with ptr and the (pre)reserved space. If we need to write michael@0: // additional fields, use the buffer directly michael@0: if (flatFlags & kHasTypeface_FlatFlag) { michael@0: buffer.writeTypeface(this->getTypeface()); michael@0: } michael@0: if (flatFlags & kHasEffects_FlatFlag) { michael@0: buffer.writeFlattenable(this->getPathEffect()); michael@0: buffer.writeFlattenable(this->getShader()); michael@0: buffer.writeFlattenable(this->getXfermode()); michael@0: buffer.writeFlattenable(this->getMaskFilter()); michael@0: buffer.writeFlattenable(this->getColorFilter()); michael@0: buffer.writeFlattenable(this->getRasterizer()); michael@0: buffer.writeFlattenable(this->getLooper()); michael@0: buffer.writeFlattenable(this->getImageFilter()); michael@0: michael@0: if (fAnnotation) { michael@0: buffer.writeBool(true); michael@0: fAnnotation->writeToBuffer(buffer); michael@0: } else { michael@0: buffer.writeBool(false); michael@0: } michael@0: } michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: if (flatFlags & kHasNonDefaultPaintOptionsAndroid_FlatFlag) { michael@0: this->getPaintOptionsAndroid().flatten(buffer); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void SkPaint::unflatten(SkReadBuffer& buffer) { michael@0: uint8_t flatFlags = 0; michael@0: SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize); michael@0: const void* podData = buffer.skip(kPODPaintSize); michael@0: const uint32_t* pod = reinterpret_cast(podData); michael@0: michael@0: // the order we read must match the order we wrote in flatten() michael@0: this->setTextSize(read_scalar(pod)); michael@0: this->setTextScaleX(read_scalar(pod)); michael@0: this->setTextSkewX(read_scalar(pod)); michael@0: this->setStrokeWidth(read_scalar(pod)); michael@0: this->setStrokeMiter(read_scalar(pod)); michael@0: this->setColor(*pod++); michael@0: michael@0: // previously flags:16, textAlign:8, flatFlags:8 michael@0: // now flags:16, hinting:4, textAlign:4, flatFlags:8 michael@0: uint32_t tmp = *pod++; michael@0: this->setFlags(tmp >> 16); michael@0: michael@0: // hinting added later. 0 in this nibble means use the default. michael@0: uint32_t hinting = (tmp >> 12) & 0xF; michael@0: this->setHinting(0 == hinting ? kNormal_Hinting : static_cast(hinting-1)); michael@0: michael@0: this->setTextAlign(static_cast((tmp >> 8) & 0xF)); michael@0: michael@0: flatFlags = tmp & 0xFF; michael@0: michael@0: tmp = *pod++; michael@0: this->setStrokeCap(static_cast((tmp >> 24) & 0xFF)); michael@0: this->setStrokeJoin(static_cast((tmp >> 16) & 0xFF)); michael@0: this->setStyle(static_cast