Skia深入分析5——skia文字绘制的实现

文字绘制主要包括编码转换(主要是中文)、字形解析(点线或image)和实际渲染三个步骤。在这个过程中,字形解析和实际渲染均是耗时步骤。Skia对文字解析的结果做了一套缓存机制。在中文字较多,使用多种字体,绘制的样式(粗/斜体)有变化时,这个缓存会变得很大,因此Skia文字缓存做了内存上的限制。

1、SkPaint

文字绘制与SkPaint的属性相关很大,先回头看下SkPaint相关的属性

[cpp]  view plain  copy
  1. class SkPaint  
  2. {  
  3. private  
  4.     SkTypeface*     fTypeface;//字体  
  5.     SkPathEffect*   fPathEffect;//路径绘制效果  
  6.     SkShader*       fShader;//取色器  
  7.     SkXfermode*     fXfermode;//混合模式,类似OpenGL里面的Blend设置  
  8.     SkColorFilter*  fColorFilter;//图像绘制时,自定义图像采样函数时使用  
  9.     SkMaskFilter*   fMaskFilter;//路径绘制时,按有无像素做进一步自定义改进处理时使用  
  10.     SkRasterizer*   fRasterizer;//路径绘制时自定义生成像素点的算法时使用  
  11.     SkDrawLooper*   fLooper;//循环绘制,SkCanvas里面的第二重循环,一般不用关注  
  12.     SkImageFilter*  fImageFilter;//SkCanvas的第一重循环,绘制后做后处理用,一般不用关注  
  13.     SkAnnotation*   fAnnotation;//暂时没用到的属性  
  14.   
  15.     SkScalar        fTextSize;//文字大小  
  16.   
  17.     SkScalar        fTextScaleX;//文字水平方向上的拉伸,仅用于PDF绘制  
  18.     SkScalar        fTextSkewX;//文字横向扭曲度,仅用于PDF绘制  
  19.   
  20.     SkColor         fColor;//纯色,在fShader为空时使用  
  21.     SkScalar        fWidth;//带边界时(kStroke_Style/kStrokeAndFill_Style)生效,边界的宽度   
  22.     SkScalar        fMiterLimit;//drawPath时,连接各个path片断时,要求的圆滑连接阈值,Join 类型为默认的kMiter_Join时无效  
  23.     /*一组不超过32位的属性*/  
  24.     union {  
  25.         struct {  
  26.             // all of these bitfields should add up to 32  
  27.             unsigned        fFlags : 16;//包含所有的0/1二值属性:  
  28.             /* 
  29.                kAntiAlias_Flag       = 0x01,//是否抗锯齿 
  30.                kDither_Flag          = 0x04,//是否做抖动处理 
  31.                kUnderlineText_Flag   = 0x08,//是否绘制文字下划线 
  32.                kStrikeThruText_Flag  = 0x10,//目前未看到其作用 
  33.                kFakeBoldText_Flag    = 0x20, 
  34.                kLinearText_Flag      = 0x40, 
  35.                kSubpixelText_Flag    = 0x80,//文字像素精确采样 
  36.                kDevKernText_Flag     = 0x100 
  37.                kLCDRenderText_Flag   = 0x200 
  38.                kEmbeddedBitmapText_Flag = 0x400, 
  39.                kAutoHinting_Flag     = 0x800, 
  40.                kVerticalText_Flag    = 0x1000,//是否竖向绘制文字 
  41.                kGenA8FromLCD_Flag    = 0x2000, 
  42.                kDistanceFieldTextTEMP_Flag = 0x4000, 
  43.                kAllFlags = 0xFFFF 
  44.              */  
  45.   
  46.             unsigned        fTextAlign : 2;//文字对齐方式,取值如下:  
  47.             /* 
  48.             enum Align { 
  49.                 kLeft_Align,//左对齐 
  50.                 kCenter_Align,//居中 
  51.                 kRight_Align,//右对齐 
  52.             }; 
  53.             */  
  54.   
  55.             unsigned        fCapType : 2;//边界连接类型,分无连接,圆角连接,半方形连接  
  56.             unsigned        fJoinType : 2;//Path片断连接类型  
  57.               
  58.             unsigned        fStyle : 2;//绘制模式,填充边界/区域  
  59.             /* 
  60.                enum Style { 
  61.                kFill_Style, //填充区域 
  62.                kStroke_Style,//绘制边界 
  63.                kStrokeAndFill_Style,//填充区域并绘制边界 
  64.                }; 
  65.              */  
  66.   
  67.             unsigned        fTextEncoding : 2;//文字编码格式,支持如下几种  
  68.             enum TextEncoding {  
  69.                 kUTF8_TextEncoding,//utf-8,默认格式  
  70.                 kUTF16_TextEncoding,  
  71.                 kUTF32_TextEncoding,  
  72.                 kGlyphID_TextEncoding  
  73.             };  
  74.   
  75.             unsigned        fHinting : 2;  
  76.             unsigned        fFilterLevel : 2;//在图像绘制时提到的采样质量要求  
  77.             //unsigned      fFreeBits : 2;  
  78.         };  
  79.         uint32_t fBitfields;  
  80.     };  
  81.     uint32_t fDirtyBits;//记录哪些属性被改变了,以便更新相关的缓存  
  82. };  

2、字体绘制基本流程


SkCanvas
绘制文字和下划线

SkDraw

两种绘制方式:

(1)将文字解析为路径,然后绘制路径,缓存路径(drawText_asPaths)。

[cpp]  view plain  copy
  1. void SkDraw::drawText_asPaths(const char text[], size_t byteLength,  
  2.                               SkScalar x, SkScalar y,  
  3.                               const SkPaint& paint) const {  
  4.     SkDEBUGCODE(this->validate();)  
  5.   
  6.     SkTextToPathIter iter(text, byteLength, paint, true);  
  7.   
  8.     SkMatrix    matrix;  
  9.     matrix.setScale(iter.getPathScale(), iter.getPathScale());  
  10.     matrix.postTranslate(x, y);  
  11.   
  12.     const SkPath* iterPath;  
  13.     SkScalar xpos, prevXPos = 0;  
  14.   
  15.     while (iter.next(&iterPath, &xpos)) {  
  16.         matrix.postTranslate(xpos - prevXPos, 0);  
  17.         if (iterPath) {  
  18.             const SkPaint& pnt = iter.getPaint();  
  19.             if (fDevice) {  
  20.                 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);  
  21.             } else {  
  22.                 this->drawPath(*iterPath, pnt, &matrix, false);  
  23.             }  
  24.         }  
  25.         prevXPos = xpos;  
  26.     }  
  27. }  

(2)将文字解析为Mask(32*32的A8图片),然后绘制模板,缓存模板。

[cpp]  view plain  copy
  1. SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();  
  2.   
  3. SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, fMatrix);  
  4. SkGlyphCache*       cache = autoCache.getCache();  
  5.   
  6. // transform our starting point  
  7. {  
  8.     SkPoint loc;  
  9.     fMatrix->mapXY(x, y, &loc);  
  10.     x = loc.fX;  
  11.     y = loc.fY;  
  12. }  
  13.   
  14. // need to measure first  
  15. if (paint.getTextAlign() != SkPaint::kLeft_Align) {  
  16.     SkVector    stop;  
  17.   
  18.     measure_text(cache, glyphCacheProc, text, byteLength, &stop);  
  19.   
  20.     SkScalar    stopX = stop.fX;  
  21.     SkScalar    stopY = stop.fY;  
  22.   
  23.     if (paint.getTextAlign() == SkPaint::kCenter_Align) {  
  24.         stopX = SkScalarHalf(stopX);  
  25.         stopY = SkScalarHalf(stopY);  
  26.     }  
  27.     x -= stopX;  
  28.     y -= stopY;  
  29. }  
  30.   
  31. const char* stop = text + byteLength;  
  32.   
  33. SkAAClipBlitter     aaBlitter;  
  34. SkAutoBlitterChoose blitterChooser;  
  35. SkBlitter*          blitter = NULL;  
  36. if (needsRasterTextBlit(*this)) {  
  37.     blitterChooser.choose(*fBitmap, *fMatrix, paint);  
  38.     blitter = blitterChooser.get();  
  39.     if (fRC->isAA()) {  
  40.         aaBlitter.init(blitter, &fRC->aaRgn());  
  41.         blitter = &aaBlitter;  
  42.     }  
  43. }  
  44.   
  45. SkAutoKern          autokern;  
  46. SkDraw1Glyph        d1g;  
  47. SkDraw1Glyph::Proc  proc = d1g.init(this, blitter, cache, paint);  
  48.   
  49. SkFixed fxMask = ~0;  
  50. SkFixed fyMask = ~0;  
  51. if (cache->isSubpixel()) {  
  52.     SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);  
  53.     if (kX_SkAxisAlignment == baseline) {  
  54.         fyMask = 0;  
  55.         d1g.fHalfSampleY = SK_FixedHalf;  
  56.     } else if (kY_SkAxisAlignment == baseline) {  
  57.         fxMask = 0;  
  58.         d1g.fHalfSampleX = SK_FixedHalf;  
  59.     }  
  60. }  
  61.   
  62. SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;  
  63. SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;  
  64.   
  65. while (text < stop) {  
  66.     const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);  
  67.   
  68.     fx += autokern.adjust(glyph);  
  69.   
  70.     if (glyph.fWidth) {  
  71.         proc(d1g, fx, fy, glyph);  
  72.     }  
  73.   
  74.     fx += glyph.fAdvanceX;  
  75.     fy += glyph.fAdvanceY;  
  76. }  

cacheProc是翻译字符编码的函数,由SkPaint::getDrawCacheProc产生:

[cpp]  view plain  copy
  1. SkDrawCacheProc SkPaint::getDrawCacheProc() const {  
  2.     static const SkDrawCacheProc gDrawCacheProcs[] = {  
  3.         sk_getMetrics_utf8_00,  
  4.         sk_getMetrics_utf16_00,  
  5.         sk_getMetrics_utf32_00,  
  6.         sk_getMetrics_glyph_00,  
  7.   
  8.         sk_getMetrics_utf8_xy,  
  9.         sk_getMetrics_utf16_xy,  
  10.         sk_getMetrics_utf32_xy,  
  11.         sk_getMetrics_glyph_xy  
  12.     };  
  13.   
  14.     unsigned index = this->getTextEncoding();  
  15.     if (fFlags & kSubpixelText_Flag) {  
  16.         index += 4;  
  17.     }  
  18.   
  19.     SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));  
  20.     return gDrawCacheProcs[index];  
  21. }  

SkGlyphCache:

字形解析的结果缓存。

SkScalerContext:

负责字形的解析,有多种实现。Android中是用FreeType:SkScalerContext_FreeType。主要是generateImage和generatePath两个方法:

generateImage:

[cpp]  view plain  copy
  1. void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {  
  2.     SkAutoMutexAcquire  ac(gFTMutex);  
  3.   
  4.     FT_Error    err;  
  5.   
  6.     if (this->setupSize()) {  
  7.         goto ERROR;  
  8.     }  
  9.   
  10.     err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);  
  11.     if (err != 0) {  
  12.         SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",  
  13.                     glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));  
  14.     ERROR:  
  15.         memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);  
  16.         return;  
  17.     }  
  18.   
  19.     emboldenIfNeeded(fFace, fFace->glyph);  
  20.     generateGlyphImage(fFace, glyph);  
  21. }  
  22. void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) {  
  23.     const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);  
  24.     const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);  
  25.   
  26.     switch ( face->glyph->format ) {  
  27.         case FT_GLYPH_FORMAT_OUTLINE: {  
  28.             FT_Outline* outline = &face->glyph->outline;  
  29.             FT_BBox     bbox;  
  30.             FT_Bitmap   target;  
  31.   
  32.             int dx = 0, dy = 0;  
  33.             if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {  
  34.                 dx = SkFixedToFDot6(glyph.getSubXFixed());  
  35.                 dy = SkFixedToFDot6(glyph.getSubYFixed());  
  36.                 // negate dy since freetype-y-goes-up and skia-y-goes-down  
  37.                 dy = -dy;  
  38.             }  
  39.             FT_Outline_Get_CBox(outline, &bbox);  
  40.             /* 
  41.                 what we really want to do for subpixel is 
  42.                     offset(dx, dy) 
  43.                     compute_bounds 
  44.                     offset(bbox & !63) 
  45.                 but that is two calls to offset, so we do the following, which 
  46.                 achieves the same thing with only one offset call. 
  47.             */  
  48.             FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),  
  49.                                           dy - ((bbox.yMin + dy) & ~63));  
  50.   
  51.             if (SkMask::kLCD16_Format == glyph.fMaskFormat) {  
  52.                 FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);  
  53.                 SkMask mask;  
  54.                 glyph.toMask(&mask);  
  55.                 if (fPreBlend.isApplicable()) {  
  56.                     copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,  
  57.                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);  
  58.                 } else {  
  59.                     copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,  
  60.                                         fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);  
  61.                 }  
  62.             } else {  
  63.                 target.width = glyph.fWidth;  
  64.                 target.rows = glyph.fHeight;  
  65.                 target.pitch = glyph.rowBytes();  
  66.                 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);  
  67.                 target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);  
  68.                 target.num_grays = 256;  
  69.   
  70.                 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);  
  71.                 FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);  
  72.             }  
  73.         } break;  
  74.   
  75.         case FT_GLYPH_FORMAT_BITMAP: {  
  76.             FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);  
  77.             SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);  
  78.   
  79.             // Assume that the other formats do not exist.  
  80.             SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||  
  81.                      FT_PIXEL_MODE_GRAY == pixel_mode ||  
  82.                      FT_PIXEL_MODE_BGRA == pixel_mode);  
  83.   
  84.             // These are the only formats this ScalerContext should request.  
  85.             SkASSERT(SkMask::kBW_Format == maskFormat ||  
  86.                      SkMask::kA8_Format == maskFormat ||  
  87.                      SkMask::kARGB32_Format == maskFormat ||  
  88.                      SkMask::kLCD16_Format == maskFormat);  
  89.   
  90.             if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&  
  91.                 !(face->style_flags & FT_STYLE_FLAG_BOLD))  
  92.             {  
  93.                 FT_GlyphSlot_Own_Bitmap(face->glyph);  
  94.                 FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap,  
  95.                                    kBitmapEmboldenStrength, 0);  
  96.             }  
  97.   
  98.             // If no scaling needed, directly copy glyph bitmap.  
  99.             if (glyph.fWidth == face->glyph->bitmap.width &&  
  100.                 glyph.fHeight == face->glyph->bitmap.rows &&  
  101.                 glyph.fTop == -face->glyph->bitmap_top &&  
  102.                 glyph.fLeft == face->glyph->bitmap_left)  
  103.             {  
  104.                 SkMask dstMask;  
  105.                 glyph.toMask(&dstMask);  
  106.                 copyFTBitmap(face->glyph->bitmap, dstMask);  
  107.                 break;  
  108.             }  
  109.   
  110.             // Otherwise, scale the bitmap.  
  111.   
  112.             // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)  
  113.             SkBitmap unscaledBitmap;  
  114.             unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width,  
  115.                                                          face->glyph->bitmap.rows,  
  116.                                                          SkColorType_for_FTPixelMode(pixel_mode),  
  117.                                                          kPremul_SkAlphaType));  
  118.   
  119.             SkMask unscaledBitmapAlias;  
  120.             unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());  
  121.             unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height());  
  122.             unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();  
  123.             unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType());  
  124.             copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);  
  125.   
  126.             // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.  
  127.             // BW requires an A8 target for resizing, which can then be down sampled.  
  128.             // LCD should use a 4x A8 target, which will then be down sampled.  
  129.             // For simplicity, LCD uses A8 and is replicated.  
  130.             int bitmapRowBytes = 0;  
  131.             if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {  
  132.                 bitmapRowBytes = glyph.rowBytes();  
  133.             }  
  134.             SkBitmap dstBitmap;  
  135.             dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,  
  136.                                                 SkColorType_for_SkMaskFormat(maskFormat),  
  137.                                                 kPremul_SkAlphaType),  
  138.                               bitmapRowBytes);  
  139.             if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {  
  140.                 dstBitmap.allocPixels();  
  141.             } else {  
  142.                 dstBitmap.setPixels(glyph.fImage);  
  143.             }  
  144.   
  145.             // Scale unscaledBitmap into dstBitmap.  
  146.             SkCanvas canvas(dstBitmap);  
  147.             canvas.clear(SK_ColorTRANSPARENT);  
  148.             canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width),  
  149.                          SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows));  
  150.             SkPaint paint;  
  151.             paint.setFilterLevel(SkPaint::kMedium_FilterLevel);  
  152.             canvas.drawBitmap(unscaledBitmap, 0, 0, &paint);  
  153.   
  154.             // If the destination is BW or LCD, convert from A8.  
  155.             if (SkMask::kBW_Format == maskFormat) {  
  156.                 // Copy the A8 dstBitmap into the A1 glyph.fImage.  
  157.                 SkMask dstMask;  
  158.                 glyph.toMask(&dstMask);  
  159.                 packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());  
  160.             } else if (SkMask::kLCD16_Format == maskFormat) {  
  161.                 // Copy the A8 dstBitmap into the LCD16 glyph.fImage.  
  162.                 uint8_t* src = dstBitmap.getAddr8(0, 0);  
  163.                 uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);  
  164.                 for (int y = dstBitmap.height(); y --> 0;) {  
  165.                     for (int x = 0; x < dstBitmap.width(); ++x) {  
  166.                         dst[x] = grayToRGB16(src[x]);  
  167.                     }  
  168.                     dst = (uint16_t*)((char*)dst + glyph.rowBytes());  
  169.                     src += dstBitmap.rowBytes();  
  170.                 }  
  171.             }  
  172.   
  173.         } break;  
  174.   
  175.         default:  
  176.             SkDEBUGFAIL("unknown glyph format");  
  177.             memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);  
  178.             return;  
  179.     }  
  180.   
  181. // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,  
  182. // it is optional  
  183. #if defined(SK_GAMMA_APPLY_TO_A8)  
  184.     if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) {  
  185.         uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;  
  186.         unsigned rowBytes = glyph.rowBytes();  
  187.   
  188.         for (int y = glyph.fHeight - 1; y >= 0; --y) {  
  189.             for (int x = glyph.fWidth - 1; x >= 0; --x) {  
  190.                 dst[x] = fPreBlend.fG[dst[x]];  
  191.             }  
  192.             dst += rowBytes;  
  193.         }  
  194.     }  
  195. #endif  
  196. }  
generatePath:

[cpp]  view plain  copy
  1. void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,  
  2.                                             SkPath* path) {  
  3.     SkAutoMutexAcquire  ac(gFTMutex);  
  4.   
  5.     SkASSERT(&glyph && path);  
  6.   
  7.     if (this->setupSize()) {  
  8.         path->reset();  
  9.         return;  
  10.     }  
  11.   
  12.     uint32_t flags = fLoadGlyphFlags;  
  13.     flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline  
  14.     flags &= ~FT_LOAD_RENDER;   // don't scan convert (we just want the outline)  
  15.   
  16.     FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);  
  17.   
  18.     if (err != 0) {  
  19.         SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",  
  20.                     glyph.getGlyphID(fBaseGlyphCount), flags, err));  
  21.         path->reset();  
  22.         return;  
  23.     }  
  24.     emboldenIfNeeded(fFace, fFace->glyph);  
  25.   
  26.     generateGlyphPath(fFace, path);  
  27.   
  28.     // The path's origin from FreeType is always the horizontal layout origin.  
  29.     // Offset the path so that it is relative to the vertical origin if needed.  
  30.     if (fRec.fFlags & SkScalerContext::kVertical_Flag) {  
  31.         FT_Vector vector;  
  32.         vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;  
  33.         vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;  
  34.         FT_Vector_Transform(&vector, &fMatrix22);  
  35.         path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y));  
  36.     }  
  37. }  


3、字体缓存管理
SkTypeface是Skia中的字体类,对应可有多种字体库解析实现。
由于Android上面使用的是FreeType,因此也只讲FreeType分支。
FreeType的使用方法可参考:http://blog.csdn.net/furtherchan/article/details/8667884
字体建立的代码如下:
[cpp]  view plain  copy
  1. SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {  
  2.     return SkFontHost::CreateTypefaceFromStream(stream);  
  3. }  
  4.   
  5. bool find_name_and_attributes(SkStream* stream, SkString* name,  
  6.                               SkTypeface::Style* style, bool* isFixedPitch) {  
  7.     FT_Library  library;  
  8.     if (FT_Init_FreeType(&library)) {  
  9.         return false;  
  10.     }  
  11.   
  12.     FT_Open_Args    args;  
  13.     memset(&args, 0, sizeof(args));  
  14.   
  15.     const void* memoryBase = stream->getMemoryBase();  
  16.     FT_StreamRec    streamRec;  
  17.   
  18.     if (NULL != memoryBase) {  
  19.         args.flags = FT_OPEN_MEMORY;  
  20.         args.memory_base = (const FT_Byte*)memoryBase;  
  21.         args.memory_size = stream->getLength();  
  22.     } else {  
  23.         memset(&streamRec, 0, sizeof(streamRec));  
  24.         streamRec.size = stream->getLength();  
  25.         streamRec.descriptor.pointer = stream;  
  26.         streamRec.read  = sk_stream_read;  
  27.         streamRec.close = sk_stream_close;  
  28.   
  29.         args.flags = FT_OPEN_STREAM;  
  30.         args.stream = &streamRec;  
  31.     }  
  32.   
  33.     FT_Face face;  
  34.     if (FT_Open_Face(library, &args, 0, &face)) {  
  35.         FT_Done_FreeType(library);  
  36.         return false;  
  37.     }  
  38.   
  39.     int tempStyle = SkTypeface::kNormal;  
  40.     if (face->style_flags & FT_STYLE_FLAG_BOLD) {  
  41.         tempStyle |= SkTypeface::kBold;  
  42.     }  
  43.     if (face->style_flags & FT_STYLE_FLAG_ITALIC) {  
  44.         tempStyle |= SkTypeface::kItalic;  
  45.     }  
  46.   
  47.     if (name) {  
  48.         name->set(face->family_name);  
  49.     }  
  50.     if (style) {  
  51.         *style = (SkTypeface::Style) tempStyle;  
  52.     }  
  53.     if (isFixedPitch) {  
  54.         *isFixedPitch = FT_IS_FIXED_WIDTH(face);  
  55.     }  
  56.   
  57.     FT_Done_Face(face);  
  58.     FT_Done_FreeType(library);  
  59.     return true;  
  60. }  



对于Android,在系统初始化时,所有字体文件在预加载时即被解析,包装为SkFaceRec,存为一个全局链表。(frameworks/base/graphic 和 frameworks/base/core/jni目录下面的代码)

[java]  view plain  copy
  1. public class Typeface {  
  2.     /* 
  3.        ....... 
  4.     */  
  5.     private static void init() {  
  6.         // Load font config and initialize Minikin state  
  7.         File systemFontConfigLocation = getSystemFontConfigLocation();  
  8.         File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);  
  9.         try {  
  10.             FileInputStream fontsIn = new FileInputStream(configFilename);  
  11.             FontListParser.Config fontConfig = FontListParser.parse(fontsIn);  
  12.   
  13.             List<FontFamily> familyList = new ArrayList<FontFamily>();  
  14.             // Note that the default typeface is always present in the fallback list;  
  15.             // this is an enhancement from pre-Minikin behavior.  
  16.             for (int i = 0; i < fontConfig.families.size(); i++) {  
  17.                 Family f = fontConfig.families.get(i);  
  18.                 if (i == 0 || f.name == null) {  
  19.                     familyList.add(makeFamilyFromParsed(f));  
  20.                 }  
  21.             }  
  22.             sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);  
  23.             setDefault(Typeface.createFromFamilies(sFallbackFonts));  
  24.   
  25.             Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();  
  26.             for (int i = 0; i < fontConfig.families.size(); i++) {  
  27.                 Typeface typeface;  
  28.                 Family f = fontConfig.families.get(i);  
  29.                 if (f.name != null) {  
  30.                     if (i == 0) {  
  31.                         // The first entry is the default typeface; no sense in  
  32.                         // duplicating the corresponding FontFamily.  
  33.                         typeface = sDefaultTypeface;  
  34.                     } else {  
  35.                         FontFamily fontFamily = makeFamilyFromParsed(f);  
  36.                         FontFamily[] families = { fontFamily };  
  37.                         typeface = Typeface.createFromFamiliesWithDefault(families);  
  38.                     }  
  39.                     systemFonts.put(f.name, typeface);  
  40.                 }  
  41.             }  
  42.             for (FontListParser.Alias alias : fontConfig.aliases) {  
  43.                 Typeface base = systemFonts.get(alias.toName);  
  44.                 Typeface newFace = base;  
  45.                 int weight = alias.weight;  
  46.                 if (weight != 400) {  
  47.                     newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));  
  48.                 }  
  49.                 systemFonts.put(alias.name, newFace);  
  50.             }  
  51.             sSystemFontMap = systemFonts;  
  52.   
  53.         } catch (RuntimeException e) {  
  54.             Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);  
  55.             // TODO: normal in non-Minikin case, remove or make error when Minikin-only  
  56.         } catch (FileNotFoundException e) {  
  57.             Log.e(TAG, "Error opening " + configFilename);  
  58.         } catch (IOException e) {  
  59.             Log.e(TAG, "Error reading " + configFilename);  
  60.         } catch (XmlPullParserException e) {  
  61.             Log.e(TAG, "XML parse exception for " + configFilename);  
  62.         }  
  63.     }  
  64.   
  65.     static {  
  66.         init();  
  67.         // Set up defaults and typefaces exposed in public API  
  68.         DEFAULT         = create((String) null0);  
  69.         DEFAULT_BOLD    = create((String) null, Typeface.BOLD);  
  70.         SANS_SERIF      = create("sans-serif"0);  
  71.         SERIF           = create("serif"0);  
  72.         MONOSPACE       = create("monospace"0);  
  73.   
  74.         sDefaults = new Typeface[] {  
  75.             DEFAULT,  
  76.             DEFAULT_BOLD,  
  77.             create((String) null, Typeface.ITALIC),  
  78.             create((String) null, Typeface.BOLD_ITALIC),  
  79.         };  
  80.   
  81.     }  
  82.     /* 
  83.     ...... 
  84.     */  
  85. }  


SkTypeface 记录一个字体的id,在使用时,到链表中查出相关的字体。

对一个字体和样式,建一个 SkGlyphCache缓存,内含一个 SkScalerContext 和一个 SkGlyph 的哈希表,SkGlyph 缓存一个字体中一个字解析出来的位图。此有内存容量限制,当超过容量时,会清除之前缓存的位图。Hash冲突时,直接生成新字形替换原来的字形。

缓存限制的内存宏详见:src/core/SkGlyphCache_Globals.h。和include/core/SkUserConfig.h中的SK_DEFAULT_FONT_CACHE_LIMIT宏

[cpp]  view plain  copy
  1. struct SkGlyph {  
  2.     void*       fImage;  
  3.     SkPath*     fPath;  
  4.     SkFixed     fAdvanceX, fAdvanceY;  
  5.   
  6.     uint32_t    fID;  
  7.     uint16_t    fWidth, fHeight;  
  8.     int16_t     fTop, fLeft;  
  9.   
  10.     void*       fDistanceField;  
  11.     uint8_t     fMaskFormat;  
  12.     int8_t      fRsbDelta, fLsbDelta;  // used by auto-kerning  
  13. };  


当绘制字体只绘边界或者位图缓存机制不好处理时,将字体解析成点线,构成SkPath,也做缓存。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值