Coder
分类: Android平台
2013-01-25 23:11:27
码农中,对于那些只会调用API的做法总是嗤之以鼻。虽然,掌握很多APIs的用法并不一定是什么多么令人自豪的事情,但如果没有掌握足够多的常用API,如STL,Java 标准库等的用法,则多半是不那么光彩的。
早期的android,在文本渲染部分一直有一个Bug,即,对于像泰语、印度语这类复杂语系,没有应用OpenType字库文件的GPOS信息。在Google的原生Code中,这个Bug甚至在4.1 Jelly Bean 中都一直存在。 其实在4.0 ICS,引入了OpenType Shaping引擎Harfbuzz,已经能够正确的获取GPOS这些信息,只是这些信息以前是直接被丢弃不用罢了。
对于这个问题,其实做android的各家,自己都有在提供解决方案。一位前辈的解法为:在Skia中开了一路接口出来,SkCanvas->SkDevice->SkDraw->SkScalerContext 等,以便于GPOS的这些information可以被带下去,以便于在往Canvas中画字时可以被应用到。
在android 4.2中,这个问题已经得到了适当的处理,其中比较重要的一段Code如下:
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的用法,大概就好象修理工熟悉自己的钳子、螺丝刀一样重要吧。