全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:4691
推到 Plurk!
推到 Facebook!

如何顯示一個很大的bmp圖檔

尚未結案
sam_000
一般會員


發表:27
回覆:47
積分:14
註冊:2003-09-15

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-02-14 16:52:03 IP:219.80.xxx.xxx 未訂閱
各位前輩新年快樂 我在image上顯示一個從掃描器上掃到的一個很大的bmp圖檔(超過50M),總顯示記憶體不足的ERROR,有辦法解決嗎? 大過年的時候提出問題還請前輩們包涵,謝謝 sam上
hagar
版主


發表:143
回覆:4056
積分:4445
註冊:2002-04-14

發送簡訊給我
#2 引用回覆 回覆 發表時間:2005-02-14 21:05:22 IP:202.39.xxx.xxx 未訂閱
參考這篇: http://groups.google.com/groups?hl=zh-TW&lr=&ie=UTF-8&th=17cc47b29c7a86d6&rnum=1 下載 TBigBitmap 來用試試
wameng
版主


發表:31
回覆:1336
積分:1188
註冊:2004-09-16

發送簡訊給我
#3 引用回覆 回覆 發表時間:2005-02-15 21:02:47 IP:61.31.xxx.xxx 未訂閱
補充:    事實上,會出現記憶體不足的ERROR 。 Sometime 並不是真的不足。主要是GDI 資源不足的問題。 尤其主要發生在Win9x/Me 的作業系統下。 若換為JPEG 格式,就可能不會了。    我記得Delphi 有一本書。(日本人寫的) 內容有提到原因。    我用Google 找到下列源碼。您可以參考他的作法。 是利用只載入顯示有看到的部分。
unit Unit1;    interface    uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, ExtDlgs;    type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    Panel1: TPanel;
    Button1: TButton;
    Label1: TLabel;
    procedure PaintBox1Paint(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
    FBitmap: TBitmap;
    FFileName: string;
  public
    { Public declarations }
  end;    var
  Form1: TForm1;    implementation    {$R *.DFM}    Function MyGetMem( size: DWORD ): pointer;
Begin
  result := Pointer(GlobalAlloc(GPTR, size));
End;    Procedure MyFreeMem( p: pointer );
Begin
  If p = Nil Then Exit;
  GlobalFree(THandle(p));
End;    { This code will fill a bitmap by resampling an image coming from a big
bitmap on disk.      FileName.- Name of the uncompressed bitmap to read
  DestBitmap.- Target bitmap  where the bitmap on disk will be resampled.
  BufferSize.- The size of a memory buffer used for reading scanlines from
the physical bitmap on disk.
    This value will decide how many scanlines can be read from disk at the
same time, with always a
    minimum value of 2 scanlines.      Will return false on error.
}
Function GetDIBInBands( Const FileName: String;
  DestBitmap: TBitmap; BufferSize: integer;
  Out TotalBitmapWidth, TotalBitmapHeight: Integer): Boolean;
Var
  FileSize: integer;  // calculated file size
  ImageSize: integer;  // calculated image size
  dest_MaxScans: integer; // number of scanline from source bitmap
  dsty_top: Integer;  // used to calculate number of passes
  NumPasses: integer;  // number of passed needed
  dest_Residual: integer; // number of scanlines on last band
  Stream: TStream;  // stream used for opening the bitmap
  bmf: TBITMAPFILEHEADER; // the bitmap header
  lpBitmapInfo: PBITMAPINFO; // bitmap info record
  BitmapHeaderSize: integer; // size of header of bitmap
  SourceIsTopDown: Boolean; // is reversed bitmap ?
  SourceBytesPerScanLine: integer;  // number of bytes per scanline
  SourceLastScanLine: Extended;     // last scanline processes
  SourceBandHeight: Extended;       //
  BitmapInfo: PBITMAPINFO;
  img_start: integer;
  img_end: integer;
  img_numscans: integer;
  OffsetInFile: integer;
  OldHeight: Integer;
  bits: Pointer;
  CurrentTop: Integer;
  CurrentBottom: Integer;
Begin
  result := False;      // open the big bitmap
  Stream := TFileStream.Create( FileName, fmOpenRead Or fmShareDenyWrite );      // total size of bitmap
  FileSize := Stream.Size;
  // read the header
  Stream.ReadBuffer( bmf, sizeof( TBITMAPFILEHEADER ) );
  // calculate header size
  BitmapHeaderSize := bmf.bfOffBits - sizeof( TBITMAPFILEHEADER );
  // calculate size of bitmap bits
  ImageSize := FileSize - integer( bmf.bfOffBits );
  // check for valid bitmap and exit if not
  If ( ( bmf.bfType <> $4D42 ) Or
    ( integer( bmf.bfOffBits ) < 1 ) Or
    ( FileSize < 1 ) Or ( BitmapHeaderSize < 1 ) Or ( ImageSize < 1 ) Or
    ( FileSize < ( SizeOf( TBITMAPFILEHEADER )   BitmapHeaderSize  
ImageSize ) ) ) Then
  Begin
    Stream.Free;
    Exit;
  End;
  lpBitmapInfo := MyGetMem( BitmapHeaderSize );
  Try
    Stream.ReadBuffer( lpBitmapInfo^, BitmapHeaderSize );
    // check for uncompressed bitmap
    If ( ( lpBitmapInfo^.bmiHeader.biCompression = BI_RLE4 ) Or
      ( lpBitmapInfo^.bmiHeader.biCompression = BI_RLE8 ) ) Then
    Begin
      Exit;
    End;        // bitmap dimensions
    TotalBitmapWidth := lpBitmapInfo^.bmiHeader.biWidth;
    TotalBitmapHeight := abs( lpBitmapInfo^.bmiHeader.biHeight );        // is reversed order ?
    SourceIsTopDown := ( lpBitmapInfo^.bmiHeader.biHeight < 0 );        // calculate number of bytes used per scanline
    SourceBytesPerScanLine := ((((lpBitmapInfo^.bmiHeader.biWidth *
      lpBitmapInfo^.bmiHeader.biBitCount)   31) And Not 31) Div 8);        // adjust buffer size
    If BufferSize < Abs( SourceBytesPerScanLine ) Then
      BufferSize := Abs( SourceBytesPerScanLine );        // calculate number of scanlines for every pass on the destination
bitmap
    dest_MaxScans := round( BufferSize / abs( SourceBytesPerScanLine ) );
    dest_MaxScans := round( dest_MaxScans * ( DestBitmap.Height /
TotalBitmapHeight ) );        If dest_MaxScans < 2 Then
      dest_MaxScans := 2;         // at least two scan lines        // is not big enough ?
    If dest_MaxScans > TotalBitmapHeight Then
      dest_MaxScans := TotalBitmapHeight;        { count the number of passes needed to fill the destination bitmap }
    dsty_top := 0;
    NumPasses := 0;
    While ( dsty_Top   dest_MaxScans ) <= DestBitmap.Height Do
    Begin
      inc( NumPasses );
      inc( dsty_top, dest_MaxScans );
    End;
    If NumPasses = 0 Then Exit;        // calculate scanlines on last pass
    dest_Residual := DestBitmap.Height Mod dest_MaxScans;        // now calculate how many scanlines in source bitmap needed for every
band on the destination bitmap
    SourceBandHeight :=( TotalBitmapHeight * ( 1 - ( dest_Residual /
DestBitmap.Height ) ) ) / NumPasses;        // initialize first band
    CurrentTop := 0;
    CurrentBottom := dest_MaxScans;        // a floating point used in order to not loose last scanline precision
on source bitmap
    // because every band on target could be a fraction (not integral) on
the source bitmap
    SourceLastScanLine := 0.0;        While CurrentTop < DestBitmap.Height Do
    begin          // scanline start of band in source bitmap
      img_start := Round( SourceLastScanLine );
      SourceLastScanLine := SourceLastScanLine   SourceBandHeight;
      // scanline finish of band in source bitmap
      img_end := Round( SourceLastScanLine );
      If img_end > TotalBitmapHeight - 1 Then
        img_end := TotalBitmapHeight - 1;
      img_numscans := img_end - img_start;
      If img_numscans < 1 Then Break;
      OldHeight := lpBitmapInfo^.bmiHeader.biHeight;
      If SourceIsTopDown Then
        lpBitmapInfo^.bmiHeader.biHeight := -img_numscans
      else
        lpBitmapInfo^.bmiHeader.biHeight := img_numscans;          // memory used to read only the current band
      bits := MyGetMem( Abs(SourceBytesPerScanLine) * img_numscans);          Try
        // calculate offset of band on disk
        OffsetInFile := TotalBitmapHeight - (img_start   img_numscans);
        Stream.Seek( integer( bmf.bfOffBits )   ( OffsetInFile * abs(
SourceBytesPerScanLine ) ), soFromBeginning );
        Stream.ReadBuffer( bits^, abs( SourceBytesPerScanLine ) *
img_numscans );            SetStretchBltMode( DestBitmap.Canvas.Handle, COLORONCOLOR );
        // now stretch the band readed to the destination bitmap
        StretchDIBits(
          DestBitmap.Canvas.Handle,
          0,
          CurrentTop,
          DestBitmap.Width,
          Abs(CurrentBottom - CurrentTop),
          0,
          0,
          TotalBitmapWidth,
          img_numscans,
          Bits,
          lpBitmapInfo^,
          DIB_RGB_COLORS, SRCCOPY );
      Finally
        MyFreeMem( bits );
        lpBitmapInfo^.bmiHeader.biHeight := OldHeight;
      End;          CurrentTop := CurrentBottom;
      CurrentBottom := CurrentTop   dest_MaxScans;
      If CurrentBottom > DestBitmap.Height Then
        CurrentBottom := DestBitmap.Height;
    end;
  Finally
    Stream.Free;
    MyFreeMem( lpBitmapInfo );
  End;
  Result:= True;
End;    procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  If FBitmap.Empty then exit;
  PaintBox1.Canvas.Draw(0,0,FBitmap);
end;    procedure TForm1.Button1Click(Sender: TObject);
var
  bmw, bmh: Integer;
begin
  with TOpenDialog.Create(Nil) do
    try
      DefaultExt:= 'BMP';
      Filter := 'Bitmaps (*.bmp)|*.bmp';
      Title := 'Define bitmap to display';
      if not Execute then Exit;
      FFileName:= FileName;
      FBitmap.PixelFormat:= pf24Bit;
      { define the size of the required bitmap }
      FBitmap.Width:= PaintBox1.ClientWidth;
      FBitmap.Height:= PaintBox1.ClientHeight;
      Screen.Cursor:= crHourglass;
      If not GetDIBInBands( FileName, FBitmap, 100 * 1024, bmw, bmh ) then
exit;   // 100k of buffer
      Label1.Caption:= Format( '%d X %d', [bmw, bmh] );
      PaintBox1.Invalidate;
    finally
      Free;
      Screen.Cursor:= crDefault;
    end;
end;    procedure TForm1.FormCreate(Sender: TObject);
begin
  FBitmap:= TBitmap.Create;
end;    procedure TForm1.FormDestroy(Sender: TObject);
begin
  FBitmap.free;
end;    procedure TForm1.FormResize(Sender: TObject);
var
  bmw, bmh: integer;
begin
  If Length(FFileName)=0 then Exit;
  FBitmap.PixelFormat:= pf24Bit;
  { define the size of the required bitmap }
  FBitmap.Width:= PaintBox1.ClientWidth;
  FBitmap.Height:= PaintBox1.ClientHeight;
  Screen.Cursor:= crHourglass;
  try
    If not GetDIBInBands( FFileName, FBitmap, 100 * 1024, bmw, bmh ) then
exit;   // 100k of buffer
    Label1.Caption:= Format( '%d X %d', [bmw, bmh] );
  finally
    Screen.Cursor:= crDefault;
  end;
end;    end.        object Form1: TForm1
  Left = 264
  Top = 146
  Width = 696
  Height = 480
  Caption = 'Big Bitmap Viewer'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  WindowState = wsMaximized
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnResize = FormResize
  PixelsPerInch = 120
  TextHeight = 16
  object PaintBox1: TPaintBox
    Left = 0
    Top = 37
    Width = 688
    Height = 403
    Align = alClient
    OnPaint = PaintBox1Paint
  end
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 688
    Height = 37
    Align = alTop
    TabOrder = 0
    object Label1: TLabel
      Left = 148
      Top = 12
      Width = 301
      Height = 16
      AutoSize = False
    end
    object Button1: TButton
      Left = 12
      Top = 8
      Width = 121
      Height = 25
      Caption = 'Define Bitmap'
      TabOrder = 0
      OnClick = Button1Click
    end
  end
end
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#4 引用回覆 回覆 發表時間:2005-02-15 22:50:55 IP:218.175.xxx.xxx 未訂閱
思考 手賤 亂 一下     
引言: 掃描器上掃到的一個很大的bmp圖檔(超過50M),總顯示記憶體不足的ERROR
除非是要原圖印刷廠用所以要高解析的圖 如果只要顯示於PC上就利用失真壓縮檔案 來快速顯示客端選擇 優點 快快快 缺點 選擇列印時 利用專業級排版繪圖軟體協助處理 巨集 比較複雜 但色票與OS色票問題或印表機意外問題 都可解決 自己寫AP沒有專業級的協助 優點 技術自己攪 缺點 慢中之慢 非常慢 色票 印表機 暫存問題 問題一籮筐 決對是挑戰的一門高深遊戲 除非是要攪成 友立或ㄚ逗比 摳煮駱等 在windows下顯示選擇 送指令至 蘋果 去檔案伺服器抓圖用 ㄚ逗比 使用巨集 給他 專業的 硬 出來 還好是只用一般掃瞄器A4~A3 如果是高速雷射掃瞄器 或 巴士車身廣告看版的圖 一張100~600mb都是很正常ㄟ
引言: 若換為JPEG 格式,就可能不會了
那甲ㄟ 那就使用jpeg2000 如 wameng 版主 所說 http://delphi.ktop.com.tw/topic.php?TOPIC_ID=38057 http://delphi.ktop.com.tw/topic.php?topic_id=26603 純哈啦 別K庵喔 發表人 - conundrum 於 2005/02/15 23:02:09
sam_000
一般會員


發表:27
回覆:47
積分:14
註冊:2003-09-15

發送簡訊給我
#5 引用回覆 回覆 發表時間:2005-02-18 09:19:26 IP:211.22.xxx.xxx 未訂閱
謝謝兩位板主及conundrum前輩的答覆,前天電腦故障重裝,還未試完待試完了再向您報告
yorkland
高階會員


發表:2
回覆:138
積分:108
註冊:2004-12-17

發送簡訊給我
#6 引用回覆 回覆 發表時間:2005-02-20 20:07:35 IP:203.70.xxx.xxx 未訂閱
我的工作需要, 我有寫一個秀大圖的程式。 最初的版本也是用TImage來秀圖, 也遇到你提的問題, 但我記得當時遇到的狀況是以Delphi 5.0, 後來我改用Delphi 6.0後的版本, 就沒有遇到你提的問題。 不過要載入後才能秀圖, 的確效率還是很差, 而且我的程式中還需要放大縮小的功能, 所以現在的版本是以記憶體載入資料檔後, 畫面只繪製看得到的部份。 如果是1024*768, 我們就只要抓出需要的Pixel's data, 然後Run time組成一個新的Bmp Object, 然後再利用Canvas.Draw的方式畫到顯示的Canvas上。
sam_000
一般會員


發表:27
回覆:47
積分:14
註冊:2003-09-15

發送簡訊給我
#7 引用回覆 回覆 發表時間:2005-02-23 17:02:47 IP:219.80.xxx.xxx 未訂閱
謝謝版主及幾位前輩的幫忙,可以了,首先採用版主的例題可以顯示,不過版主的技巧比較高,修改要有功力才行,yarklank前輩的提醒,我在記憶體做了一個bmp,把圖形存到bmp,再分段將圖show出來,謝謝幾位前輩得幫忙,版主比較早說明,得分就給版主,望yarklank前輩不要見怪.
yorkland
高階會員


發表:2
回覆:138
積分:108
註冊:2004-12-17

發送簡訊給我
#8 引用回覆 回覆 發表時間:2005-02-23 22:47:06 IP:203.67.xxx.xxx 未訂閱
不會在意的啦... 藉由答題檢視自己的程式想法, 我也樂於與大家分享交流。 在這裡遇到許多高手高手高高手, 大家也都不吝於分享方法。
bugmans
高階會員


發表:95
回覆:322
積分:188
註冊:2003-04-12

發送簡訊給我
#9 引用回覆 回覆 發表時間:2007-01-30 20:12:16 IP:125.225.xxx.xxx 未訂閱
今天在網路上閒逛時找到的元件,可以顯示超大的bmp圖檔,測試開啟3000*3000 25MB的圖檔
只有一開始載入時比較慢,圖顯示之後調整視窗大小速度就不受影響
我回頭找ktop相關討論,發現網友wameng將其中的原始碼貼出來,但沒有提供zip檔的下載連結
http://www.torry.net/quicksearchd.php?String=big&Title=Yes


Big Bitmap Loader Sample v.1.0
By EzSoft Engineering. This sample application can load a bigmap of 2GB in only 100KB of
memory. This solves the big bitmap problem in Win 9x and any other Windows version.
下載位址 http://www.torry.net/samples/samples/graphics/bigbitmapviewer.zip


至於這個元件在http://delphi.ktop.com.tw/board.php?cid=161&fid=110&tid=4043有介紹過
只是我一直搞不定DsgnIntf的錯誤,無法測試載入的速度為何

Big Bitmap Viewer v.1.01
By Grahame Marsh. "This component came about because I wanted to display
4000 x 4000 x 256 colour bitmaps (about 16MB in size). Using a TBitmap and a
TImage took ages to load the images as a whole load of disc-swap file activity took
place. The answer was to use a memory mapped file and the StretchDIBits API call
which takes a memory pointer to the bitmap data, and doesn't realise (of course) that
it's a memory mapped file. Load times and resource used drastically reduced."

bugmans
高階會員


發表:95
回覆:322
積分:188
註冊:2003-04-12

發送簡訊給我
#10 引用回覆 回覆 發表時間:2008-04-20 18:10:11 IP:125.225.xxx.xxx 未訂閱
http://delphi.ktop.com.tw/board.php?cid=168&fid=921&tid=52469
wearefamily網友提到的
找一本 "Delphi 於繪圖與圖形處理上的實習應用"
裡面有一個TBigBitmap,可以解決這個問題
http://www.asahi-net.or.jp/~HA3T-NKMR/DGS/DownLoad.htm可以下載
http://www.asahi-net.or.jp/~HA3T-NKMR/DGS/DHGL/dhgl1.2.lzh

編輯記錄
bugmans 重新編輯於 2008-04-20 18:14:28, 註解 無‧
系統時間:2024-05-09 8:13:22
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!