手頭有個工程,需要服務端把一系列TBitmap,用SaveToStream方法連續寫入一個流里面,然后客戶端從流里面用loadfromstream讀出一個個獨立的TBitmap。但是實際操作中發現,TBitmap的Loadfromstream這個方法,是一口氣讀到底的,也就是說要用這個方法,我需要先創建一個臨時的流,讀取其中一段資料出來,然后供TBitmap來loadfromstream,舉例如下:
服務端:
bmp:TBitmap;
bmp.savetostream(stream);
//...
//...寫入多個bitmap到流里面,當然中間會有一些分割標志
客戶端:
bmp:TBitmap
tmp:TMemoryStream; //定義一個臨時流
tmp.copyfrom(stream,10000); //用臨時流讀取一段資料
bmp.loadfromstream(tmp); //bmp從臨時流載入
上面的代碼,中間不得不借用一個臨時流,顯得很繁瑣,全都因為TBitmap的loadfromstream這個方法,居然不支持size。查看源代碼,loadfromstream呼叫的是TBitmap的私有方法Readstream:
procedure TBitmap.LoadFromStream(Stream: TStream);
begin
ReadStream(Stream, Stream.Size - Stream.Position);
end;
太缺德了,居然ReadStream是支持size的,結果這里把size引數寫死成stream的剩余全部內容了!!
于是我準備修改TBitmap這個類,想法很簡單,加一個LoadFromStream2方法,增加一個size引數即可:
procedure TBitmap.LoadFromStream2(Stream: TStream; ASize:integer);
begin
ReadStream(Stream, ASize);
end;
可是事情遠沒有我想的那么簡單,Tbitmap是如此底層的一個類,可以說是整個vcl最重要的一個基礎類,牽一發而動全身,所有涉及到bitmap的系統單元都要重新編譯,別的還好,jpeg單元沒有原始碼,無法編譯,而我的工程又需要jpeg這個單元,最后只好放棄這個辦法。
于是又想從TBitmap繼承一個類,實作我的LoadFromStream2的方法。但是嘗試之后才發現,TBitmap的readfrom居然是個私有方法,子類無法訪問!!!
那么現在問題來了: 我就想在TBitmap的基礎上,實作帶size引數的LoadFromStream,我該怎么做?求助各位大師了~~~
uj5u.com熱心網友回復:
這個有辦法,先說一下,什么版本,開發的是什么環境的軟體,如:Delphi ,Win32或者 XE5,Win32,Win64這樣的uj5u.com熱心網友回復:
直接使用二進制,傳遞影像資料即可。uj5u.com熱心網友回復:
覆寫Stream的GetSizeuj5u.com熱心網友回復:
delphi 6.0, winxp
uj5u.com熱心網友回復:
這個我倒是想過,比如通過scanline直接把記憶體資料復制出來傳過去,接收后再copymemory到目標圖片記憶體區。 不過我還沒試驗過,這樣處理,是否比bitmap的savetopstream和loadfromstream效率低? 因為我這個對速度要求比較高。
uj5u.com熱心網友回復:
具體怎樣做呢? stream也是如此底層的一個類,會不會又遇到繼承tbitmap的老問題,牽一發而動全身?
uj5u.com熱心網友回復:
tmp.copyfrom(stream,10000); //用臨時流讀取一段資料這里的stream是什么類就改這個類或者從他繼承
uj5u.com熱心網友回復:
流啊,也夠底層的,就怕又是牽一發而動全身
uj5u.com熱心網友回復:
D6呀,把procedure TBitmap.ReadData(Stream: TStream);后面的代碼發上一下,我這里沒有D6,只有D7
uj5u.com熱心網友回復:

看用的啥版本,高版本的可以用類補丁
要通用的話,就是自己在自己的單元頭部吧TBitmap重新繼承一個,比如
TBitmap = class(Graphics.TBitmap)
//增加你的方法
//或者多載你的虛方法
end;
這樣這個Bitmap就可以只在你參考了你的這個單元有效
uj5u.com熱心網友回復:
首先,必須說,沒有樓主你所謂的“太缺德了,居然ReadStream是支持size的,結果這里把size引數寫死成stream的剩余全部內容了”。ReadStream引數的那個size只是為了避免越界傳入的一個限制,具體怎么讀的你要去看ReadDIB方法:
// biSizeImage can be zero. If zero, compute the size.
if biSizeImage = 0 then // top-down DIBs have negative height
biSizeImage := BytesPerScanLine(biWidth, biBitCount, 32) * Abs(biHeight);
if biSizeImage < ImageSize then ImageSize := biSizeImage;
看見了吧,它是根據讀到的位圖頭的影像資料大小(如果為0則自己計算)來具體確定位圖大小的,沒有一讀到底這回事。
其次,可以簡單寫個小程式驗證一下:
先建立一個多bitmap檔案:
copy /b 1.bmp+2.bmp f.bin
然后:
var
f: TFileStream;
b1, b2: TBitmap;
//...
f := TFileStream.Create('f.bin', fmOpenRead);
b1 := TBitmap.Create;
b2 := TBitmap.Create;
b1.LoadFromStream(f);
b2.LoadFromStream(f);
b1.SaveToFile('b1.bmp');
b2.SaveToFile('b2.bmp');
f.Free;
b1.Free;
b2.Free;
你看看取到的兩個bitmap正不正確。
uj5u.com熱心網友回復:
佩服!的確如你所說!并沒有像我說的那樣一讀到底,內部代碼是根據讀取的bitmapheader的檔案實際尺寸讀的!

不過我后來已經把代碼改了,只傳輸scanline的記憶體資料,傳輸量少了,而且少了很多越界判斷,效率似乎更好一些。
uj5u.com熱心網友回復:
我也遇到類似問題,但解決了覺得你們的的解決方法繞彎了,沒你們想象的那么嚴重,注意讀流之前一定要把起始位置設定好,否則就可能報錯啦,因為可能在程式別的地方讀此流后,沒有歸位,然后你再次讀流時,位置就不是起始位置了
我的解決方法是
aStream.Position := 0;
aBmp := TBitmap.Create;
aBmp.LoadFromStream(aStream);
測驗ok了
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/69299.html
標籤:VCL組件開發及應用
上一篇:解釋一下Delphi中的模塊
