Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Загружать большие битовые изображения с небольшим использованием памяти

Delphi , Графика и Игры , Bitmap

Загружать большие битовые изображения с небольшим использованием памяти

Оформил: DeeCo
Автор: http://www.swissdelphicenter.ch

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 stretching 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;

 // example of usage 
procedure TForm1.Button1Click(Sender: TObject);
 var
   bmw, bmh: Integer;
   Bitmap: TBitmap;
 begin
   Bitmap := TBitmap.Create;
   with TOpenDialog.Create(nil) do
     try
       DefaultExt := 'BMP';
       Filter := 'Bitmaps (*.bmp)|*.bmp';
       Title := 'Define bitmap to display';
       if not Execute then Exit;
       { define the size of the required bitmap }
       Bitmap.Width       := Self.ClientWidth;
       Bitmap.Height      := Self.ClientHeight;
       Bitmap.PixelFormat := pf24Bit;
       Screen.Cursor      := crHourglass;
       // use 100 KB of buffer 
      if not GetDIBInBands(FileName, Bitmap, 100 * 1024, bmw, bmh) then Exit;
       // original bitmap width = bmw 
      // original bitmap height = bmh 
      Self.Canvas.Draw(0,0,Bitmap);
     finally
       Free;
       Bitmap.Free;
       Screen.Cursor := crDefault;
     end;
 end;

Перевод контента на русский язык:

Это код Delphi, который загружает большие изображения bitmap с минимальным использованием памяти. Это достигается чтением изображения в маленьких частях (строк) и растяжкой их на целевой bitmap. Код предоставляет несколько функций для достижения этого:

  1. MyGetMem - выделяет блок памяти.
  2. MyFreeMem - освобождает блок памяти.
  3. GetDIBInBands - основная функция, которая загружает bitmap из файла, растяжка ее на целевой bitmap и освобождение ресурсов.

Функция GetDIBInBands принимает несколько параметров:

  • FileName: имя некомпрессированного файла bitmap для чтения
  • DestBitmap: целевая bitmap, где будет растянуто изображение
  • BufferSize: размер буфера памяти, используемого для чтения строк из физического bitmap на диске (в байтах)
  • TotalBitmapWidth и TotalBitmapHeight: параметры вывода, хранящие ширину и высоту оригинального bitmap

Функция сначала открывает файл, читает заголовок, рассчитывает общий размер bitmap и проверяет, является ли это валидным bitmap. Если нет, она выходит.

Затем функция выделяет память для записи информации о bitmap и читает запись из файла. Затем она рассчитывает различные свойства, такие как количество байтов на строку, является ли изображение сверху вниз или снизу вверх, и сколько строк может поместиться в буфер.

Функция затем вступает в цикл, где она обрабатывает каждый банд (строку) из исходного изображения, чтит соответствующие данные с диска, растягивает их на целевую bitmap с помощью StretchDIBits, и освобождает ресурсы.

Наконец, функция освобождает любые выделенные памяти и возвращает, успешна ли операция или нет.

Пример использования кода создает диалоговое окно для выбора файла bitmap, загружает его в объект TBitmap, устанавливает свойства (ширина, высота, формат пикселей) и затем использует функцию GetDIBInBands для загрузки изображения на целевую bitmap. Код также включает в себя некоторые ошибки обработки и визуальное обратное отображение.

Для улучшения использования памяти код использует несколько техник:

  1. Чтение изображения в маленьких частях (строк) вместо загрузки всего изображения в память одновременно.
  2. Использование буфера размером, достаточным для хранения хотя бы двух строк, но не так большим, чтобы он занимал много памяти.
  3. Освобождение ресурсов (памяти и файловых handle) как только они больше не нужны.

Однако есть некоторые области, где использование памяти могло быть улучшено:

  1. Функция GetDIBInBands выделяет большой блок памяти для записи информации о bitmap, который используется временно перед освобождением. Это может потенциально привести к фрагментации памяти, если она вызывается несколько раз подряд.
  2. Код использует глобальные переменные (bmf, lpBitmapInfo и т.д.) вместо локальных переменных или параметров, передаваемых по ссылке. Хотя это может сделать код более легко читаемым, это также может привести к утечкам памяти или конфликтам ресурсов, если не правильно очищать.
  3. Функция GetDIBInBands не обрабатывает ошибки очень надежно. Если происходит ошибка при чтении файла или аллокации памяти, функция выйдет без освобождения любых ресурсов, которые были выделены до возникновения ошибки.

В целом, это код - хороший старт для загрузки больших bitmap с минимальным использованием памяти, но он мог быть улучшен в нескольких областях, чтобы сделать его более эффективным и надежным.

Загружать большие битовые изображения с небольшим использованием памяти.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Bitmap ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-15 22:47:26/0.004310131072998/0