如何在Android/iOS上将FireMonkey TBitmap转换为Windows Bitmap(.bmp)?
当然没问题!在Delphi Tokyo开发Android应用时,要把TBitmap或TBitmapSurface转换成标准Windows Bitmap(BMP)格式并存入流,其实有几种靠谱的方案,甚至不需要自定义TBmpBitmapCodec(不过真要定制的话也能做到)。下面我给你详细说说:
方案一:用TBitmapCodecManager快速实现(推荐)
Delphi的TBitmapCodecManager已经内置了对BMP格式的支持,哪怕是在Android平台上也能直接用。它会帮你处理BMP的文件头、信息头和像素编码,生成标准的Windows Bitmap格式,省心又可靠。
步骤与代码示例:
- 先把
TBitmap转换为TBitmapSurface(如果源是TBitmapSurface可以跳过这步); - 确保像素格式为24位RGB(Windows BMP最常用的无压缩格式);
- 用
TBitmapCodecManager.SaveToStream将表面数据写入流。
uses System.SysUtils, System.Classes, FMX.Graphics, FMX.Surfaces, FMX.Imaging; function ConvertToWindowsBMP(const ASource: TBitmap; out AStream: TMemoryStream): Boolean; var LSurface: TBitmapSurface; begin Result := False; AStream := nil; if not Assigned(ASource) then Exit; LSurface := TBitmapSurface.Create; try // 将TBitmap内容复制到BitmapSurface LSurface.Assign(ASource); // 转换为24位RGB格式(Windows BMP标准格式) if LSurface.Format <> TPixelFormat.RGB then LSurface.Convert(TPixelFormat.RGB); AStream := TMemoryStream.Create; // 保存为BMP格式到流 if TBitmapCodecManager.SaveToStream(AStream, LSurface, TBMPImageFormat) then begin AStream.Position := 0; // 重置流指针到开头,方便后续发送 Result := True; end else begin FreeAndNil(AStream); Exit; end; finally LSurface.Free; end; end;
调用这个函数后,AStream里就是标准的Windows BMP数据,可以直接通过DataSnap发送给服务器。
方案二:手动构建BMP文件结构(适合定制需求)
如果你需要对BMP的细节完全控制(比如调整压缩方式、自定义像素格式),可以手动构建BMP的文件头、信息头和像素数据。不过这个方式需要注意BMP的存储规则:比如像素行是从下到上存储,24位BMP的像素顺序是BGR而非RGB,且每行字节数要对齐到4字节。
代码示例:
uses System.SysUtils, System.Classes, FMX.Graphics, FMX.Surfaces, Winapi.Windows; function ManualConvertToBMP(const ASource: TBitmap; out AStream: TMemoryStream): Boolean; var LSurface: TBitmapSurface; BmpFileHeader: TBitmapFileHeader; BmpInfoHeader: TBitmapInfoHeader; RowSize: Integer; I, J: Integer; SrcPixel: PRGBQuad; DestBytes: PByte; begin Result := False; AStream := nil; if not Assigned(ASource) then Exit; LSurface := TBitmapSurface.Create; try LSurface.Assign(ASource); // 转换为24位RGB格式 if LSurface.Format <> TPixelFormat.RGB then LSurface.Convert(TPixelFormat.RGB); AStream := TMemoryStream.Create; try // 填充BMP文件头 FillChar(BmpFileHeader, SizeOf(BmpFileHeader), 0); BmpFileHeader.bfType := $4D42; // 标识为BMP文件('BM') BmpFileHeader.bfOffBits := SizeOf(BmpFileHeader) + SizeOf(BmpInfoHeader); // 填充BMP信息头 FillChar(BmpInfoHeader, SizeOf(BmpInfoHeader), 0); BmpInfoHeader.biSize := SizeOf(BmpInfoHeader); BmpInfoHeader.biWidth := LSurface.Width; BmpInfoHeader.biHeight := LSurface.Height; BmpInfoHeader.biPlanes := 1; BmpInfoHeader.biBitCount := 24; // 24位无压缩 BmpInfoHeader.biCompression := BI_RGB; // 计算每行字节数(对齐到4字节) RowSize := ((LSurface.Width * 3 + 3) div 4) * 4; BmpInfoHeader.biSizeImage := RowSize * LSurface.Height; BmpFileHeader.bfSize := BmpFileHeader.bfOffBits + BmpInfoHeader.biSizeImage; // 写入文件头和信息头到流 AStream.WriteBuffer(BmpFileHeader, SizeOf(BmpFileHeader)); AStream.WriteBuffer(BmpInfoHeader, SizeOf(BmpInfoHeader)); // 处理像素数据:从底部行开始写入,转换RGB为BGR顺序 GetMem(DestBytes, RowSize); try for I := LSurface.Height - 1 downto 0 do begin FillChar(DestBytes^, RowSize, 0); SrcPixel := LSurface.ScanLine[I]; for J := 0 to LSurface.Width - 1 do begin // BMP是BGR顺序,所以交换R和B PByte(Cardinal(DestBytes) + J*3)^ := SrcPixel^.gbBlue; PByte(Cardinal(DestBytes) + J*3 + 1)^ := SrcPixel^.gbGreen; PByte(Cardinal(DestBytes) + J*3 + 2)^ := SrcPixel^.gbRed; Inc(SrcPixel); end; AStream.WriteBuffer(DestBytes^, RowSize); end; finally FreeMem(DestBytes); end; AStream.Position := 0; Result := True; except FreeAndNil(AStream); raise; end; finally LSurface.Free; end; end;
这个方案灵活性高,但需要熟悉BMP的文件结构,容易出错,所以除非有特殊需求,优先用方案一。
关于自定义TBmpBitmapCodec的说明
其实Delphi已经在FMX.Imaging.BMP单元中内置了TBMPBitmapCodec,它就是用来处理BMP格式编码解码的,默认的TBitmapCodecManager已经会调用它。所以你完全不需要自己自定义Codec,除非你需要支持一些非标准的BMP变种(比如带Alpha通道的32位BMP,或者自定义压缩方式)。如果真要扩展,你可以继承TBitmapCodec类实现自己的编码逻辑,但一般来说自带的Codec足够满足你的需求。
缩略图生成的补充
你需要生成128×128的缩略图,在转换为BMP之前,可以先对原图进行缩放:
var OriginalBitmap: TBitmap; ThumbBitmap: TBitmap; BmpStream: TMemoryStream; begin // 假设OriginalBitmap是你从手机获取的原图 ThumbBitmap := TBitmap.Create(128, 128); try ThumbBitmap.Assign(OriginalBitmap); // 缩放到128×128 ThumbBitmap.Resize(128, 128); // 转换为BMP流 if ConvertToWindowsBMP(ThumbBitmap, BmpStream) then begin // 这里可以把BmpStream通过DataSnap发送给服务器 // ... BmpStream.Free; end; finally ThumbBitmap.Free; end; end;
用TBitmap.Resize方法简单快捷,也可以用TBitmapSurface.Resize来获得更高的性能。
内容的提问来源于stack exchange,提问作者Erwin Sienknecht




