最近工作上比較忙,加之編碼任務(wù)較多,沒來得及繼續(xù)之前的講解。抽出時間把這最重要的一部分東西做個闡述。行文以基本的編程思維及個人思考過程為線索。
眾所周知,RichEdir強(qiáng)大在于其圖文混排(在這里不跟Word、HTML比),其中的圖替換為動態(tài)圖的核心問題就歸結(jié)于如何高效刷新。我們知道GDI操作是最消耗CPU的,所以刷新整個RichEdit窗口是不可取的,其副作用會導(dǎo)致更嚴(yán)重的閃爍問題。解決問題的思路很簡單:類似于拖拽時候在屏幕繪制異或線,我們的動畫重繪時不請求RichEdit,而直接在其窗口的DC上繪制當(dāng)前動畫幀,此時缺少是如何確定該OLE的位置,這個是所有問題的關(guān)鍵。先看下面這幅圖:

假定1-5全部都是GIF圖片,非GIF可以暫時無視,這個后面大家會非常清楚如何處理。在這個過程中,2不見了,而4是新出現(xiàn)的。對于4新出現(xiàn)時,RichEdit自身肯定會觸發(fā)其:
Draw(
DWORD dwDrawAspect, LONG lindex, void* pvAspect,
DVTARGETDEVICE* ptd, HDC hicTargetDev, HDC hdcDraw,
LPCRECTL prcBounds, LPCRECTL prcWBounds,
BOOL (__stdcall *pfnContinue)(DWORD_PTR dwContinue),
DWORD_PTR dwContinue)
這個時候,我們知道新的GIF圖片進(jìn)入可視區(qū),可以把它添加到集合中。對于2的動畫觸發(fā)時間到來時,我們可以確定其位置且與可視區(qū)比對,發(fā)現(xiàn)其不再可視區(qū),則從集合中移除。這樣就可以得到一個接近于(略大于)當(dāng)前視口中的動畫控件集合,當(dāng)有新的動畫觸發(fā)時間到來時,我們可以先檢查其是否在可視區(qū),如果不在則不用GDI操作,僅僅更新其當(dāng)前幀。當(dāng)然這些工作你也可以不做,但是在動畫控件數(shù)量大的時候效率可能略有下降,主要是查找的過程(索引、位置)比較耗時。
如何確定一個OLE的位置呢?由于我們插入OLE都使用了REO_BELOWBASELINE標(biāo)志,也就是跟當(dāng)前行的底部對齊,所以O(shè)LE左下角位置的精確度對我們來說很重要??聪聢D:

假設(shè)圖中黑框是一個OLE對象,其字符索引為CPN,假定第N+1行的第一個字符索引為CPN1,那么OLE左下角坐標(biāo)={PosFromChar(CPN).x, PosFromChar(CPN1).y },PosFromChar這個是RichEdit提供的。問題的關(guān)鍵是最后一行怎么計算?此時沒有第N+1行。對于這種特殊情況,主要是Y坐標(biāo)的計算,可以這樣考慮:Y=RichEdit內(nèi)容高度-滾動條位置。猜測: 計算內(nèi)容高度可能比較耗時,故QQ的聊天消息顯示部分強(qiáng)制在底部加了一行,以避免這種情況出現(xiàn)。
得到左下角位置以后,可能你會覺得就萬事大吉了。錯!還有一個關(guān)鍵點!我們可以通過OLE的接口GetExtent得到其大小,然而這個大小沒有考慮縮放比例,所以你需要根據(jù)當(dāng)前縮放比例進(jìn)行計算,而這個計算牽扯到浮點數(shù)運算,過程中的來回不僅麻煩而且不精確,所以O(shè)LE的可視大小要想非常精確是不能通過計算來的。我們前面知道OLE繪制的時候會傳入可視范圍,假如我們保存下來是不是就可以解決問題了呢?當(dāng)然,顯然,你可以試試!
這些問題主要原因是RichEdit的很多接口方法沒有暴露,而Win8的SDK會做重大升級,很多之前的問題都會變成不是問題,或許還會引起更多的新特性,但是動畫本身的邏輯還是需要自己實現(xiàn),或者會簡單許多,至于多少我還尚不清楚,但是目前來看這種方案效率足夠!
到了這里,核心技術(shù)應(yīng)該大白天下,整個過程,我追求了位置的精準(zhǔn)度,并據(jù)此獲得最小可視集合進(jìn)行刷新優(yōu)化。
最新SDK&Demo,參見:http://code.google.com/p/im-solution/。希望你會喜歡!
posted on 2012-09-08 18:10
萬連文 閱讀(4810)
評論(16) 編輯 收藏 引用 所屬分類:
小作品 、
richedit