-2

from a third party component I am receiving a PBitmap which is a pointer to Windows.tagBitmap record.

{ 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}

I would like to convert data contained in this pointer to a regular DIB and save this data to a stream. Just as Graphics.TBitmap.SaveToStream does. So preferably I would like to have a procedure like:

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

I've tried to find information about this structure on MSDN, but none of the headers described there (BITMAPFILEHEADER, BITMAPINFOHEADER etc.) seems to conform tagBITMAP.

Could someone experienced in the matter could help me?

edited: An example in in C \ C++ would also be fine for me.

4

2 回答 2

2

Use the Win32 API CreateBitmapIndirect() function to create a DDB HBITMAP handle from your tagBITMAP structure, then assign that HBITMAP to the Handle property of a VCL TBitmap object and save the object to your TStream (it will save it as a 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;
于 2016-09-30T19:35:10.217 回答
1

Here is a draft of the solution. It should help someone to built a proper one with error handling / prettier code etc.

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;

Thanks Remy for help and thanks for downvotes to my question. Keep them pouring! :)

于 2016-10-04T17:41:25.153 回答