You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

FireMonkey中PNG图片的压缩等级指定与8位格式保存方法

FireMonkey中PNG压缩等级与8位格式保存方案

我来帮你搞定FireMonkey里PNG压缩等级和8位格式保存的问题——刚好你用的是TBitmapCodecManager,还不能用外部库,咱们直接用Delphi原生API就能解决:

一、指定PNG压缩等级

首先得说,TBitmapCodecManagerQuality参数对PNG完全没用,毕竟PNG是无损格式。要设置压缩等级,咱们可以绕开它,直接用Delphi自带的TPNGImage(这不算外部库,是VCL/FMX共享的原生组件),它专门提供了CompressionLevel属性,范围是0(无压缩)到9(最高压缩),默认值是6。

修改你原来的代码,换成下面的实现:

begin
  var oStream := TMemoryStream.Create;
  var PNGImage := TPNGImage.Create;
  try
    // 把你的TBitmap对象赋值给PNGImage
    PNGImage.Assign(aBitmap);
    // 设置压缩等级,这里设为最高的9,你可以根据需求调整0-9之间的值
    PNGImage.CompressionLevel := 9;
    // 保存到内存流
    PNGImage.SaveToStream(oStream);
    oStream.Position := 0;
    // 这里可以用oStream做后续的存储或传输操作
  finally
    oStream.Free;
    PNGImage.Free;
  end;
end;

如果硬要坚持用TBitmapCodecManager,其实可以自定义编码参数,但步骤繁琐得多,不如直接用TPNGImage来得直接高效,而且完全符合你“不依赖外部库”的要求。

二、保存为8位PNG格式

你的图片是黑白带抗锯齿,8位索引色(256级灰度)完全足够,还能大幅减小文件体积。咱们需要把32位的TBitmap转换成8位调色板模式的PNG,具体步骤如下:

  1. 创建一个256级的灰度调色板,覆盖黑白到中间过渡的所有灰度值
  2. 把32位像素转换成对应的灰度索引
  3. 配置TPNGImage为8位索引色格式
  4. 可选:同时设置压缩等级

下面是完整的可复用代码:

uses
  FMX.Graphics, FMX.Surfaces, Vcl.Imaging.pngimage, System.Math;

procedure SaveAs8BitPNG(aBitmap: TBitmap; aStream: TStream; CompressionLevel: Integer = 6);
var
  PNGImage: TPNGImage;
  Palette: array[0..255] of TRGBQuad;
  I: Integer;
  Surface: TBitmapSurface;
  X, Y: Integer;
  Pixel: TAlphaColor;
  Gray: Byte;
begin
  PNGImage := TPNGImage.Create;
  Surface := TBitmapSurface.Create;
  try
    // 从输入的TBitmap获取像素表面
    Surface.Assign(aBitmap);

    // 创建256级灰度调色板(0=纯黑,255=纯白)
    for I := 0 to 255 do
    begin
      Palette[I].rgbRed := I;
      Palette[I].rgbGreen := I;
      Palette[I].rgbBlue := I;
      Palette[I].rgbReserved := 0;
    end;

    // 初始化PNG为8位索引色格式
    PNGImage.CreateBlank(COLOR_PALETTE, 8, Surface.Width, Surface.Height);
    // 应用我们创建的灰度调色板
    PNGImage.Palette := Palette;
    // 如果你的图片不需要透明,保持False即可;如果有透明区域,设为True并调整调色板的透明索引
    PNGImage.Transparent := False;

    // 遍历所有像素,将32位颜色转成8位灰度索引
    for Y := 0 to Surface.Height - 1 do
      for X := 0 to Surface.Width - 1 do
      begin
        Pixel := Surface.Pixels[X, Y];
        // 用标准灰度公式计算灰度值,保证抗锯齿过渡自然
        Gray := Round(0.299 * TAlphaColorRec(Pixel).R + 0.587 * TAlphaColorRec(Pixel).G + 0.114 * TAlphaColorRec(Pixel).B);
        // 直接用灰度值作为调色板索引,因为调色板是灰度对应的
        PNGImage.Pixels[X, Y] := Gray;
      end;

    // 确保压缩等级在0-9的有效范围内
    PNGImage.CompressionLevel := Clamp(CompressionLevel, 0, 9);
    // 保存到目标流
    PNGImage.SaveToStream(aStream);
    aStream.Position := 0;
  finally
    Surface.Free;
    PNGImage.Free;
  end;
end;

// 调用示例
procedure TYourForm.SaveBlackWhitePNG;
var
  oStream: TMemoryStream;
begin
  oStream := TMemoryStream.Create;
  try
    // 保存为8位PNG,同时设置最高压缩等级9
    SaveAs8BitPNG(YourSourceBitmap, oStream, 9);
    // 这里可以把流保存到文件,或者做其他处理
    oStream.SaveToFile('output_8bit.png');
  finally
    oStream.Free;
  end;
end;

补充说明

  • 如果你的图片存在透明区域,可以将PNGImage.Transparent设为True,并修改调色板中对应索引的rgbReserved值为255(表示完全透明)
  • 压缩等级可以根据需求调整,0是最快但体积最大,9是最慢但体积最小

内容的提问来源于stack exchange,提问作者Peter Jones

火山引擎 最新活动