青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-341  評(píng)論-2670  文章-0  trackbacks-0

Uniscribe是Windows 2000以來(lái)就存在于WinAPI中的一個(gè)庫(kù)。這個(gè)庫(kù)能夠提供給我們關(guān)于字符串渲染的很多信息,譬如說(shuō)哪里可以換行啦,渲染的時(shí)候字符的順序應(yīng)該是什么樣子啦,還有每一個(gè)字符的大小什么的。關(guān)于Uniscribe的資料可以在http://msdn.microsoft.com/en-us/library/windows/desktop/dd374091(v=vs.85).aspx看到。

在使用Uniscribe之前,我們先看看利用Uniscribe我們可以做到什么樣的效果:

image

image

通過(guò)Uniscribe,我們可以獲得把各種不同大小的字符串混合在一起渲染的時(shí)候所需要的所有數(shù)據(jù),甚至可以再漂亮的地方換行,譬如說(shuō)這里:

image

當(dāng)然,渲染的部分不包含在Uniscribe里面,只不過(guò)Uniscribe告訴我們的信息可以讓我們直接計(jì)算出渲染每一小段字符串的位置。當(dāng)然,這也就足夠了。下面我來(lái)介紹一下Uniscribe的幾個(gè)函數(shù)的作用。

首先,我們需要注意的是,Uniscribe一次只處理一行字符串。我們固然可以把多行字符串一次性丟給Uniscribe進(jìn)行計(jì)算,但是得到的結(jié)果處理起來(lái)要困難得多。所以我們一次只給Uniscribe一行的字符串。現(xiàn)在我們需要渲染一個(gè)帶有多種格式的一行字符串。首先我們需要知道這些字符串可以被分為多少段。這在那些從右到左閱讀的文字(譬如說(shuō)阿拉伯文)特別重要,而且這也是一個(gè)特別復(fù)雜的話題,在這里我就不講了,我們假設(shè)我們只處理從左到右的字符串。

于是我們第一個(gè)遇到的函數(shù)就是ScriptItemize
HRESULT ScriptItemize(
  _In_      const WCHAR *pwcInChars,
  _In_      int cInChars,
  _In_      int cMaxItems,
  _In_opt_  const SCRIPT_CONTROL *psControl,
  _In_opt_  const SCRIPT_STATE *psState,
  _Out_     SCRIPT_ITEM *pItems,
  _Out_     int *pcItems
);

由于我們不處理從右到左的字符串渲染,也不處理把數(shù)字變成亂七八糟格式的效果(譬如在某些邪惡的帝國(guó)主義國(guó)家12345被表達(dá)成12,345),因此這個(gè)函數(shù)的psControl和psState參數(shù)我們都可以給NULL。這個(gè)時(shí)候我們需要首先為SCRIPT_ITEM數(shù)組分配空間。由于一個(gè)字符串的item最多就是字符數(shù)量那么多個(gè),所以我們要先創(chuàng)建一個(gè)cInChars+1那么長(zhǎng)的SCRIPT_ITEM數(shù)組。在調(diào)用了這個(gè)函數(shù)之后,*pcItems+1的結(jié)果就是pItems里面的有效長(zhǎng)度了。為什么pItems的長(zhǎng)度總是要+1呢?因?yàn)镾CRIPT_ITEM里面有一個(gè)很有用的成員叫做iCharPos,這個(gè)成員告訴我們這個(gè)item是從字符串的什么地方開(kāi)始的。那長(zhǎng)度呢?自然是用下一個(gè)SCRIPT_ITEM的cCharPos去剪了。那么最后一個(gè)item怎么辦呢?所以ScriptItemize給了我們額外的一個(gè)結(jié)尾item,讓我們總是可以方便的這么減……特別的蛋疼……

好了,現(xiàn)在我們把一行字符串分成了各個(gè)item。現(xiàn)在第一個(gè)問(wèn)題就來(lái)了,一行字符串里面可能有各種不同的字體的樣式,接下來(lái)怎么辦呢?我們要同時(shí)用item的邊界和樣式的邊界來(lái)切割這個(gè)字符串,讓每一個(gè)字符串的片段都完全被某個(gè)item包含,并且片段的所有字符都有一樣的樣式。這聽(tīng)起來(lái)好像很復(fù)雜,我來(lái)舉個(gè)例子:

譬如我們有一個(gè)字符串長(zhǎng)成下面這個(gè)樣子:
This parameter (foo) is optional

然后ScriptItemize告訴我們這個(gè)字符串一共分為3個(gè)片段(這個(gè)劃分當(dāng)然是我胡扯的,我只是舉個(gè)例子):
This parameter
(foo)
is optional

所以,字體的樣式和ScriptItemize的結(jié)果就把這個(gè)字符串分成了下面的五段:
This
parameter
(foo)
is
optional

是不是聽(tīng)起來(lái)很直觀呢?但是代碼寫起來(lái)還是比較麻煩的,不過(guò)其實(shí)說(shuō)麻煩也不麻煩,只需要大約十行左右就可以搞定了。在MSDN里面,這五段的“段”叫做“run”或者是“range”。

現(xiàn)在,我們拿起一個(gè)run,送進(jìn)一個(gè)叫做ScriptShape的函數(shù)里面:
HRESULT ScriptShape(
  _In_     HDC hdc,
  _Inout_  SCRIPT_CACHE *psc,
  _In_     const WCHAR *pwcChars,
  _In_     int cChars,
  _In_     int cMaxGlyphs,
  _Inout_  SCRIPT_ANALYSIS *psa,
  _Out_    WORD *pwOutGlyphs,
  _Out_    WORD *pwLogClust,
  _Out_    SCRIPT_VISATTR *psva,
  _Out_    int *pcGlyphs
);

這個(gè)函數(shù)可以告訴我們,這一堆wchar_t可以被如何分割成glyph。這里我們要注意的是,glyph的數(shù)量和wchar_t的數(shù)量并不相同。所以在調(diào)用這個(gè)函數(shù)的時(shí)候,我們要先猜一個(gè)長(zhǎng)度來(lái)分配空間。MSDN告訴我們,我們可以先讓cMaxGlyphs = cChars*1.5 + 16。

在上面的參數(shù)里,SCRIPT_ANALYSIS其實(shí)就是SCRIPT_ITEM::a。由于一個(gè)run肯定是完整的屬于一個(gè)item的,因此SCRIPT_ITEM就可以直接從上一個(gè)函數(shù)的結(jié)果獲得了。然后這個(gè)函數(shù)告訴我們?nèi)齻€(gè)信息:
1、pwOutGlyphs:這個(gè)字符串一共有多少glyph組成。
2、psva:每一個(gè)glyph的屬性是什么。
3、pwLogClust:wchar_t(術(shù)語(yǔ)叫unicode code point)是如何跟glyph對(duì)應(yīng)起來(lái)的。

在這里解釋一下glyph是什么意思。glyph其實(shí)就是字體里面的一個(gè)“圖”。一個(gè)看起來(lái)像一個(gè)字符的東西,有可能由多個(gè)glyph組成,譬如說(shuō)“á”,其實(shí)就占用了兩個(gè)wchar_t,同時(shí)這兩個(gè)wchar_t具有兩個(gè)glyph(a和上面的小點(diǎn))。而且這兩個(gè)wchar_t在渲染的時(shí)候必須被渲染在一起,因此他們至少應(yīng)該屬于同一個(gè)range,鼠標(biāo)在文本框選中的時(shí)候,這兩個(gè)wchar_t必須作為一個(gè)整體(后面這些信息可以由ScriptBreak函數(shù)給出)。當(dāng)然還有1個(gè)wchar_t對(duì)多個(gè)glyph的情況,但是我現(xiàn)在一下子找不到。

不僅如此,還有兩個(gè)wchar_t對(duì)一個(gè)glyph的情況,譬如說(shuō)這些字“? ”。雖然wchar_t的范圍是0到65536,但這并不代表utf-16只有6萬(wàn)多個(gè)字符(實(shí)際上是60多萬(wàn)),所以wchar_t其實(shí)也是變長(zhǎng)的。但是utf-16的編碼設(shè)計(jì)的很好,當(dāng)我們拿到一個(gè)wchar_t的時(shí)候,我們通過(guò)閱讀他們的數(shù)字就可以知道這個(gè)wchar_t是只有一個(gè)code point的、還是那些兩個(gè)code point的字的第一個(gè)或者是第二個(gè),跟我們以前遇到的MBCS(char/ANSI)完全不同。

因此wchar_t和glyph的對(duì)應(yīng)關(guān)系很復(fù)雜,可能是一對(duì)多、多對(duì)一、一對(duì)一或者多對(duì)多。所以pwLogClust這個(gè)數(shù)組就特別的重要。MSDN里面有一個(gè)例子:

譬如說(shuō)我們的一個(gè)7個(gè)wchar_t的字符串被分成4組glyph,對(duì)應(yīng)關(guān)系如下:
字符:| c1u1 | c2u1 | c3u1 c3u2 c3u3 | c4u1 c4u2 |
圖案:| c1g1 | c2g1 c2g2 c2g3 | c3g1 | c4g1 c4g2 c4g3 |

上面的意思是,第二個(gè)字符c2u2被渲染成了3個(gè)glyph:c2g1、c2g2和c2g3,而c3u1、c3u2和c3u3三個(gè)字符責(zé)備合并成了一個(gè)glyph:c3g1。這種情況下,pwLogClust[cChars]的內(nèi)容就是下面這個(gè)樣子的:
| 0 | 1 | 4 4 4 | 5 5 |

連續(xù)的數(shù)字相同的幾個(gè)clust說(shuō)明這些wchar_t是被歸到一起的,而且這一組wchar_t的第一個(gè)glyph的的序號(hào)就是pwLogClust的內(nèi)容了。那么這一組wchar_t究竟有多少個(gè)glyph呢?當(dāng)然就要看下一組wchar_t的第一個(gè)glyph在哪了。

為什么我們需要這些信息呢?因?yàn)樽址拈L(zhǎng)度是按照glyph的長(zhǎng)度來(lái)計(jì)算的!而且接下來(lái)我們要介紹的函數(shù)ScriptPlace會(huì)真的給我們每一個(gè)glyph的長(zhǎng)度。因此我們?cè)谟?jì)算換行的時(shí)候,我們只能在每一組glyph并且ScriptBreak告訴我們可以換行的那個(gè)地方換行,所以當(dāng)我們拿出一段完整的不會(huì)被換行的一個(gè)run的子集的時(shí)候,我們要在渲染的時(shí)候計(jì)算長(zhǎng)度,就要特別小心glyph和wchar_t的對(duì)應(yīng)關(guān)系。因?yàn)槲覀冧秩镜氖且淮畐char_t,但是我們的長(zhǎng)度是按照glyph計(jì)算的,這個(gè)對(duì)應(yīng)關(guān)系要是亂掉了,要么計(jì)算出錯(cuò),要么渲染的字符選錯(cuò),總之是很麻煩的。那么ScriptPlace究竟長(zhǎng)什么樣子呢:
HRESULT ScriptPlace(
  _In_     HDC hdc,
  _Inout_  SCRIPT_CACHE *psc,
  _In_     const WORD *pwGlyphs,
  _In_     int cGlyphs,
  _In_     const SCRIPT_VISATTR *psva,
  _Inout_  SCRIPT_ANALYSIS *psa,
  _Out_    int *piAdvance,
  _Out_    GOFFSET *pGoffset,
  _Out_    ABC *pABC
);

這就是那個(gè)傳說(shuō)中的幫我們計(jì)算glyph大小的函數(shù)了。其中pwGlyphs就是我們剛剛從ScriptShape函數(shù)拿到的pwOutGlyphs,而psa還是那個(gè)psa,psva也還是那個(gè)psva。接下來(lái)的piAdvance數(shù)組告訴我們每一個(gè)glyph的長(zhǎng)度,pGoffset這個(gè)是每一個(gè)glyph的偏移量(還記得“á”上面的那個(gè)小點(diǎn)嗎),pABC是整一個(gè)run的長(zhǎng)度。至于ABC的三個(gè)長(zhǎng)度我們并不用管,因?yàn)槲覀冃枰氖莗ABC里面三個(gè)長(zhǎng)度的和。而且這個(gè)和跟piAdvance的所有數(shù)字加起來(lái)一樣。

現(xiàn)在我們拿到了所有g(shù)lyph的尺寸信息,和他們的分組情況,最后就是知道字符串的一些屬性了,譬如說(shuō)在哪里可以換行。為什么要知道這些呢?譬如說(shuō)我們有一個(gè)字符串叫做
c:\ThisIsAFolder\ThisIsAFile.txt

然后我們渲染字符串的位置可以容納下“c:\ThisIsAFolder\”,卻不能容納完整的“c:\ThisIsAFolder\ThisIsAFile”。這個(gè)時(shí)候,ScriptBreak函數(shù)就可以告訴我們,一個(gè)優(yōu)美的換行可以在斜杠“\”的后面產(chǎn)生。讓我們來(lái)看看這個(gè)ScriptBreak函數(shù)的真面目:
HRESULT ScriptBreak(
  _In_   const WCHAR *pwcChars,
  _In_   int cChars,
  _In_   const SCRIPT_ANALYSIS *psa,
  _Out_  SCRIPT_LOGATTR *psla
);

這個(gè)函數(shù)告訴我們每一個(gè)wchar_t對(duì)應(yīng)的SCRIPT_LOGATTR。這個(gè)結(jié)構(gòu)我們暫時(shí)只關(guān)心下面幾個(gè)成員:
1、fSoftBreak:可以被換行的位置。譬如說(shuō)上面那個(gè)美妙的換行在“\”處,就是因?yàn)榻酉聛?lái)的ThisIsAFile的第一個(gè)字符“T”的fSoftBreak是TRUE。
2、fCharStop和fWordStop:告訴我們每一個(gè)wchar_t是不是char或者word的第一個(gè)code point(參考那些一個(gè)字有兩個(gè)wchar_t那么長(zhǎng)的? )。

現(xiàn)在我們距離大功告成已經(jīng)很近了。我們?cè)阡秩镜臅r(shí)候,就一個(gè)run一個(gè)run的渲染。當(dāng)我們發(fā)現(xiàn)一行剩余的空間不夠容納一個(gè)完整的run的時(shí)候,我們就可以用ScriptBreak告訴我們的信息,把這個(gè)run看成若干個(gè)可以被切開(kāi)的段,然后用ScriptPlace告訴我們的piAdvance算出每一個(gè)切開(kāi)的小段落的長(zhǎng)度,然后盡可能多的完整渲染這些段。

上面這段話雖然很簡(jiǎn)單,但是實(shí)際上需要注意的事情特別多,譬如說(shuō)那個(gè)復(fù)雜的wchar_t和glyph的關(guān)系。我們通過(guò)piAdvance計(jì)算出可以一次性渲染的glyph有多少個(gè),再把通過(guò)ScriptShape告訴我們的pwLogClust把這些glyph換算成對(duì)應(yīng)wchar_t的范圍。最后再把他們送進(jìn)TextOut函數(shù)里,如果你用的是GDI的話。每次渲染完一些glyph,x坐標(biāo)就要偏移他們的piAdvances的和。

如果把上面這些事情全部做完的話,我們就已經(jīng)完整的渲染出一行帶有復(fù)雜結(jié)構(gòu)的文字了。

=========================================================

最后我貼上這個(gè)程序的代碼。這個(gè)程序使用GacUI編寫,中間的部分使用GDI進(jìn)行渲染。由于這只是個(gè)臨時(shí)代碼,會(huì)從codeplex上刪掉,所以把代碼留在這里,給有需要的人閱讀。

代碼里面用到的這個(gè)叫document.txt的文件,可以在GacUI的Codeplex頁(yè)面上下載代碼后,在(\Libraries\GacUI\GacUISrc\GacUISrcCodepackedTest\Resources\document.txt)找到

#include <GacUI.h>
#include <usp10.h>

#pragma comment(lib, "usp10.lib")

using namespace vl::collections;
using namespace vl::stream;
using namespace vl::regex;
using namespace vl::presentation::windows;

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
    return SetupWindowsGDIRenderer();
}

/***********************************************************************
Uniscribe
***********************************************************************/

bool operator==(const SCRIPT_ITEM&, const SCRIPT_ITEM&){return false;}
bool operator!=(const SCRIPT_ITEM&, const SCRIPT_ITEM&){return false;}

bool operator==(const SCRIPT_VISATTR&, const SCRIPT_VISATTR&){return false;}
bool operator!=(const SCRIPT_VISATTR&, const SCRIPT_VISATTR&){return false;}

bool operator==(const GOFFSET&, const GOFFSET&){return false;}
bool operator!=(const GOFFSET&, const GOFFSET&){return false;}

bool operator==(const SCRIPT_LOGATTR&, const SCRIPT_LOGATTR&){return false;}
bool operator!=(const SCRIPT_LOGATTR&, const SCRIPT_LOGATTR&){return false;}

namespace test
{

/***********************************************************************
DocumentFragment
***********************************************************************/

    class DocumentFragment : public Object
    {
    public:
        bool                paragraph;
        WString                font;
        bool                bold;
        Color                color;
        int                    size;
        WString                text;
        Ptr<WinFont>        fontObject;

        DocumentFragment()
            :paragraph(false)
            ,bold(false)
            ,size(0)
        {
        }

        DocumentFragment(Ptr<DocumentFragment> prototype, const WString& _text)
            :paragraph(prototype->paragraph)
            ,font(prototype->font)
            ,bold(prototype->bold)
            ,color(prototype->color)
            ,size(prototype->size)
            ,fontObject(prototype->fontObject)
            ,text(_text)
        {
        }

        WString GetFingerPrint()
        {
            return font+L":"+(bold?L"B":L"N")+L":"+itow(size);
        }
    };

    int ConvertHex(wchar_t c)
    {
        if(L'a'<=c && c<=L'f') return c-L'a'+10;
        if(L'A'<=c && c<=L'F') return c-L'A'+10;
        if(L'0'<=c && c<=L'9') return c-L'0';
        return 0;
    }

    Color ConvertColor(const WString& colorString)
    {
        return Color(
            ConvertHex(colorString[1])*16+ConvertHex(colorString[2]),
            ConvertHex(colorString[3])*16+ConvertHex(colorString[4]),
            ConvertHex(colorString[5])*16+ConvertHex(colorString[6])
            );
    }

    void BuildDocumentFragments(const WString& fileName, List<Ptr<DocumentFragment>>& fragments)
    {
        fragments.Clear();
        WString rawDocument;
        {
            FileStream fileStream(fileName, FileStream::ReadOnly);
            Utf8Decoder decoder;
            DecoderStream decoderStream(fileStream, decoder);
            StreamReader reader(decoderStream);
            rawDocument=reader.ReadToEnd();
        }

        Regex regex(L"<(<tag>s)>(<font>[^:]+):(<bold>[^:]+):(<color>[^:]+):(<size>[^:]+):(<text>/.*?)<//s>|<(<tag>p)//>");
        RegexMatch::List matches;
        regex.Search(rawDocument, matches);
       
        for(int i=0;i<matches.Count();i++)
        {
            Ptr<RegexMatch> match=matches[i];
            Ptr<DocumentFragment> fragment=new DocumentFragment;
            fragments.Add(fragment);
            if(match->Groups()[L"tag"][0].Value()==L"p")
            {
                fragment->paragraph=true;
            }
            else
            {
                WString font=match->Groups()[L"tag"][0].Value();
                WString bold=match->Groups()[L"bold"][0].Value();
                WString color=match->Groups()[L"color"][0].Value();
                WString size=match->Groups()[L"size"][0].Value();
                WString text=match->Groups()[L"text"][0].Value();

                fragment->font=font;
                fragment->bold=bold==L"true";
                fragment->size=wtoi(size);
                fragment->color=ConvertColor(color);
                fragment->text=text;
            }
        }
    }

/***********************************************************************
ScriptFragment
***********************************************************************/
   
    struct GlyphData
    {
        Array<WORD>                    glyphs;
        Array<SCRIPT_VISATTR>        glyphVisattrs;
        Array<int>                    glyphAdvances;
        Array<GOFFSET>                glyphOffsets;
        Array<WORD>                    charCluster;
        ABC                            runAbc;

        GlyphData()
        {
            memset(&runAbc, 0, sizeof(runAbc));
        }

        void ClearUniscribeData(int glyphCount, int length)
        {
            glyphs.Resize(glyphCount);
            glyphVisattrs.Resize(glyphCount);
            glyphAdvances.Resize(glyphCount);
            glyphOffsets.Resize(glyphCount);
            charCluster.Resize(length);
            memset(&runAbc, 0, sizeof(runAbc));
        }
           
        bool BuildUniscribeData(WinDC* dc, DocumentFragment* documentFragment, SCRIPT_ITEM* scriptItem, SCRIPT_CACHE& scriptCache, const wchar_t* runText, int length)
        {
            int glyphCount=glyphs.Count();
            bool resizeGlyphData=false;
            if(glyphCount==0)
            {
                glyphCount=(int)(1.5*length+16);
                resizeGlyphData=true;
            }
            {
                // generate shape information
                WinDC* dcParameter=0;
                if(resizeGlyphData)
                {
                    glyphs.Resize(glyphCount);
                    glyphVisattrs.Resize(glyphCount);
                    charCluster.Resize(length);
                }

                while(true)
                {
                    int availableGlyphCount=0;
                    HRESULT hr=ScriptShape(
                        (dcParameter?dcParameter->GetHandle():NULL),
                        &scriptCache,
                        runText,
                        length,
                        glyphCount,
                        &scriptItem->a,
                        &glyphs[0],
                        &charCluster[0],
                        &glyphVisattrs[0],
                        &availableGlyphCount
                        );
                    if(hr==0)
                    {
                        glyphCount=availableGlyphCount;
                        break;
                    }
                    else if(hr==E_PENDING)
                    {
                        dcParameter=dc;
                    }
                    else if(hr==E_OUTOFMEMORY)
                    {
                        if(resizeGlyphData)
                        {
                            glyphCount+=length;
                        }
                        else
                        {
                            goto BUILD_UNISCRIBE_DATA_FAILED;
                        }
                    }
                    else
                    {
                        goto BUILD_UNISCRIBE_DATA_FAILED;
                    }
                }
                if(resizeGlyphData)
                {
                    glyphs.Resize(glyphCount);
                    glyphVisattrs.Resize(glyphCount);
                }
            }
            {
                // generate place information
                WinDC* dcParameter=0;
                if(resizeGlyphData)
                {
                    glyphAdvances.Resize(glyphCount);
                    glyphOffsets.Resize(glyphCount);
                }
                while(true)
                {
                    HRESULT hr=ScriptPlace(
                        (dcParameter?dcParameter->GetHandle():NULL),
                        &scriptCache,
                        &glyphs[0],
                        glyphCount,
                        &glyphVisattrs[0],
                        &scriptItem->a,
                        &glyphAdvances[0],
                        &glyphOffsets[0],
                        &runAbc
                        );
                    if(hr==0)
                    {
                        break;
                    }
                    else if(hr==E_PENDING)
                    {
                        dcParameter=dc;
                    }
                    else
                    {
                        goto BUILD_UNISCRIBE_DATA_FAILED;
                    }
                }
            }

            return true;
BUILD_UNISCRIBE_DATA_FAILED:
            return false;
        }
    };

    class ScriptRun : public Object
    {
    public:

        DocumentFragment*                documentFragment;
        SCRIPT_ITEM*                    scriptItem;
        int                                start;
        int                                length;
        const wchar_t*                    runText;

        SCRIPT_CACHE                    scriptCache;
        Array<SCRIPT_LOGATTR>            charLogattrs;
        int                                advance;
        GlyphData                        wholeGlyph;
        GlyphData                        tempGlyph;

        ScriptRun()
            :documentFragment(0)
            ,scriptItem(0)
            ,start(0)
            ,length(0)
            ,scriptCache(0)
            ,advance(0)
        {
        }

        ~ScriptRun()
        {
            ClearUniscribeData();
        }

        void ClearUniscribeData()
        {
            if(scriptCache)
            {
                ScriptFreeCache(&scriptCache);
                scriptCache=0;
            }
            charLogattrs.Resize(0);
            advance=0;
            wholeGlyph.ClearUniscribeData(0, 0);
            tempGlyph.ClearUniscribeData(0, 0);
        }

        bool BuildUniscribeData(WinDC* dc)
        {
            ClearUniscribeData();
            {
                // generate break information
                charLogattrs.Resize(length);

                HRESULT hr=ScriptBreak(
                    runText,
                    length,
                    &scriptItem->a,
                    &charLogattrs[0]
                    );
                if(hr!=0)
                {
                    goto BUILD_UNISCRIBE_DATA_FAILED;
                }
            }

            dc->SetFont(documentFragment->fontObject);
            if(!wholeGlyph.BuildUniscribeData(dc, documentFragment, scriptItem, scriptCache, runText, length))
            {
                goto BUILD_UNISCRIBE_DATA_FAILED;
            }
            tempGlyph.ClearUniscribeData(wholeGlyph.glyphs.Count(), length);
            advance=wholeGlyph.runAbc.abcA+wholeGlyph.runAbc.abcB+wholeGlyph.runAbc.abcC;

            return true;
BUILD_UNISCRIBE_DATA_FAILED:
            ClearUniscribeData();
            return false;
        }

        int SumWidth(int charStart, int charLength)
        {
            int cluster=wholeGlyph.charCluster[charStart];
            int nextCluster
                =charStart+charLength==length
                ?wholeGlyph.glyphs.Count()
                :wholeGlyph.charCluster[charStart+charLength];
            int width=0;
            for(int i=cluster;i<nextCluster;i++)
            {
                width+=wholeGlyph.glyphAdvances[i];
            }
            return width;
        }

        void SearchForLineBreak(int tempStart, int maxWidth, bool firstRun, int& charLength, int& charAdvances)
        {
            int width=0;
            charLength=0;
            charAdvances=0;
            for(int i=tempStart;i<=length;)
            {
                if(i==length || charLogattrs[i].fSoftBreak==TRUE)
                {
                    if(width<=maxWidth || (firstRun && charLength==0))
                    {
                        charLength=i-tempStart;
                        charAdvances=width;
                    }
                    else
                    {
                        return;
                    }
                }
                if(i==length) break;

                int cluster=wholeGlyph.charCluster[i];
                int clusterLength=1;
                while(i+clusterLength<length)
                {
                    if(wholeGlyph.charCluster[i+clusterLength]==cluster)
                    {
                        clusterLength++;
                    }
                    else
                    {
                        break;
                    }
                }

                int nextCluster
                    =i+clusterLength==length
                    ?wholeGlyph.glyphs.Count()
                    :wholeGlyph.charCluster[i+clusterLength];
                for(int j=cluster;j<nextCluster;j++)
                {
                    width+=wholeGlyph.glyphAdvances[j];
                }
                i+=clusterLength;
            }
        }

        bool BuildUniscribeDataTemp(WinDC* dc, int tempStart, int tempLength)
        {
            return tempGlyph.BuildUniscribeData(dc, documentFragment, scriptItem, scriptCache, runText+tempStart, tempLength);
        }
    };

    class ScriptLine : public Object
    {
    public:
        List<Ptr<DocumentFragment>>        documentFragments;
        WString                            lineText;

        Array<SCRIPT_ITEM>                scriptItems;
        List<Ptr<ScriptRun>>            scriptRuns;

        void CLearUniscribeData()
        {
            scriptItems.Resize(0);
            scriptRuns.Clear();
        }

        bool BuildUniscribeData(WinDC* dc)
        {
            lineText=L"";
            CLearUniscribeData();

            FOREACH(Ptr<DocumentFragment>, fragment, documentFragments.Wrap())
            {
                lineText+=fragment->text;
            }

            if(lineText!=L"")
            {
                {
                    // itemize a line
                    scriptItems.Resize(lineText.Length()+2);
                    int scriptItemCount=0;
                    HRESULT hr=ScriptItemize(
                        lineText.Buffer(),
                        lineText.Length(),
                        scriptItems.Count()-1,
                        NULL,
                        NULL,
                        &scriptItems[0],
                        &scriptItemCount
                        );
                    if(hr!=0)
                    {
                        goto BUILD_UNISCRIBE_DATA_FAILED;
                    }
                    scriptItems.Resize(scriptItemCount+1);
                }
                {
                    // use item and document fragment information to produce runs
                    // one item is constructed by one or more runs
                    // characters in each run contains the same style
                    int fragmentIndex=0;
                    int fragmentStart=0;
                    for(int i=0;i<scriptItems.Count()-1;i++)
                    {
                        SCRIPT_ITEM* scriptItem=&scriptItems[i];
                        int start=scriptItem[0].iCharPos;
                        int length=scriptItem[1].iCharPos-scriptItem[0].iCharPos;
                        int currentStart=start;

                        while(currentStart<start+length)
                        {
                            DocumentFragment* fragment=0;
                            int itemRemainLength=length-(currentStart-start);
                            int fragmentRemainLength=0;
                            while(true)
                            {
                                fragment=documentFragments[fragmentIndex].Obj();
                                fragmentRemainLength=fragment->text.Length()-(currentStart-fragmentStart);
                                if(fragmentRemainLength<=0)
                                {
                                    fragmentStart+=fragment->text.Length();
                                    fragmentIndex++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            int shortLength=itemRemainLength<fragmentRemainLength?itemRemainLength:fragmentRemainLength;

                            Ptr<ScriptRun> run=new ScriptRun;
                            run->documentFragment=fragment;
                            run->scriptItem=scriptItem;
                            run->start=currentStart;
                            run->length=shortLength;
                            run->runText=lineText.Buffer()+currentStart;
                            scriptRuns.Add(run);
                            currentStart+=shortLength;
                        }
                    }

                    // for each run, generate shape information
                    FOREACH(Ptr<ScriptRun>, run, scriptRuns.Wrap())
                    {
                        if(!run->BuildUniscribeData(dc))
                        {
                            goto BUILD_UNISCRIBE_DATA_FAILED;
                        }
                    }
                }
            }
            return true;
BUILD_UNISCRIBE_DATA_FAILED:
            CLearUniscribeData();
            return false;
        }
    };

    class ScriptParagraph : public Object
    {
    public:
        List<Ptr<ScriptLine>>            lines;
    };

    class ScriptDocument : public Object
    {
    public:
        List<Ptr<ScriptParagraph>>        paragraphs;
    };

    Ptr<ScriptDocument> BuildScriptParagraphs(List<Ptr<DocumentFragment>>& fragments)
    {
        Ptr<ScriptDocument> document=new ScriptDocument;
        document->paragraphs.Clear();
        Regex regex(L"\r\n");
        Ptr<ScriptParagraph> currentParagraph;
        Ptr<ScriptLine> currentLine;
        Dictionary<WString, Ptr<WinFont>> fonts;

        FOREACH(Ptr<DocumentFragment>, fragment, fragments.Wrap())
        {
            WString fragmentFingerPrint=fragment->GetFingerPrint();
            int index=fonts.Keys().IndexOf(fragmentFingerPrint);
            if(index==-1)
            {
                fragment->fontObject=new WinFont(fragment->font, fragment->size, 0, 0, 0, (fragment->bold?FW_BOLD:FW_NORMAL), false, false, false, true);
                fonts.Add(fragmentFingerPrint, fragment->fontObject);
            }
            else
            {
                fragment->fontObject=fonts.Values()[index];
            }

            if(!currentParagraph)
            {
                currentParagraph=new ScriptParagraph;
                document->paragraphs.Add(currentParagraph);
            }
           
            if(fragment->paragraph)
            {
                currentParagraph=0;
                currentLine=0;
            }
            else
            {
                RegexMatch::List matches;
                regex.Split(fragment->text, true, matches);
                for(int i=0;i<matches.Count();i++)
                {
                    Ptr<RegexMatch> match=matches[i];
                    if(i>0)
                    {
                        currentLine=0;
                    }
                    if(!currentLine)
                    {
                        currentLine=new ScriptLine;
                        currentParagraph->lines.Add(currentLine);
                    }
                    currentLine->documentFragments.Add(new DocumentFragment(fragment, match->Result().Value()));
                }
            }
        }

        HDC hdc=CreateCompatibleDC(NULL);
        WinProxyDC dc;
        dc.Initialize(hdc);
        FOREACH(Ptr<ScriptParagraph>, paragraph, document->paragraphs.Wrap())
        {
            FOREACH(Ptr<ScriptLine>, line, paragraph->lines.Wrap())
            {
                line->BuildUniscribeData(&dc);
            }
        }
        DeleteDC(hdc);

        return document;
    }

/***********************************************************************
TestWindow
***********************************************************************/

    class TestWindow : public GuiWindow
    {
    protected:
        Ptr<ScriptDocument>                document;
        Ptr<WinFont>                    messageFont;

        void element_Rendering(GuiGraphicsComposition* composition, GuiGDIElementEventArgs& arguments)
        {
            WinDC* dc=arguments.dc;
            Rect bounds=arguments.bounds;
            if(document)
            {
                int x=bounds.Left()+10;
                int y=bounds.Top()+10;
                int w=bounds.Width()-20;
                int h=bounds.Height()-10;
                int cx=0;
                int cy=0;
                const int lineDistance=5;
                const int paragraphDistance=10;

                FOREACH(Ptr<ScriptParagraph>, paragraph, document->paragraphs.Wrap())
                {
                    if(cy>=h) break;
                    FOREACH(Ptr<ScriptLine>, line, paragraph->lines.Wrap())
                    {
                        if(line->scriptRuns.Count()==0)
                        {
                            // if this line doesn't contains any run, skip and render a blank line
                            cy+=line->documentFragments[0]->size+lineDistance;
                        }
                        else
                        {
                            // render this line into linces with auto line wrapping
                            int startRun=0;
                            int startRunOffset=0;
                            int lastRun=0;
                            int lastRunOffset=0;
                            int currentWidth=0;

                            while(startRun<line->scriptRuns.Count())
                            {
                                int currentWidth=0;
                                bool firstRun=true;
                                // search for a range to fit in the given width
                                for(int i=startRun;i<line->scriptRuns.Count();i++)
                                {
                                    int charLength=0;
                                    int charAdvances=0;
                                    ScriptRun* run=line->scriptRuns[i].Obj();
                                    run->SearchForLineBreak(lastRunOffset, w-currentWidth, firstRun, charLength, charAdvances);
                                    firstRun=false;

                                    if(charLength==run->length-lastRunOffset)
                                    {
                                        lastRun=i+1;
                                        lastRunOffset=0;
                                        currentWidth+=charAdvances;
                                    }
                                    else
                                    {
                                        lastRun=i;
                                        lastRunOffset=lastRunOffset+charLength;
                                        break;
                                    }
                                }

                                // if the range is empty, than this should be the end of line, ignore it
                                if(startRun<lastRun || (startRun==lastRun && startRunOffset<lastRunOffset))
                                {
                                    // calculate the max line height in this range;
                                    int maxHeight=0;
                                    for(int i=startRun;i<=lastRun && i<line->scriptRuns.Count();i++)
                                    {
                                        int size=line->scriptRuns[i]->documentFragment->size;
                                        if(maxHeight<size)
                                        {
                                            maxHeight=size;
                                        }
                                    }

                                    // render all runs inside this range
                                    for(int i=startRun;i<=lastRun && i<line->scriptRuns.Count();i++)
                                    {
                                        ScriptRun* run=line->scriptRuns[i].Obj();
                                        int start=i==startRun?startRunOffset:0;
                                        int end=i==lastRun?lastRunOffset:run->length;
                                        int length=end-start;
                                           
                                        Color color=run->documentFragment->color;
                                        dc->SetFont(run->documentFragment->fontObject);
                                        dc->SetTextColor(RGB(color.r, color.g, color.b));
                                        dc->DrawBuffer(x+cx, y+cy+(maxHeight-run->documentFragment->size), run->runText+start, length);

                                        cx+=run->SumWidth(start, length);
                                    }

                                    cx=0;
                                    cy+=maxHeight+lineDistance;
                                }

                                startRun=lastRun;
                                startRunOffset=lastRunOffset;
                            }
                        }
                    }
                    cy+=paragraphDistance;
                }
            }
            else
            {
                dc->SetFont(messageFont);
                WString message=L"Initializing uniscribe data...";
                SIZE size=dc->MeasureString(message);
                int x=bounds.Left()+(bounds.Width()-size.cx)/2;
                int y=bounds.Top()+(bounds.Height()-size.cy)/2;
                dc->DrawString(x, y, message);
            }
        }
    public:
        TestWindow()
            :GuiWindow(GetCurrentTheme()->CreateWindowStyle())
        {
            SetText(L"GacUISrc Test Application");
            SetClientSize(Size(640, 480));
            GetBoundsComposition()->SetPreferredMinSize(Size(320, 240));
            MoveToScreenCenter();
            {
                GuiGDIElement* element=GuiGDIElement::Create();
                element->Rendering.AttachMethod(this, &TestWindow::element_Rendering);
           
                GuiBoundsComposition* composition=new GuiBoundsComposition;
                composition->SetOwnedElement(element);
                composition->SetAlignmentToParent(Margin(0, 0, 0, 0));
                GetContainerComposition()->AddChild(composition);

                messageFont=new WinFont(L"Segoe UI", 56, 0, 0, 0,FW_NORMAL, false, false, false, true);
            }
            GetApplication()->InvokeAsync([=]()
            {
                List<Ptr<DocumentFragment>> fragments;
                BuildDocumentFragments(L"..\\GacUISrcCodepackedTest\\Resources\\document.txt", fragments);
                Ptr<ScriptDocument> scriptDocument=BuildScriptParagraphs(fragments);
                GetApplication()->InvokeInMainThreadAndWait([=]()
                {
                    document=scriptDocument;
                });
            });
        }
    };
}
using namespace test;

void GuiMain()
{
    TestWindow window;
    GetApplication()->Run(&window);
}

posted on 2012-11-06 06:34 陳梓瀚(vczh) 閱讀(5175) 評(píng)論(5)  編輯 收藏 引用 所屬分類: C++2DGacUI

評(píng)論:
# re: C++使用Uniscribe進(jìn)行文字自動(dòng)換行的計(jì)算和渲染 2012-11-06 20:31 | 陳昱(CY)
文字果然好復(fù)雜。。。  回復(fù)  更多評(píng)論
  
# re: C++使用Uniscribe進(jìn)行文字自動(dòng)換行的計(jì)算和渲染 2012-11-07 00:00 | iunkown
好厲害。文本排版果然很復(fù)雜  回復(fù)  更多評(píng)論
  
# re: C++使用Uniscribe進(jìn)行文字自動(dòng)換行的計(jì)算和渲染[未登錄](méi) 2012-11-08 06:08 | megax
我覺(jué)得排版這東西還得自己來(lái)做, 記得Uniscribe的主要目的是繪制復(fù)雜字形(帶有連筆)的, 排版應(yīng)該是副產(chǎn)物:-), 現(xiàn)在應(yīng)該被directwrite代替了吧!  回復(fù)  更多評(píng)論
  
# re: C++使用Uniscribe進(jìn)行文字自動(dòng)換行的計(jì)算和渲染 2012-11-09 07:36 | 陳梓瀚(vczh)
@megax
沒(méi)有這些信息自己根本不可能做好的。排版的目的太多了,譬如說(shuō)正確的順序顯示阿拉伯文字啦。舉個(gè)例子,假設(shè)abcde是LTR,ABCDE是RTL,那么字符串ABCDE abcde會(huì)被顯示為abcde EDCBA。這種事情都要自己來(lái)那太他媽麻煩了,所以讓uniscribe先把這些亂七八糟的東西先算好,然后自己來(lái)確定文字的pixel位置就行了。  回復(fù)  更多評(píng)論
  
# re: C++使用Uniscribe進(jìn)行文字自動(dòng)換行的計(jì)算和渲染 2013-08-18 22:06 | bombless
Uniscribe那個(gè)東西好像在《Windows編程默示錄》里面教過(guò)……  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久99久久精品女同性| 国产精品xxxxx| 亚洲欧洲免费视频| 久久高清国产| 欧美影院精品一区| 国产视频一区在线观看| 亚洲视频香蕉人妖| 亚洲在线国产日韩欧美| 国产精品久久久久7777婷婷| 中文精品在线| 欧美在线电影| 国内外成人免费激情在线视频| 亚洲欧美美女| 美女视频一区免费观看| 亚洲国产高清视频| 欧美人与性动交a欧美精品| 亚洲激情在线视频| 在线观看日韩av电影| 欧美中文字幕在线视频| 国产人成一区二区三区影院| 欧美中文字幕视频| 亚洲人成艺术| 久久国产精品99国产精| 亚洲国产影院| 国产精品日产欧美久久久久| 久久国产精品一区二区| 亚洲欧洲精品一区二区三区 | 国产精品九九久久久久久久| 亚洲一区日韩| 美女视频一区免费观看| 亚洲伊人久久综合| 欧美高清视频在线| 亚洲精品乱码久久久久久按摩观| 亚洲人体1000| 国产亚洲成av人在线观看导航| 午夜宅男久久久| 欧美电影在线观看| 欧美亚洲一区二区在线观看| 亚洲国产成人久久综合一区| 国产精品成人免费视频| 免费观看亚洲视频大全| 亚洲一区中文| 亚洲三级色网| 欧美国产一区二区| 久久久视频精品| 一区二区欧美日韩视频| 久久蜜桃精品| 欧美成人一区二区三区片免费| 国产精品99久久久久久人| 国产乱码精品一区二区三区忘忧草| 欧美1区2区视频| 欧美在线观看视频在线| 在线亚洲一区二区| 亚洲区免费影片| 欧美二区不卡| 欧美 日韩 国产一区二区在线视频 | 国产精品porn| 欧美激情91| 久久精品二区| 午夜精品久久久久久久久久久久久| 亚洲区国产区| 亚洲国产精品精华液网站| 久久午夜精品一区二区| 久久精品国产在热久久 | 欧美一区二区久久久| 亚洲视频在线观看视频| 亚洲精品裸体| 亚洲激情电影在线| 亚洲国产欧美久久| 影音先锋中文字幕一区| 狠狠色丁香久久婷婷综合丁香| 国产精品欧美久久| 欧美精品国产| 欧美日韩一区二区精品| 欧美精品在线免费播放| 欧美电影在线| 欧美日韩精品免费观看视一区二区 | 国产日韩欧美另类| 欧美天天在线| 国产精品国产三级欧美二区| 欧美午夜视频在线| 欧美日本一区二区视频在线观看| 亚洲欧美日韩人成在线播放| 午夜在线一区| 另类亚洲自拍| 欧美va天堂在线| 久久久999国产| 免费亚洲电影在线| 国产精品v日韩精品v欧美精品网站| 国产精品自在线| 欧美日韩卡一卡二| 日韩视频在线一区二区| 91久久中文字幕| 欧美淫片网站| 亚洲在线日韩| 久久国产精品一区二区三区| 久久精品一级爱片| 欧美大色视频| 亚洲国产精品成人| 亚洲高清久久| 亚洲图片自拍偷拍| 欧美一区二区日韩| 欧美.日韩.国产.一区.二区| 欧美成人精品高清在线播放| 欧美图区在线视频| 影音先锋久久资源网| 一区二区欧美日韩视频| 久久久久欧美| 一区二区国产精品| 巨乳诱惑日韩免费av| 欧美成人小视频| 亚洲人成网在线播放| 久久免费午夜影院| 欧美伦理一区二区| 伊人夜夜躁av伊人久久| 亚洲性av在线| 欧美~级网站不卡| 午夜精品久久久久久久99水蜜桃 | 亚洲午夜在线视频| 久久夜色精品| 一区二区三区成人精品| 麻豆freexxxx性91精品| 国产日韩欧美a| 亚洲一二三级电影| 久久综合中文| 一区二区三区日韩欧美| 欧美国产三级| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲美女性视频| 欧美一区二区三区精品电影| 久久se精品一区二区| 欧美日韩国产小视频在线观看| 久久久噜噜噜久久中文字幕色伊伊| 精品av久久707| 一本到12不卡视频在线dvd| 欧美一级久久| 国产精品久久久久一区二区| 永久555www成人免费| 日韩视频―中文字幕| 久久久精彩视频| 亚洲国产激情| 亚洲一区中文| 亚洲视频大全| 欧美日韩国产色综合一二三四 | 欧美不卡视频一区发布| 国产精品外国| 亚洲人成免费| 久久国产精品久久久久久久久久| 亚洲国产精品一区二区第一页| 亚洲综合色噜噜狠狠| 免费成人av在线| 国产嫩草一区二区三区在线观看 | 亚洲网在线观看| 亚洲国产欧美国产综合一区| 亚洲国产一区二区三区a毛片| 午夜精品区一区二区三| 欧美性猛片xxxx免费看久爱| 亚洲精品久久久久久一区二区| 久久久国产精品一区二区中文| 最新中文字幕亚洲| 欧美激情五月| 亚洲伦理久久| 亚洲免费不卡| 国产亚洲观看| 亚洲国产成人一区| 欧美午夜不卡影院在线观看完整版免费| 一区二区精品| 亚洲一区二区欧美| 国产一区二区日韩精品| 国产精品99久久久久久久久| 亚洲一区精品视频| 亚洲日本视频| 欧美另类视频| 亚洲视频综合| 亚洲欧美在线一区| 国内精品视频久久| 久久久久网址| 美女性感视频久久久| 99精品免费| 一区电影在线观看| 极品尤物一区二区三区| 欧美电影免费观看高清| 欧美日韩免费观看一区二区三区 | 国产精品一区久久久久| 久久综合中文色婷婷| 欧美精品一卡二卡| 午夜亚洲激情| 欧美v国产在线一区二区三区| 亚洲视频1区| 久久久91精品| 亚洲免费成人| 亚洲男女毛片无遮挡| 在线免费观看日韩欧美| 一区二区欧美国产| 精品动漫3d一区二区三区免费| 亚洲级视频在线观看免费1级| 国产精品一二三四| 欧美黄免费看| 国产视频一区在线观看一区免费| 99精品免费视频|