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

假定1-5全部都是GIF圖片,非GIF可以暫時無視,這個后面大家會非常清楚如何處理。在這個過程中,2不見了,而4是新出現的。對于4新出現時,RichEdit自身肯定會觸發其:
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圖片進入可視區,可以把它添加到集合中。對于2的動畫觸發時間到來時,我們可以確定其位置且與可視區比對,發現其不再可視區,則從集合中移除。這樣就可以得到一個接近于(略大于)當前視口中的動畫控件集合,當有新的動畫觸發時間到來時,我們可以先檢查其是否在可視區,如果不在則不用GDI操作,僅僅更新其當前幀。當然這些工作你也可以不做,但是在動畫控件數量大的時候效率可能略有下降,主要是查找的過程(索引、位置)比較耗時。
如何確定一個OLE的位置呢?由于我們插入OLE都使用了REO_BELOWBASELINE標志,也就是跟當前行的底部對齊,所以OLE左下角位置的精確度對我們來說很重要。看下圖:

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