본문 바로가기

언어/delphi

TBitmap 원본으로부터 크기조정한 비트맵 얻기 (Transparent, Antialiased)

출처: http://www.delmadang.com/community/bbs_view.asp?bbsNo=3&bbsCat=0&indx=452991&page=1


비트맵의 PixelFormat 속성이 pf32Bit인 TBitmap 원본으로부터 크기조정한

비트맵을 얻는 소스입니다.

Andrew Jameson의 오리지널 소스에서 Transparency 값이 유지되도록 수정한 것과

GDI+ API를 이용한 것 중에서 선택할 수 있습니다.


type 
  TRGBAArray = array[Word] of TRGBQUAD;
  pRGBAArray = ^TRGBAArray;

// Modified(to reserve transparency) version of Andrew Jameson's work.
// Src 비트맵 이미지를 Dst 비트맵의 크기에 맞추어 Dst 비트맵에 복사한다.
function SmoothResize(Src, Dst: TBitmap): BOOL;
var
   x, y: Integer;
   xP, yP: Integer;
   xP2, yP2: Integer;
   SrcLine1, SrcLine2: pRGBAArray;
   t3: Integer;
   z, z2, iz2: Integer;
   DstLine: pRGBAArray;
   DstGap: Integer;
   w1, w2, w3, w4: Integer;
begin
   Src.PixelFormat := pf32Bit;
   Dst.PixelFormat := pf32Bit;

   if (Src.Width = Dst.Width) and (Src.Height = Dst.Height) then
   begin
     Result := true;
     Dst.Assign(Src);
   end else
   try
     DstLine := Dst.ScanLine[0];
     DstGap  := Integer(Dst.ScanLine[1]) - Integer(DstLine);

     xP2 := MulDiv(pred(Src.Width), $10000, Dst.Width);
     yP2 := MulDiv(pred(Src.Height), $10000, Dst.Height);
     yP  := 0;

     for y := 0 to pred(Dst.Height) do
     begin
       xP := 0;
       SrcLine1 := Src.ScanLine[yP shr 16];

       if (yP shr 16 < pred(Src.Height)) then
         SrcLine2 := Src.ScanLine[succ(yP shr 16)]
       else
         SrcLine2 := Src.ScanLine[yP shr 16];

       z2  := succ(yP and $FFFF);
       iz2 := succ((not yp) and $FFFF);
       for x := 0 to pred(Dst.Width) do
       begin
         t3 := xP shr 16;
         z  := xP and $FFFF;
         w2 := MulDiv(z, iz2, $10000);
         w1 := iz2 - w2;
         w4 := MulDiv(z, z2, $10000);
         w3 := z2 - w4;
         DstLine[x].rgbRed := (SrcLine1[t3].rgbRed * w1 +
           SrcLine1[t3 + 1].rgbRed * w2 +
           SrcLine2[t3].rgbRed * w3 + SrcLine2[t3 + 1].rgbRed * w4) shr 16;
         DstLine[x].rgbGreen := (SrcLine1[t3].rgbGreen * w1 +
           SrcLine1[t3 + 1].rgbGreen * w2 +
           SrcLine2[t3].rgbGreen * w3 + SrcLine2[t3 + 1].rgbGreen * w4) shr 16;
         DstLine[x].rgbBlue := (SrcLine1[t3].rgbBlue * w1 +
           SrcLine1[t3 + 1].rgbBlue * w2 +
           SrcLine2[t3].rgbBlue * w3 + SrcLine2[t3 + 1].rgbBlue * w4) shr 16;
         DstLine[x].rgbReserved := (SrcLine1[t3].rgbReserved * w1 +
           SrcLine1[t3 + 1].rgbReserved * w2 +
           SrcLine2[t3].rgbReserved * w3 + SrcLine2[t3 + 1].rgbReserved * w4) shr 16;
         Inc(xP, xP2);
       end; {for}
       Inc(yP, yP2);
       DstLine := pRGBAArray(Integer(DstLine) + DstGap);
     end; {for}

     Result := true;
   except
     Result := false;
   end; {try}
end; {SmoothResize}  

//-------------------------

(사용 함수)
{$DEFINE USE_GDIPLUS}
function GetResizedBitmap(Bmp: TBitmap; NewSize: TSize): TBitmap;
{$IFDEF USE_GDIPLUS}
var
  InputBmp: TGPBitmap;
  Graphics: TGPGraphics;
  x, y: integer;
  P: PARGBArray;
  GPColor: TGPColor;

  // SrcBmp: TGPBitmap;
  // pixels: pInt;
  BitmapData: TBitmapData;
  R: TGPRect;
  p2: pbyte;
{$ENDIF}
begin
  if not Assigned(Bmp) then
    exit;

  // pixel당 24bit 또는 32 bit로 이미지를 구성하는 Bitmap만 처리한다.
  if (Bmp.PixelFormat <> pf24Bit) and (Bmp.PixelFormat <> pf32Bit) then
    exit;

  // create graphics object for output image
  try
    Result := TBitmap.Create;
    Result.PixelFormat := bmp.PixelFormat;
    Result.Width := NewSize.cx;
    Result.Height := NewSize.cy;
    {$IFNDEF USE_GDIPLUS}
    SmoothResize(bmp, Result);  // GDI+ 함수를 이용한 이미지 크기조정은 아래 코드 참조.
    {$ENDIF}
  except
    FreeAndNil(Result);
    exit;
  end;

  {$IFNDEF USE_GDIPLUS}
  exit;
  {$ENDIF}

  {$IFDEF USE_GDIPLUS}
  // 아래와 같이 Bitmap의 핸들을 이용한 이미지 취득시는 알파채널의 값을 잃은 이미지(=불투명 이미지)를
  // 얻는다.
 { if (Bmp.PixelFormat in [pf1Bit, pf4Bit, pf8Bit]) then
    InputBmp := TGPBitmap.Create(Bmp.Handle, Bmp.Palette)
  else
    InputBmp := TGPBitmap.Create(Bmp.Handle, 0);
  }

  // 아래와 같이 픽셀단위 또는 라인 단위로 이미지 데이터를 직접 복사하면 알파채널의 값이
  // 유지된다.
  // 또 다른 방법으로 DIB를 취득해서 이미지를 데이터를 한번에 복사하는 방법이 있는데 이 것은 
  // 별도로 게시한 "알파채널의 값을 잃지 않고 TBitmap으로 부터 GDI+ TGPBitmap을 얻는 함수"에
  // 나와있다.

  R := MakeRect(0, 0, Bmp.Width, Bmp.Height);      // 픽셀단위로 처리시는 불필요
  if Bmp.AlphaFormat = afPremultiplied then
  begin
    InputBmp := TGPBitmap.Create(Bmp.Width, Bmp.Height, PixelFormat32bppPARGB);
    InputBmp.LockBits(R, ImageLockModeWrite, PixelFormat32bppPARGB, BitmapData); // 픽셀단위로 처리시는 불필요
  end else
  begin
    InputBmp := TGPBitmap.Create(Bmp.Width, Bmp.Height, PixelFormat32bppARGB);
    InputBmp.LockBits(R, ImageLockModeWrite, PixelFormat32bppARGB, BitmapData);  // 픽셀단위로 처리시는 불필요
  end;

  p2 := BitmapData.Scan0;
  for y := 0 to Bmp.Height - 1 do
  begin
   // 픽셀단위로 처리시는 아래 코드를 이용한다.
   { for x := 0 to (Bmp.Width - 1) do
    begin
      GPColor := MakeColor(P[x].rgbReserved, P[x].rgbRed, P[x].rgbGreen, P[x].rgbBlue);
      InputBmp.SetPixel(x, y, GPColor);
    end; }

    // 라인 단위로 처리시는 아래 코드를 이용한다.
    P := Bmp.Scanline[y];
    CopyMemory(p2, p, Bmp.Width * 4);
    inc(p2, BitmapData.Stride);
  end;

  InputBmp.UnlockBits(BitmapData);   // 픽셀단위로 처리시는 불필요

  try
    Graphics := TGPGraphics.Create(Result.Canvas.Handle);
    try
      // set the composition mode to copy
      Graphics.SetCompositingMode(CompositingModeSourceCopy);
      // set high quality rendering modes
      Graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
      Graphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);
      Graphics.SetSmoothingMode(SmoothingModeHighQuality);
      // draw the input image on the output in modified size
      Graphics.DrawImage(InputBmp, 0, 0, Result.Width, Result.Height);
    finally
      Graphics.Free;
    end;
  finally
    InputBmp.Free;
  end;

  {$ENDIF}
end;