Android4.2文本渲染加強之應用GPOS

碼農中,對於那些只會調用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的用法,大概就好象修理工熟悉本身的鉗子、螺絲刀同樣重要吧。 函數

相關文章
相關標籤/搜索