要給項(xiàng)目中增加一個(gè)新的模塊,需要先在服務(wù)器端做一些圖片處理相關(guān)的工作。本來(lái),對(duì)圖片
做一些諸如ALPHA混合旋轉(zhuǎn)縮放的操作,在游戲客戶端應(yīng)該是很容易的事。但是這事要在服務(wù)
器做,就不得不引入一些第三方庫(kù)。反正我們的服務(wù)器運(yùn)行于WINDOWS下,這里又需要處理
JPG圖片的加載,我就考慮到了GDI+。
在這之前對(duì)GDI+沒(méi)有過(guò)任何接觸。直接翻了MSDN,還好居然有個(gè)一系列的usage。GDI+的Image
本身支持JPG的直接載入。但是并沒(méi)有我理想中的CreateFromMemory( const void *buf )接口。
看起來(lái)唯一可以從內(nèi)存創(chuàng)建Gdiplus::Image對(duì)象的方法是從一個(gè)叫IStream*的COM東西。我揣摩
微軟為什么沒(méi)有提供我理想中的那個(gè)接口,或者說(shuō)要把GDI+設(shè)計(jì)成這樣,可能還是考慮到對(duì)多語(yǔ)
言的支持。于是問(wèn)題轉(zhuǎn)換為如何將一個(gè)C語(yǔ)言的const void*轉(zhuǎn)換為IStream*。我甚至在開(kāi)始的時(shí)候
感覺(jué)到是不是要自己實(shí)現(xiàn)個(gè)Stream。后來(lái)在google上找到了一個(gè)似乎是標(biāo)準(zhǔn)的方法:首先創(chuàng)建個(gè)
HGLOBAL對(duì)象,然后通過(guò)GlobalLock就可以將一個(gè)C的const void*直接memcpy到這個(gè)HGLOBAL
里,最后,通過(guò)CreateStreamOnHGlobal這樣的接口就可以得到一個(gè)IStream。
惡心的是,基于之前對(duì)服務(wù)器內(nèi)存使用的優(yōu)化,我現(xiàn)在對(duì)于內(nèi)存的使用非常敏感(誰(shuí)說(shuō)現(xiàn)在內(nèi)存
大了就可以任意malloc了??)。上面那個(gè)過(guò)程對(duì)于資源的管理在MSDN文檔中似乎顯得有點(diǎn)
模糊。CreateStreamOnHGlobal函數(shù)的第二個(gè)參數(shù)指定當(dāng)IStream->Release的時(shí)候,是否會(huì)自動(dòng)
刪除這個(gè)HGLOBAL對(duì)象。我雖然對(duì)COM不懂,但也知道它的對(duì)象是基于一種引用計(jì)數(shù)的管理方式。
逐字看了下文檔,發(fā)現(xiàn)一個(gè)final單詞,原來(lái)是IStream->Release最后一次釋放時(shí),會(huì)同時(shí)釋放掉
這個(gè)HGLOBAL對(duì)象。更讓人發(fā)指的是,我猜測(cè)Image( IStream * )來(lái)創(chuàng)建Image時(shí),Image又
會(huì)對(duì)這個(gè)IStream進(jìn)行一次AddRef。我發(fā)覺(jué)MSDN對(duì)于Gdiplus::Image::FromStream函數(shù)的說(shuō)明
也有點(diǎn)模糊。我揣摩使用FromStream獲得的Image*,是否需要手動(dòng)去delete?這個(gè)地方的內(nèi)存
資源管理,一定得搞個(gè)水落石出。結(jié)果是,F(xiàn)romStream的實(shí)現(xiàn)就是簡(jiǎn)單地new了個(gè)Image。而
Image內(nèi)部肯定會(huì)對(duì)IStream進(jìn)行AddRef,并且,如果在Image銷毀前銷毀這個(gè)HGLOBAL,這個(gè)
Image基本也就廢了。
也就是說(shuō),Image本身不對(duì)HGLOBAL中的圖片數(shù)據(jù)進(jìn)行復(fù)制。囧。別想讓我再寫個(gè)wrap class把
HGLOBAL和Image糾結(jié)在一起,簡(jiǎn)單考慮,將CreateStreamOnHGlobal第二個(gè)參數(shù)設(shè)為TRUE。
要將一個(gè)Image保存為一段內(nèi)存,也比較麻煩。我的方法和google上的相同。當(dāng)然,微軟的庫(kù)依
然讓我在很多細(xì)節(jié)上栽跟斗(如前所說(shuō),可能這是基于多語(yǔ)言支持的考慮)。首先需要?jiǎng)?chuàng)建個(gè)空
的IStream,即CreateStreamOnHGlobal第一個(gè)參數(shù)為NULL。然后將Image Save到這個(gè)IStream。
再根據(jù)該IStream::Seek獲取其大小,自己再分配段內(nèi)存,最后IStream::Read讀取進(jìn)來(lái)。同樣,
需要注意相關(guān)內(nèi)存資源的管理。
下午簡(jiǎn)單把以上兩個(gè)過(guò)程簡(jiǎn)單封裝了下。
下載代碼。