【问题标题】:How to save Windows.tagBitmap to stream as a full DIB?如何将 Windows.tagBitmap 保存为完整的 DIB 流?
【发布时间】:2016-09-30 09:28:44
【问题描述】:

我收到来自第三方组件的 PBitmap,它是指向 Windows.tagBitmap 记录的指针。

{ Bitmap Header Definition }
  PBitmap = ^TBitmap;
  {$EXTERNALSYM tagBITMAP}
  tagBITMAP = record
    bmType: Longint;
    bmWidth: Longint;
    bmHeight: Longint;
    bmWidthBytes: Longint;
    bmPlanes: Word;
    bmBitsPixel: Word;
    bmBits: Pointer;
  end;
  TBitmap = tagBITMAP;
  {$EXTERNALSYM TBitmap}
  BITMAP = tagBITMAP;
  {$EXTERNALSYM BITMAP}

我想将此指针中包含的数据转换为常规DIB 并将此数据保存到流中。就像Graphics.TBitmap.SaveToStream 一样。 所以最好我想有一个像这样的程序:

procedure SavetagBitmapAsDIBToStream(const ABitmap: PBitmap; var AStream: TStream);

我试图在MSDN 上找到有关此结构的信息,但那里描述的标题(BITMAPFILEHEADERBITMAPINFOHEADER 等)似乎都符合tagBITMAP

有这方面经验的人可以帮助我吗?

已编辑: C\C++ 中的示例对我来说也可以。

【问题讨论】:

  • 有关该结构的 Microsoft 文档可在此 MSDN 页面上找到:msdn.microsoft.com/en-us/library/dd183371(v=vs.85).aspx
  • 您也可以使用 Win32 CreateBitmap 函数为该 tagBITMAP 数据创建 Windows GDI 句柄,然后将该句柄分配给 Vcl.Graphics.TBitmap.Handle 并将 TBitmap 保存到文件/流/任何内容
  • 我不明白这个问题。您已经有了 BITMAP 结构的布局(tagBITMAP 只是内部名称,早期 C 的产物,参见 Why do structures get tag names even if there is a typedef?)。另一方面,DIB 并没有什么特别之处。它只是具有某些功能的常规位图。那么你真正想在这里解决什么问题?
  • 那是位图,不一定是DIB。我还是不明白这个问题。
  • 我还是不明白这个问题。听起来您是在询问您的解决方案,而不是您真正想要解决的问题(并在此过程中混淆了不相关的概念)。我建议您改为询问您的问题。

标签: delphi winapi bitmap delphi-xe5 dib


【解决方案1】:

使用 Win32 API CreateBitmapIndirect() 函数从 tagBITMAP 结构创建 DDB HBITMAP 句柄,然后将 HBITMAP 分配给 VCL TBitmap 对象的 Handle 属性并将对象保存到你的TStream(它将保存为 DIB)。

uses
  Winapi.Windows, Vcl.Graphics;

procedure SavetagBitmapAsDIBToStream(const ABitmap: PBitmap; var AStream: TStream);
var
  Bmp: Vcl.Graphics.TBitmap;
begin
  Bmp := Vcl.Graphics.TBitmap.Create;
  try
    Bmp.Handle := CreateBitmapIndirect(ABitmap);
    Bmp.HandleType := bmDIB; // optional
    Bmp.SaveToStream(AStream);
  finally
    Bmp.Free;
  end;
end;

【讨论】:

  • 感谢 Remy,您的函数完成了这项工作。但是,由于性能原因,我想避免使用Graphics.TBitmap。我想直接保存到流中。如果没有更好的答案出现,我会接受这个作为答案。
  • 然后您将不得不手动处理 DDB 并从中创建一个 DIB。查看Vcl.Graphics.pasTBitmap 的源代码,特别是TBitmap.DIBNeeded()TBitmap.SaveToStream() 方法,以及CopyBitmap() 函数。通过CreateDIBSection()使用从DDB中提取的信息创建DIB,然后从DIB中提取数据并使用BITMAPFILEHEADERBITMAPCOREHEADERDIBSECTIONRGBQUAD等将数据写入目标TStream
【解决方案2】:

这是解决方案的草稿。它应该可以帮助某人构建一个正确的错误处理/更漂亮的代码等。

function CreateBitmapInfoStruct(pBmp: PBitmap): TBitmapInfo;
var
  bmi: TBitmapInfo;
  cClrBits: Word;
begin
  cClrBits := pBmp.bmPlanes * pBmp.bmBitsPixel;
  if (cClrBits = 1) then
      cClrBits := 1
  else if (cClrBits <= 4) then
      cClrBits := 4
  else if (cClrBits <= 8) then
      cClrBits := 8
  else if (cClrBits <= 16) then
      cClrBits := 16
  else if (cClrBits <= 24) then
      cClrBits := 24
  else cClrBits := 32;

  bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth := pBmp.bmWidth;
  bmi.bmiHeader.biHeight := pBmp.bmHeight;
  bmi.bmiHeader.biPlanes := pBmp.bmPlanes;
  bmi.bmiHeader.biBitCount := pBmp.bmBitsPixel;
  if (cClrBits < 24) then
      bmi.bmiHeader.biClrUsed := (1 shl cClrBits)
  else
    bmi.bmiHeader.biClrUsed := 0;

  bmi.bmiHeader.biCompression := BI_RGB;
  bmi.bmiHeader.biSizeImage := ((bmi.bmiHeader.biWidth * cClrBits + 31) and (not 31)) div 8
      * bmi.bmiHeader.biHeight;
  bmi.bmiHeader.biClrImportant := 0;
  Result := bmi;
end;

procedure SavetagBitmapAsDIBToStream(const ABitmap: PBitmap; AStream: TStream);
var
  pbi: TBitmapInfo;
  lHDC: HDC;
  pbih: BITMAPINFOHEADER ;
  hdr: BITMAPFILEHEADER;
  lpBits: PByte;
  hBMP: HBITMAP;
begin
  pbi := CreateBitmapInfoStruct(ABitmap);
  lHDC := CreateCompatibleDC(0);
  GetMem(lpBits, pbih.biSizeImage);
  hBmp := CreateBitmapIndirect(ABitmap^);
  try
    pbih := pbi.bmiHeader;
    GetDIBits(lHDC, hBMP, 0, pbih.biHeight, lpBits, pbi, DIB_RGB_COLORS);
    hdr.bfType := $4d42;
    hdr.bfSize := sizeof(BITMAPFILEHEADER) + pbih.biSize + pbih.biClrUsed
          * sizeof(RGBQUAD) + pbih.biSizeImage;
    hdr.bfReserved1 := 0;
    hdr.bfReserved2 := 0;
    hdr.bfOffBits := sizeof(BITMAPFILEHEADER) +
        pbih.biSize + pbih.biClrUsed
        * sizeof (RGBQUAD);

    AStream.Write(hdr, SizeOf(BITMAPFILEHEADER));
    AStream.Write(pbih, SizeOf(BITMAPINFOHEADER) + pbih.biClrUsed * SizeOf(RGBQUAD));
    AStream.Write(lpBits^, pbih.biSizeImage);
  finally
    FreeMem(lpBits);
    DeleteObject(hBMP);
    ReleaseDC(0, lHDC);
  end;
end;

感谢 Remy 的帮助,并感谢对我的问题投反对票。让他们倾倒! :)

【讨论】:

    猜你喜欢
    • 2018-03-07
    • 1970-01-01
    • 2012-06-22
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 2020-08-05
    相关资源
    最近更新 更多