碼農中,對於那些只會調用API的作法老是嗤之以鼻。雖然,掌握不少APIs的用法並不必定是什麼多麼使人自豪的事情,但若是沒有掌握足夠多的經常使用API,如STL,Java 標準庫等的用法,則多半是不那麼光彩的。 android
早期的android,在文本渲染部分一直有一個Bug,即,對於像泰語、印度語這類複雜語系,沒有應用OpenType字庫文件的GPOS信息。在Google的原生Code中,這個Bug甚至在4.1 Jelly Bean 中都一直存在。 其實在4.0 ICS,引入了OpenType Shaping引擎Harfbuzz,已經可以正確的獲取GPOS這些信息,只是這些信息之前是直接被丟棄不用罷了。 canvas
對於這個問題,其實作android的各家,本身都有在提供解決方案。一位前輩的解法爲:在Skia中開了一路接口出來,SkCanvas->SkDevice->SkDraw->SkScalerContext 等,以便於GPOS的這些information能夠被帶下去,以便於在往Canvas中畫字時能夠被應用到。 api
在android 4.2中,這個問題已經獲得了適當的處理,其中比較重要的一段Code以下:
app
721 // Get glyph positions (and reverse them in place if RTL) 722 if (outPos) { 723 size_t countGlyphs = mShaperItem.num_glyphs; 724 jfloat x = totalAdvance; 725 for (size_t i = 0; i < countGlyphs; i++) { 726 size_t index = (!isRTL) ? i : countGlyphs - 1 - i; 727 float xo = HBFixedToFloat(mShaperItem.offsets[index].x); 728 float yo = HBFixedToFloat(mShaperItem.offsets[index].y); 729 // Apply skewX component of transform to position offsets. Note 730 // that scale has already been applied through x_ and y_scale 731 // set in the mFontRec. 732 outPos->add(x + xo + yo * skewX); 733 outPos->add(yo); 734#if DEBUG_GLYPHS 735 ALOGD(" -- hb adv[%d] = %f, log_cluster[%d] = %d", 736 index, HBFixedToFloat(mShaperItem.advances[index]), 737 index, mShaperItem.log_clusters[index]); 738#endif 739 x += HBFixedToFloat(mShaperItem.advances[index]); 740 } 741 }這段Code的主要做用爲,將Harfbuzz返回的數據作一個格式轉換,轉換爲SkCanvas::drawPosText()可用的形式,一個是數字的格式的轉換,即由Harfbuzz shape時所用的單位轉換到float型的像素值,另外就是知足drawPosText()對於數據放置位置的要求,SkCanvas::drawPosText()的原型爲:
705 /** Draw the text, with each character/glyph origin specified by the pos[] 706 array. The origin is interpreted by the Align setting in the paint. 707 @param text The text to be drawn 708 @param byteLength The number of bytes to read from the text parameter 709 @param pos Array of positions, used to position each character 710 @param paint The paint used for the text (e.g. color, size, style) 711 */ 712 virtual void drawPosText(const void* text, size_t byteLength, 713 const SkPoint pos[], const SkPaint& paint);在JNI的Canvas.cpp中能夠看到另外的變化:
819 static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count, 820 jfloat x, jfloat y, int flags, SkPaint* paint) { 821 // Beware: this needs Glyph encoding (already done on the Paint constructor) 822 canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint); 823 } 824 825 static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray, 826 int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { 827 SkPoint* posPtr = new SkPoint[count]; 828 for (int indx = 0; indx < count; indx++) { 829 posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]); 830 posPtr[indx].fY = SkFloatToScalar(y + posArray[indx * 2 + 1]); 831 } 832 canvas->drawPosText(glyphArray, count << 1, posPtr, *paint); 833 delete[] posPtr; 834 }上面的doDrawGlyphs() 在android 4.2已經被棄置不用了,實際在執行的爲doDrawGlyphsPos()函數。能夠看到原來使用SkCanvas::drawText() 也是遭遇了被棄置不用的命運,在新版中,有改採SkCanvas::drawPosText()這個函數。 Google的這個解法,明顯要簡潔的多。
看到Google的解法,對比以前看到的解法,實在是不能不使人感嘆,「API不是萬能的,但不懂API是萬萬不能的」。對於碼農來講,熟悉API的用法,大概就好象修理工熟悉本身的鉗子、螺絲刀同樣重要吧。 函數