版權聲明:本文爲博主原創文章,轉載請註明出處:http://blog.csdn.net/jxt1234and2010
文字繪製主要包括編碼轉換(主要是中文)、字形解析(點線或image)和實際渲染三個步驟。在這個過程中,字形解析和實際渲染均是耗時步驟。Skia對文字解析的結果做了一套緩存機制。在中文字較多,使用多種字體,繪製的樣式(粗/斜體)有變化時,這個緩存會變得很大,因此Skia文字緩存做了內存上的限制。
1、SkPaint
文字繪製與SkPaint的屬性相關很大,先回頭看下SkPaint相關的屬性
- class SkPaint
- {
- private
- SkTypeface* fTypeface;//字體
- SkPathEffect* fPathEffect;//路徑繪製效果
- SkShader* fShader;//取色器
- SkXfermode* fXfermode;//混合模式,類似OpenGL裏面的Blend設置
- SkColorFilter* fColorFilter;//圖像繪製時,自定義圖像採樣函數時使用
- SkMaskFilter* fMaskFilter;//路徑繪製時,按有無像素做進一步自定義改進處理時使用
- SkRasterizer* fRasterizer;//路徑繪製時自定義生成像素點的算法時使用
- SkDrawLooper* fLooper;//循環繪製,SkCanvas裏面的第二重循環,一般不用關注
- SkImageFilter* fImageFilter;//SkCanvas的第一重循環,繪製後做後處理用,一般不用關注
- SkAnnotation* fAnnotation;//暫時沒用到的屬性
- SkScalar fTextSize;//文字大小
- SkScalar fTextScaleX;//文字水平方向上的拉伸,僅用於PDF繪製
- SkScalar fTextSkewX;//文字橫向扭曲度,僅用於PDF繪製
- SkColor fColor;//純色,在fShader爲空時使用
- SkScalar fWidth;//帶邊界時(kStroke_Style/kStrokeAndFill_Style)生效,邊界的寬度
- SkScalar fMiterLimit;//drawPath時,連接各個path片斷時,要求的圓滑連接閾值,Join 類型爲默認的kMiter_Join時無效
- /*一組不超過32位的屬性*/
- union {
- struct {
- // all of these bitfields should add up to 32
- unsigned fFlags : 16;//包含所有的0/1二值屬性:
- /*
- kAntiAlias_Flag = 0x01,//是否抗鋸齒
- kDither_Flag = 0x04,//是否做抖動處理
- kUnderlineText_Flag = 0x08,//是否繪製文字下劃線
- kStrikeThruText_Flag = 0x10,//目前未看到其作用
- kFakeBoldText_Flag = 0x20,
- kLinearText_Flag = 0x40,
- kSubpixelText_Flag = 0x80,//文字像素精確採樣
- kDevKernText_Flag = 0x100
- kLCDRenderText_Flag = 0x200
- kEmbeddedBitmapText_Flag = 0x400,
- kAutoHinting_Flag = 0x800,
- kVerticalText_Flag = 0x1000,//是否豎向繪製文字
- kGenA8FromLCD_Flag = 0x2000,
- kDistanceFieldTextTEMP_Flag = 0x4000,
- kAllFlags = 0xFFFF
- */
- unsigned fTextAlign : 2;//文字對齊方式,取值如下:
- /*
- enum Align {
- kLeft_Align,//左對齊
- kCenter_Align,//居中
- kRight_Align,//右對齊
- };
- */
- unsigned fCapType : 2;//邊界連接類型,分無連接,圓角連接,半方形連接
- unsigned fJoinType : 2;//Path片斷連接類型
- unsigned fStyle : 2;//繪製模式,填充邊界/區域
- /*
- enum Style {
- kFill_Style, //填充區域
- kStroke_Style,//繪製邊界
- kStrokeAndFill_Style,//填充區域並繪製邊界
- };
- */
- unsigned fTextEncoding : 2;//文字編碼格式,支持如下幾種
- enum TextEncoding {
- kUTF8_TextEncoding,//utf-8,默認格式
- kUTF16_TextEncoding,
- kUTF32_TextEncoding,
- kGlyphID_TextEncoding
- };
- unsigned fHinting : 2;
- unsigned fFilterLevel : 2;//在圖像繪製時提到的採樣質量要求
- //unsigned fFreeBits : 2;
- };
- uint32_t fBitfields;
- };
- uint32_t fDirtyBits;//記錄哪些屬性被改變了,以便更新相關的緩存
- };
2、字體繪製基本流程
繪製文字和下劃線
SkDraw
兩種繪製方式:
(1)將文字解析爲路徑,然後繪製路徑,緩存路徑(drawText_asPaths)。
- void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
- SkScalar x, SkScalar y,
- const SkPaint& paint) const {
- SkDEBUGCODE(this->validate();)
- SkTextToPathIter iter(text, byteLength, paint, true);
- SkMatrix matrix;
- matrix.setScale(iter.getPathScale(), iter.getPathScale());
- matrix.postTranslate(x, y);
- const SkPath* iterPath;
- SkScalar xpos, prevXPos = 0;
- while (iter.next(&iterPath, &xpos)) {
- matrix.postTranslate(xpos - prevXPos, 0);
- if (iterPath) {
- const SkPaint& pnt = iter.getPaint();
- if (fDevice) {
- fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
- } else {
- this->drawPath(*iterPath, pnt, &matrix, false);
- }
- }
- prevXPos = xpos;
- }
- }
(2)將文字解析爲Mask(32*32的A8圖片),然後繪製模板,緩存模板。
- SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
- SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
- SkGlyphCache* cache = autoCache.getCache();
- // transform our starting point
- {
- SkPoint loc;
- fMatrix->mapXY(x, y, &loc);
- x = loc.fX;
- y = loc.fY;
- }
- // need to measure first
- if (paint.getTextAlign() != SkPaint::kLeft_Align) {
- SkVector stop;
- measure_text(cache, glyphCacheProc, text, byteLength, &stop);
- SkScalar stopX = stop.fX;
- SkScalar stopY = stop.fY;
- if (paint.getTextAlign() == SkPaint::kCenter_Align) {
- stopX = SkScalarHalf(stopX);
- stopY = SkScalarHalf(stopY);
- }
- x -= stopX;
- y -= stopY;
- }
- const char* stop = text + byteLength;
- SkAAClipBlitter aaBlitter;
- SkAutoBlitterChoose blitterChooser;
- SkBlitter* blitter = NULL;
- if (needsRasterTextBlit(*this)) {
- blitterChooser.choose(*fBitmap, *fMatrix, paint);
- blitter = blitterChooser.get();
- if (fRC->isAA()) {
- aaBlitter.init(blitter, &fRC->aaRgn());
- blitter = &aaBlitter;
- }
- }
- SkAutoKern autokern;
- SkDraw1Glyph d1g;
- SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
- SkFixed fxMask = ~0;
- SkFixed fyMask = ~0;
- if (cache->isSubpixel()) {
- SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
- if (kX_SkAxisAlignment == baseline) {
- fyMask = 0;
- d1g.fHalfSampleY = SK_FixedHalf;
- } else if (kY_SkAxisAlignment == baseline) {
- fxMask = 0;
- d1g.fHalfSampleX = SK_FixedHalf;
- }
- }
- SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
- SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
- while (text < stop) {
- const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
- fx += autokern.adjust(glyph);
- if (glyph.fWidth) {
- proc(d1g, fx, fy, glyph);
- }
- fx += glyph.fAdvanceX;
- fy += glyph.fAdvanceY;
- }
cacheProc是翻譯字符編碼的函數,由SkPaint::getDrawCacheProc產生:
- SkDrawCacheProc SkPaint::getDrawCacheProc() const {
- static const SkDrawCacheProc gDrawCacheProcs[] = {
- sk_getMetrics_utf8_00,
- sk_getMetrics_utf16_00,
- sk_getMetrics_utf32_00,
- sk_getMetrics_glyph_00,
- sk_getMetrics_utf8_xy,
- sk_getMetrics_utf16_xy,
- sk_getMetrics_utf32_xy,
- sk_getMetrics_glyph_xy
- };
- unsigned index = this->getTextEncoding();
- if (fFlags & kSubpixelText_Flag) {
- index += 4;
- }
- SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
- return gDrawCacheProcs[index];
- }
SkGlyphCache:
字形解析的結果緩存。
SkScalerContext:
負責字形的解析,有多種實現。Android中是用FreeType:SkScalerContext_FreeType。主要是generateImage和generatePath兩個方法:
generateImage:
- void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
- SkAutoMutexAcquire ac(gFTMutex);
- FT_Error err;
- if (this->setupSize()) {
- goto ERROR;
- }
- err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);
- if (err != 0) {
- SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
- glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
- ERROR:
- memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
- return;
- }
- emboldenIfNeeded(fFace, fFace->glyph);
- generateGlyphImage(fFace, glyph);
- }
- void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) {
- const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
- const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
- switch ( face->glyph->format ) {
- case FT_GLYPH_FORMAT_OUTLINE: {
- FT_Outline* outline = &face->glyph->outline;
- FT_BBox bbox;
- FT_Bitmap target;
- int dx = 0, dy = 0;
- if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
- dx = SkFixedToFDot6(glyph.getSubXFixed());
- dy = SkFixedToFDot6(glyph.getSubYFixed());
- // negate dy since freetype-y-goes-up and skia-y-goes-down
- dy = -dy;
- }
- FT_Outline_Get_CBox(outline, &bbox);
- /*
- what we really want to do for subpixel is
- offset(dx, dy)
- compute_bounds
- offset(bbox & !63)
- but that is two calls to offset, so we do the following, which
- achieves the same thing with only one offset call.
- */
- FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
- dy - ((bbox.yMin + dy) & ~63));
- if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
- FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
- SkMask mask;
- glyph.toMask(&mask);
- if (fPreBlend.isApplicable()) {
- copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- } else {
- copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- }
- } else {
- target.width = glyph.fWidth;
- target.rows = glyph.fHeight;
- target.pitch = glyph.rowBytes();
- target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
- target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);
- target.num_grays = 256;
- memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
- FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
- }
- } break;
- case FT_GLYPH_FORMAT_BITMAP: {
- FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
- SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
- // Assume that the other formats do not exist.
- SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
- FT_PIXEL_MODE_GRAY == pixel_mode ||
- FT_PIXEL_MODE_BGRA == pixel_mode);
- // These are the only formats this ScalerContext should request.
- SkASSERT(SkMask::kBW_Format == maskFormat ||
- SkMask::kA8_Format == maskFormat ||
- SkMask::kARGB32_Format == maskFormat ||
- SkMask::kLCD16_Format == maskFormat);
- if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
- !(face->style_flags & FT_STYLE_FLAG_BOLD))
- {
- FT_GlyphSlot_Own_Bitmap(face->glyph);
- FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap,
- kBitmapEmboldenStrength, 0);
- }
- // If no scaling needed, directly copy glyph bitmap.
- if (glyph.fWidth == face->glyph->bitmap.width &&
- glyph.fHeight == face->glyph->bitmap.rows &&
- glyph.fTop == -face->glyph->bitmap_top &&
- glyph.fLeft == face->glyph->bitmap_left)
- {
- SkMask dstMask;
- glyph.toMask(&dstMask);
- copyFTBitmap(face->glyph->bitmap, dstMask);
- break;
- }
- // Otherwise, scale the bitmap.
- // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
- SkBitmap unscaledBitmap;
- unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width,
- face->glyph->bitmap.rows,
- SkColorType_for_FTPixelMode(pixel_mode),
- kPremul_SkAlphaType));
- SkMask unscaledBitmapAlias;
- unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());
- unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height());
- unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();
- unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType());
- copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);
- // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
- // BW requires an A8 target for resizing, which can then be down sampled.
- // LCD should use a 4x A8 target, which will then be down sampled.
- // For simplicity, LCD uses A8 and is replicated.
- int bitmapRowBytes = 0;
- if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {
- bitmapRowBytes = glyph.rowBytes();
- }
- SkBitmap dstBitmap;
- dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,
- SkColorType_for_SkMaskFormat(maskFormat),
- kPremul_SkAlphaType),
- bitmapRowBytes);
- if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {
- dstBitmap.allocPixels();
- } else {
- dstBitmap.setPixels(glyph.fImage);
- }
- // Scale unscaledBitmap into dstBitmap.
- SkCanvas canvas(dstBitmap);
- canvas.clear(SK_ColorTRANSPARENT);
- canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width),
- SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows));
- SkPaint paint;
- paint.setFilterLevel(SkPaint::kMedium_FilterLevel);
- canvas.drawBitmap(unscaledBitmap, 0, 0, &paint);
- // If the destination is BW or LCD, convert from A8.
- if (SkMask::kBW_Format == maskFormat) {
- // Copy the A8 dstBitmap into the A1 glyph.fImage.
- SkMask dstMask;
- glyph.toMask(&dstMask);
- packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());
- } else if (SkMask::kLCD16_Format == maskFormat) {
- // Copy the A8 dstBitmap into the LCD16 glyph.fImage.
- uint8_t* src = dstBitmap.getAddr8(0, 0);
- uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
- for (int y = dstBitmap.height(); y --> 0;) {
- for (int x = 0; x < dstBitmap.width(); ++x) {
- dst[x] = grayToRGB16(src[x]);
- }
- dst = (uint16_t*)((char*)dst + glyph.rowBytes());
- src += dstBitmap.rowBytes();
- }
- }
- } break;
- default:
- SkDEBUGFAIL("unknown glyph format");
- memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
- return;
- }
- // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
- // it is optional
- #if defined(SK_GAMMA_APPLY_TO_A8)
- if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) {
- uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
- unsigned rowBytes = glyph.rowBytes();
- for (int y = glyph.fHeight - 1; y >= 0; --y) {
- for (int x = glyph.fWidth - 1; x >= 0; --x) {
- dst[x] = fPreBlend.fG[dst[x]];
- }
- dst += rowBytes;
- }
- }
- #endif
- }
- void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
- SkPath* path) {
- SkAutoMutexAcquire ac(gFTMutex);
- SkASSERT(&glyph && path);
- if (this->setupSize()) {
- path->reset();
- return;
- }
- uint32_t flags = fLoadGlyphFlags;
- flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
- flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
- FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);
- if (err != 0) {
- SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
- glyph.getGlyphID(fBaseGlyphCount), flags, err));
- path->reset();
- return;
- }
- emboldenIfNeeded(fFace, fFace->glyph);
- generateGlyphPath(fFace, path);
- // The path's origin from FreeType is always the horizontal layout origin.
- // Offset the path so that it is relative to the vertical origin if needed.
- if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
- FT_Vector vector;
- vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
- vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
- FT_Vector_Transform(&vector, &fMatrix22);
- path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y));
- }
- }
3、字體緩存管理
SkTypeface是Skia中的字體類,對應可有多種字體庫解析實現。
由於Android上面使用的是FreeType,因此也只講FreeType分支。
FreeType的使用方法可參考:http://blog.csdn.net/furtherchan/article/details/8667884
字體建立的代碼如下:
- SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {
- return SkFontHost::CreateTypefaceFromStream(stream);
- }
- bool find_name_and_attributes(SkStream* stream, SkString* name,
- SkTypeface::Style* style, bool* isFixedPitch) {
- FT_Library library;
- if (FT_Init_FreeType(&library)) {
- return false;
- }
- FT_Open_Args args;
- memset(&args, 0, sizeof(args));
- const void* memoryBase = stream->getMemoryBase();
- FT_StreamRec streamRec;
- if (NULL != memoryBase) {
- args.flags = FT_OPEN_MEMORY;
- args.memory_base = (const FT_Byte*)memoryBase;
- args.memory_size = stream->getLength();
- } else {
- memset(&streamRec, 0, sizeof(streamRec));
- streamRec.size = stream->getLength();
- streamRec.descriptor.pointer = stream;
- streamRec.read = sk_stream_read;
- streamRec.close = sk_stream_close;
- args.flags = FT_OPEN_STREAM;
- args.stream = &streamRec;
- }
- FT_Face face;
- if (FT_Open_Face(library, &args, 0, &face)) {
- FT_Done_FreeType(library);
- return false;
- }
- int tempStyle = SkTypeface::kNormal;
- if (face->style_flags & FT_STYLE_FLAG_BOLD) {
- tempStyle |= SkTypeface::kBold;
- }
- if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
- tempStyle |= SkTypeface::kItalic;
- }
- if (name) {
- name->set(face->family_name);
- }
- if (style) {
- *style = (SkTypeface::Style) tempStyle;
- }
- if (isFixedPitch) {
- *isFixedPitch = FT_IS_FIXED_WIDTH(face);
- }
- FT_Done_Face(face);
- FT_Done_FreeType(library);
- return true;
- }
對於Android,在系統初始化時,所有字體文件在預加載時即被解析,包裝爲SkFaceRec,存爲一個全局鏈表。(frameworks/base/graphic 和 frameworks/base/core/jni目錄下面的代碼)
- public class Typeface {
- /*
- .......
- */
- private static void init() {
- // Load font config and initialize Minikin state
- File systemFontConfigLocation = getSystemFontConfigLocation();
- File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
- try {
- FileInputStream fontsIn = new FileInputStream(configFilename);
- FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
- List<FontFamily> familyList = new ArrayList<FontFamily>();
- // Note that the default typeface is always present in the fallback list;
- // this is an enhancement from pre-Minikin behavior.
- for (int i = 0; i < fontConfig.families.size(); i++) {
- Family f = fontConfig.families.get(i);
- if (i == 0 || f.name == null) {
- familyList.add(makeFamilyFromParsed(f));
- }
- }
- sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
- setDefault(Typeface.createFromFamilies(sFallbackFonts));
- Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
- for (int i = 0; i < fontConfig.families.size(); i++) {
- Typeface typeface;
- Family f = fontConfig.families.get(i);
- if (f.name != null) {
- if (i == 0) {
- // The first entry is the default typeface; no sense in
- // duplicating the corresponding FontFamily.
- typeface = sDefaultTypeface;
- } else {
- FontFamily fontFamily = makeFamilyFromParsed(f);
- FontFamily[] families = { fontFamily };
- typeface = Typeface.createFromFamiliesWithDefault(families);
- }
- systemFonts.put(f.name, typeface);
- }
- }
- for (FontListParser.Alias alias : fontConfig.aliases) {
- Typeface base = systemFonts.get(alias.toName);
- Typeface newFace = base;
- int weight = alias.weight;
- if (weight != 400) {
- newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
- }
- systemFonts.put(alias.name, newFace);
- }
- sSystemFontMap = systemFonts;
- } catch (RuntimeException e) {
- Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
- // TODO: normal in non-Minikin case, remove or make error when Minikin-only
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Error opening " + configFilename);
- } catch (IOException e) {
- Log.e(TAG, "Error reading " + configFilename);
- } catch (XmlPullParserException e) {
- Log.e(TAG, "XML parse exception for " + configFilename);
- }
- }
- static {
- init();
- // Set up defaults and typefaces exposed in public API
- DEFAULT = create((String) null, 0);
- DEFAULT_BOLD = create((String) null, Typeface.BOLD);
- SANS_SERIF = create("sans-serif", 0);
- SERIF = create("serif", 0);
- MONOSPACE = create("monospace", 0);
- sDefaults = new Typeface[] {
- DEFAULT,
- DEFAULT_BOLD,
- create((String) null, Typeface.ITALIC),
- create((String) null, Typeface.BOLD_ITALIC),
- };
- }
- /*
- ......
- */
- }
SkTypeface 記錄一個字體的id,在使用時,到鏈表中查出相關的字體。
對一個字體和樣式,建一個 SkGlyphCache緩存,內含一個 SkScalerContext 和一個 SkGlyph 的哈希表,SkGlyph 緩存一個字體中一個字解析出來的位圖。此有內存容量限制,當超過容量時,會清除之前緩存的位圖。Hash衝突時,直接生成新字形替換原來的字形。
緩存限制的內存宏詳見:src/core/SkGlyphCache_Globals.h。和include/core/SkUserConfig.h中的SK_DEFAULT_FONT_CACHE_LIMIT宏
- struct SkGlyph {
- void* fImage;
- SkPath* fPath;
- SkFixed fAdvanceX, fAdvanceY;
- uint32_t fID;
- uint16_t fWidth, fHeight;
- int16_t fTop, fLeft;
- void* fDistanceField;
- uint8_t fMaskFormat;
- int8_t fRsbDelta, fLsbDelta; // used by auto-kerning
- };
當繪製字體只繪邊界或者位圖緩存機制不好處理時,將字體解析成點線,構成SkPath,也做緩存。