【问题标题】:How to resize a picture?如何调整图片大小?
【发布时间】:2023-04-01 12:20:02
【问题描述】:

我有图像 (500x500) 但我需要将其调整为 200x200 并将其绘制在 TImage 上。如何达到这样的效果?

注意
我知道 TImage 中的 Stretch 属性,但我想以编程方式调整图像大小。

【问题讨论】:

    标签: image delphi resize delphi-7


    【解决方案1】:

    如果你知道新的维度不大于原来的维度,你可以简单地做

    procedure ShrinkBitmap(Bitmap: TBitmap; const NewWidth, NewHeight: integer);
    begin
      Bitmap.Canvas.StretchDraw(
        Rect(0, 0, NewWidth, NewHeight),
        Bitmap);
      Bitmap.SetSize(NewWidth, NewHeight);
    end;
    

    如果您知道新尺寸不小于原始尺寸小于,我将编写相应代码作为练习。

    如果你想要一个通用功能,你可以这样做

    procedure ResizeBitmap(Bitmap: TBitmap; const NewWidth, NewHeight: integer);
    var
      buffer: TBitmap;
    begin
      buffer := TBitmap.Create;
      try
        buffer.SetSize(NewWidth, NewHeight);
        buffer.Canvas.StretchDraw(Rect(0, 0, NewWidth, NewHeight), Bitmap);
        Bitmap.SetSize(NewWidth, NewHeight);
        Bitmap.Canvas.Draw(0, 0, buffer);
      finally
        buffer.Free;
      end;
    end;
    

    这种方法有两个像素复制操作的缺点。对于这个问题,我至少可以想到两种解决方案。 (哪个?)

    【讨论】:

    • @Robers:就我个人而言,当我需要操作位图时,我总是使用它们。
    • @AndreasRejbrand 仅当您将其调整为小 dem 时才有效,如果您有一个 48x48 的 bmp 文件并希望将其调整为 256x256,此过程将失败。
    • @Sami:非常正确。我的错。
    • @AndreasRejbrand 我的代码有问题,它可以工作,但我已经用我的手机拍摄的图片进行了尝试,并且图片被旋转并且其中一部分被切断了你能告诉我为什么会这样?我正在使用您回答的第一部分,它也适用于所有其他图片,但手机中的图片不起作用。请帮忙
    【解决方案2】:

    出色的可用性和图像质量提供了下面单元 1) 中的ResizeImage 功能。代码取决于Graphics32GIFImage 2)PNGImage 2)

    该函数采用两个文件名或两个流。输入(自动检测为)BMP、PNG、GIF 或 JPG,输出始终为 JPG。

    unit AwResizeImage;
    
    interface
    
    uses
      Windows, SysUtils, Classes, Graphics, Math, JPEG, GR32, GIFImage, PNGImage,
      GR32_Resamplers;
    
    type
      TImageType = (itUnknown, itBMP, itGIF, itJPG, itPNG);
      TImageInfo = record
        ImgType: TImageType;
        Width: Cardinal;
        Height: Cardinal;
      end;
    
      function GetImageInfo(const AFilename: String): TImageInfo; overload;
      function GetImageInfo(const AStream: TStream): TImageInfo; overload;
    
      function ResizeImage(const ASource, ADest: String; const AWidth,
        AHeight: Integer; const ABackColor: TColor;
        const AType: TImageType = itUnknown): Boolean; overload;
      function ResizeImage(const ASource, ADest: TStream; const AWidth,
        AHeight: Integer; const ABackColor: TColor;
        const AType: TImageType = itUnknown): Boolean; overload;
    
    implementation
    
    type
      TGetDimensions = procedure(const ASource: TStream;
        var AImageInfo: TImageInfo);
    
      TCardinal = record
        case Byte of
          0: (Value: Cardinal);
          1: (Byte1, Byte2, Byte3, Byte4: Byte);
      end;
    
      TWord = record
        case Byte of
          0: (Value: Word);
          1: (Byte1, Byte2: Byte);
      end;
    
      TPNGIHDRChunk = packed record
        Width: Cardinal;
        Height: Cardinal;
        Bitdepth: Byte;
        Colortype: Byte;
        Compression: Byte;
        Filter: Byte;
        Interlace: Byte;
      end;
    
      TGIFHeader = packed record
        Signature: array[0..2] of Char;
        Version: array[0..2] of Char;
        Width: Word;
        Height: Word;
      end;
    
      TJPGChunk = record
        ID: Word;
        Length: Word;
      end;
    
      TJPGHeader = packed record
        Reserved: Byte;
        Height: Word;
        Width: Word;
      end;
    
    const
      SIG_BMP: array[0..1] of Char = ('B', 'M');
      SIG_GIF: array[0..2] of Char = ('G', 'I', 'F');
      SIG_JPG: array[0..2] of Char = (#255, #216, #255);
      SIG_PNG: array[0..7] of Char = (#137, #80, #78, #71, #13, #10, #26, #10);
    
    function SwapBytes(const ASource: Cardinal): Cardinal; overload;
    var
      mwSource: TCardinal;
      mwDest: TCardinal;
    begin
      mwSource.Value := ASource;
      mwDest.Byte1 := mwSource.Byte4;
      mwDest.Byte2 := mwSource.Byte3;
      mwDest.Byte3 := mwSource.Byte2;
      mwDest.Byte4 := mwSource.Byte1;
      Result := mwDest.Value;
    end;
    
    function SwapBytes(const ASource: Word): Word; overload;
    var
      mwSource: TWord;
      mwDest: TWord;
    begin
      mwSource.Value  := ASource;
      mwDest.Byte1 := mwSource.Byte2;
      mwDest.Byte2 := mwSource.Byte1;
      Result := mwDest.Value;
    end;
    
    procedure GetBMPDimensions(const ASource: TStream; var AImageInfo: TImageInfo);
    var
      bmpFileHeader: TBitmapFileHeader;
      bmpInfoHeader: TBitmapInfoHeader;
    begin
      FillChar(bmpFileHeader, SizeOf(TBitmapFileHeader), #0);
      FillChar(bmpInfoHeader, SizeOf(TBitmapInfoHeader), #0);
      ASource.Read(bmpFileHeader, SizeOf(TBitmapFileHeader));
      ASource.Read(bmpInfoHeader, SizeOf(TBitmapInfoHeader));
      AImageInfo.Width := bmpInfoHeader.biWidth;
      AImageInfo.Height := bmpInfoHeader.biHeight;
    end;
    
    procedure GetGIFDimensions(const ASource: TStream; var AImageInfo: TImageInfo);
    var
      gifHeader: TGIFHeader;
    begin
      FillChar(gifHeader, SizeOf(TGIFHeader), #0);
      ASource.Read(gifHeader, SizeOf(TGIFHeader));
      AImageInfo.Width := gifHeader.Width;
      AImageInfo.Height := gifHeader.Height;
    end;
    
    procedure GetJPGDimensions(const ASource: TStream; var AImageInfo: TImageInfo);
    var
      cSig: array[0..1] of Char;
      jpgChunk: TJPGChunk;
      jpgHeader: TJPGHeader;
      iSize: Integer;
      iRead: Integer;
    begin
      FillChar(cSig, SizeOf(cSig), #0);
      ASource.Read(cSig, SizeOf(cSig));
      iSize := SizeOf(TJPGChunk);
      repeat
        FillChar(jpgChunk, iSize, #0);
        iRead := ASource.Read(jpgChunk, iSize);
        if iRead <> iSize then
          Break;
        if jpgChunk.ID = $C0FF then
        begin
          ASource.Read(jpgHeader, SizeOf(TJPGHeader));
          AImageInfo.Width := SwapBytes(jpgHeader.Width);
          AImageInfo.Height := SwapBytes(jpgHeader.Height);
          Break;
        end
        else
          ASource.Position := ASource.Position + (SwapBytes(jpgChunk.Length) - 2);
      until False;
    end;
    
    procedure GetPNGDimensions(const ASource: TStream; var AImageInfo: TImageInfo);
    var
      cSig: array[0..7] of Char;
      cChunkLen: Cardinal;
      cChunkType: array[0..3] of Char;
      ihdrData: TPNGIHDRChunk;
    begin
      FillChar(cSig, SizeOf(cSig), #0);
      FillChar(cChunkType, SizeOf(cChunkType), #0);
      ASource.Read(cSig, SizeOf(cSig));
      cChunkLen := 0;
      ASource.Read(cChunkLen, SizeOf(Cardinal));
      cChunkLen := SwapBytes(cChunkLen);
      if cChunkLen = SizeOf(TPNGIHDRChunk) then
      begin
        ASource.Read(cChunkType, SizeOf(cChunkType));
        if AnsiUpperCase(cChunkType) = 'IHDR' then
        begin
          FillChar(ihdrData, SizeOf(TPNGIHDRChunk), #0);
          ASource.Read(ihdrData, SizeOf(TPNGIHDRChunk));
          AImageInfo.Width := SwapBytes(ihdrData.Width);
          AImageInfo.Height := SwapBytes(ihdrData.Height);
        end;
      end;
    end;
    
    function GetImageInfo(const AFilename: String): TImageInfo;
    var
      fsImage: TFileStream;
    begin
      fsImage := TFileStream.Create(AFilename, fmOpenRead or fmShareDenyWrite);
      try
        Result := GetImageInfo(fsImage);
      finally
        FreeAndNil(fsImage);
      end;
    end;
    
    function GetImageInfo(const AStream: TStream): TImageInfo;
    var
      iPos: Integer;
      cBuffer: array[0..2] of Char;
      cPNGBuffer: array[0..4] of Char;
      GetDimensions: TGetDimensions;
    begin
      GetDimensions := nil;
      Result.ImgType := itUnknown;
      Result.Width := 0;
      Result.Height := 0;
      FillChar(cBuffer, SizeOf(cBuffer), #0);
      FillChar(cPNGBuffer, SizeOf(cPNGBuffer), #0);
      iPos := AStream.Position;
      AStream.Read(cBuffer, SizeOf(cBuffer));
      if cBuffer = SIG_GIF then
      begin
        Result.ImgType := itGIF;
        GetDimensions := GetGIFDimensions;
      end
      else if cBuffer = SIG_JPG then
      begin
        Result.ImgType := itJPG;
        GetDimensions := GetJPGDimensions;
      end
      else if cBuffer = Copy(SIG_PNG, 1, 3) then
      begin
        AStream.Read(cPNGBuffer, SizeOf(cPNGBuffer));
        if cPNGBuffer = Copy(SIG_PNG, 4, 5) then
        begin
          Result.ImgType := itPNG;
          GetDimensions := GetPNGDimensions;
        end;
      end
      else if Copy(cBuffer, 1, 2) = SIG_BMP then
      begin
        Result.ImgType := itBMP;
        GetDimensions := GetBMPDimensions;
      end;
      AStream.Position := iPos;
      if Assigned(GetDimensions) then
      begin
        GetDimensions(AStream, Result);
        AStream.Position := iPos;
      end;
    end;
    
    procedure GIFToBMP(const ASource: TStream; const ADest: TBitmap);
    var
      imgSource: TGIFImage;
    begin
      imgSource := TGIFImage.Create();
      try
        imgSource.LoadFromStream(ASource);
        ADest.Assign(imgSource);
      finally
        FreeAndNil(imgSource);
      end;
    end;
    
    procedure JPGToBMP(const ASource: TStream; const ADest: TBitmap);
    var
      imgSource: TJPEGImage;
    begin
      imgSource := TJPEGImage.Create();
      try
        imgSource.LoadFromStream(ASource);
        ADest.Assign(imgSource);
      finally
        FreeAndNil(imgSource);
      end;
    end;
    
    procedure PNGToBMP(const ASource: TStream; const ADest: TBitmap);
    var
      imgSource: TPNGImage;
    begin
      imgSource := TPNGImage.Create();
      try
        imgSource.LoadFromStream(ASource);
        ADest.Assign(imgSource);
      finally
        FreeAndNil(imgSource);
      end;
    end;
    
    function ResizeImage(const ASource, ADest: String; const AWidth,
      AHeight: Integer; const ABackColor: TColor;
      const AType: TImageType = itUnknown): Boolean;
    var
      fsSource: TFileStream;
      fsDest: TFileStream;
    begin
      Result := False;
      fsSource := TFileStream.Create(ASource, fmOpenRead or fmShareDenyWrite);
      try
        fsDest := TFileStream.Create(ADest, fmCreate or fmShareExclusive);
        try
          Result := not Result; //hide compiler hint
          Result := ResizeImage(fsSource, fsDest, AWidth, AHeight, ABackColor, AType);
        finally
          FreeAndNil(fsDest);
        end;
      finally
        FreeAndNil(fsSource);
      end;
    end;
    
    function ResizeImage(const ASource, ADest: TStream; const AWidth,
      AHeight: Integer; const ABackColor: TColor;
      const AType: TImageType = itUnknown): Boolean;
    var
      itImage: TImageType;
      ifImage: TImageInfo;
      bmpTemp: TBitmap;
      bmpSource: TBitmap32;
      bmpResized: TBitmap32;
      cBackColor: TColor32;
      rSource: TRect;
      rDest: TRect;
      dWFactor: Double;
      dHFactor: Double;
      dFactor: Double;
      iSrcWidth: Integer;
      iSrcHeight: Integer;
      iWidth: Integer;
      iHeight: Integer;
      jpgTemp: TJPEGImage;
    begin
      Result := False;
      itImage := AType;
      if itImage = itUnknown then
      begin
        ifImage := GetImageInfo(ASource);
        itImage := ifImage.ImgType;
        if itImage = itUnknown then
          Exit;
      end;
      bmpTemp := TBitmap.Create();
      try
        case itImage of
          itBMP: bmpTemp.LoadFromStream(ASource);
          itGIF: GIFToBMP(ASource, bmpTemp);
          itJPG: JPGToBMP(ASource, bmpTemp);
          itPNG: PNGToBMP(ASource, bmpTemp);
        end;
        bmpSource := TBitmap32.Create();
        bmpResized := TBitmap32.Create();
        try
          cBackColor  := Color32(ABackColor);
          bmpSource.Assign(bmpTemp);
          bmpResized.Width := AWidth;
          bmpResized.Height := AHeight;
          bmpResized.Clear(cBackColor);
          iSrcWidth := bmpSource.Width;
          iSrcHeight := bmpSource.Height;
          iWidth := iSrcWidth;
          iHeight := iSrcHeight;
          with rSource do
          begin
            Left := 0;
            Top := 0;
            Right := iSrcWidth;
            Bottom := iSrcHeight;
          end;
          if (iWidth > AWidth) or (iHeight > AHeight) then
          begin
            dWFactor := AWidth / iWidth;
            dHFactor := AHeight / iHeight;
            if (dWFactor > dHFactor) then
              dFactor := dHFactor
            else
              dFactor := dWFactor;
            iWidth := Floor(iWidth * dFactor);
            iHeight := Floor(iHeight * dFactor);
          end;
          with rDest do
          begin
            Left := Floor((AWidth - iWidth) / 2);
            Top := Floor((AHeight - iHeight) / 2);
            Right := Left + iWidth;
            Bottom := Top + iHeight;
          end;
          bmpSource.Resampler := TKernelResampler.Create;
          TKernelResampler(bmpSource.Resampler).Kernel := TLanczosKernel.Create;
          bmpSource.DrawMode := dmOpaque;
          bmpResized.Draw(rDest, rSource, bmpSource);
          bmpTemp.Assign(bmpResized);
          jpgTemp := TJPEGImage.Create();
          jpgTemp.CompressionQuality := 80;
          try
            jpgTemp.Assign(bmpTemp);
            jpgTemp.SaveToStream(ADest);
            Result := True;
          finally
            FreeAndNil(jpgTemp);
          end;
        finally
          FreeAndNil(bmpResized);
          FreeAndNil(bmpSource);
        end;
      finally
        FreeAndNil(bmpTemp);
      end;
    end;
    
    end.
    

    注意事项:

    • 1) 我当然没有自己编写代码,但不知道我从哪里得到它。
    • 2) 包含在最近的 Delphi 版本中。
    • 如果使用更新版本的 RAD Studio/Delphi XE 进行编译,请记住用 ansichar 替换所有 char 变量类型,否则 GetImageInfo 将不起作用,并且不会调整图像大小。这是必需的,因为默认的 char 类型是两个字节,而函数期望它是单字节。

    【讨论】:

      【解决方案3】:

      我经常使用此页面中的SmoothResize 程序:http://www.swissdelphicenter.ch/torry/printcode.php?id=1896

      缩放比StretchDraw函数好很多。

      不要让标题欺骗了你。该页面演示了调整 JPG 的大小,但 SmoothResize 过程本身使用位图来调整大小。可以通过类似的方式调整 PNG 的大小,但是如果使用此过程,您将失去透明度。

      【讨论】:

      • 这段代码只做位图。如果您使用 TPNGImage,您可以在位图画布上绘制 PNG 图像并调整其大小,但它会破坏图像中的任何透明度。对于真正的 PNG 调整大小,我认为您需要 NGLN 的答案。
      【解决方案4】:

      请参阅这个简单示例,了解如何使用两个 TBitmap32 对象调整图像大小。 TBitmap32 在速度/图像质量比方面是最好的。

      它需要https://github.com/graphics32 库。

      uses 
        GR32, GR32_Resamplers;
      
      procedure Resize(InputPicture: TBitmap; OutputImage: TImage; const DstWidth, DstHeigth: Integer);
      var
        Src, Dst: TBitmap32;
      begin
        Dst := nil;
        try
          Src := TBitmap32.Create;
          try
            Src.Assign(InputPicture);
            SetHighQualityStretchFilter(Src);
            Dst := TBitmap32.Create;
            Dst.SetSize(DstWidth, DstHeigth);
            Src.DrawTo(Dst, Rect(0, 0, DstWidth, DstHeigth), Rect(0, 0, Src.Width, Src.Height));
          finally
            FreeAndNil(Src);
          end;
          OutputImage.Assign(Dst);
        finally
          FreeAndNil(Dst);
        end;
      end;
      
      // If you need to set a highest quality resampler, use this helper routine to configure it
      procedure SetHighQualityStretchFilter(B: TBitmap32);
      var
        KR: TKernelResampler;
      begin
        if not (B.Resampler is TKernelResampler) then
        begin
          KR := TKernelResampler.Create(B);
          KR.Kernel := TLanczosKernel.Create;
        end
        else
        begin
          KR := B.Resampler as TKernelResampler;
          if not (KR.Kernel is TLanczosKernel) then
          begin
            KR.Kernel.Free;
            KR.Kernel := TLanczosKernel.Create;
          end;
        end;
      end;
      

      【讨论】:

        【解决方案5】:

        我建议使用 JanFX 库(现在已合并到 fat Jedi 发行版中,但幸运的是,您可以从 Jedi 中提取此文件)。在 JanFX 中,请参阅 Stretch(我认为)功能。它提供了非常好的平滑效果(不如 Graphics32 但足够好),但速度要快得多。 Jedi 中的 JanFX.pas 存在错误:当 {$R} 为 ON 时不起作用。您需要定义 {$R-}。而已。 Jedi 的人进入了这个 bug :)

        【讨论】:

          【解决方案6】:

          对于任何类型的图像,您都可以使用这个:

          img := TIMage.create(nil);
          img.picture.loadfromfile('any_file_type');
          Result:= TBitmap.Create;
          result.Width := newWidth;
          result.Height := newHeight;
          Result.Canvas.Draw(0,0,img.Picture.Graphic);
          

          【讨论】:

            猜你喜欢
            • 2019-05-11
            • 1970-01-01
            • 1970-01-01
            • 2012-02-22
            • 1970-01-01
            • 2015-03-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多