815 lines
25 KiB
Plaintext
815 lines
25 KiB
Plaintext
{*******************************************************}
|
|
{ }
|
|
{ Tocsg.Graphic }
|
|
{ }
|
|
{ Copyright (C) 2023 kku }
|
|
{ }
|
|
{*******************************************************}
|
|
|
|
unit Tocsg.Graphic;
|
|
|
|
interface
|
|
|
|
uses
|
|
Vcl.Graphics, System.SysUtils, Winapi.Windows, Vcl.Imaging.pngimage,
|
|
Winapi.GDIPAPI;
|
|
|
|
type
|
|
PColorMatrix = ^TColorMatrix;
|
|
|
|
function RotatePng(var aPng: TPNGImage; Angle: Extended): Boolean;
|
|
|
|
function RotatePngFile(sSrcPath, sDestpath: String; Angle: Extended): Boolean;
|
|
|
|
// RotateBitmap_STF() 이건 이미지 돌린 후 가로 넓이 AdjustSize 적용이 제대로 되지 않는다... 짤림
|
|
procedure RotateBitmap_STF(Bmp: Vcl.Graphics.TBitmap; Rads: Single; AdjustSize: Boolean;
|
|
BkColor: TColor = clNone; nAddWidth: Integer = 0);
|
|
procedure RotateBitmap_PlgBlt(Bmp: Vcl.Graphics.TBitmap; Rads: Single; AdjustSize: Boolean;
|
|
BkColor: TColor = clNone);
|
|
function ScalePercentBmp(bitmp: Vcl.Graphics.TBitmap; iPercent: Integer{100이면 원본}): Boolean;
|
|
|
|
function MakeColorMatrix(R, G, B, A: Single): TColorMatrix;
|
|
function MakeTransparentMatrix(A: Single): TColorMatrix;
|
|
|
|
procedure InitializeGDIPlus;
|
|
procedure FinalizeGDIPlus;
|
|
|
|
function DrawBitmapWaterEx(aDestDC: HDC; nX, nY: Integer; aSrcBmp: Vcl.Graphics.TBitmap; pCM: PColorMatrix = nil; nStretchW: Integer = 0; nStretchH: Integer = 0): Boolean;
|
|
function DrawBitmapWaterEx2(aDestDC: HDC; nX, nY: Integer; aSrcBmp: Vcl.Graphics.TBitmap; pCM: PColorMatrix = nil; nStretchW: Integer = 0; nStretchH: Integer = 0; fAngle: Single = 0): Boolean;
|
|
function DrawBitmapWaterEx3(
|
|
aDestDC: HDC;
|
|
nX, nY: Integer;
|
|
aSrcBmp: Vcl.Graphics.TBitmap;
|
|
pCM: PColorMatrix = nil;
|
|
nStretchW: Integer = 0;
|
|
nStretchH: Integer = 0;
|
|
fAngle: Single = 0
|
|
): Boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Tocsg.Safe, Tocsg.Exception, System.Classes, Winapi.GDIPOBJ, Winapi.ActiveX, System.UIConsts, System.Math;
|
|
|
|
// 이걸로 돌리면 아래에 검은선이 생긴다. 25_0401 10:07:20 kku
|
|
//function RotatePng(var aPng: TPNGImage; Angle: Extended): Boolean;
|
|
//
|
|
// {Supporting functions}
|
|
// function TrimInt(i, Min, Max: Integer): Integer;
|
|
// begin
|
|
// if i>Max then Result:=Max
|
|
// else if i<Min then Result:=Min
|
|
// else Result:=i;
|
|
// end;
|
|
// function IntToByte(i:Integer):Byte;
|
|
// begin
|
|
// if i>255 then Result:=255
|
|
// else if i<0 then Result:=0
|
|
// else Result:=i;
|
|
// end;
|
|
// function Min(A, B: Double): Double;
|
|
// begin
|
|
// if A < B then Result := A else Result := B;
|
|
// end;
|
|
// function Max(A, B: Double): Double;
|
|
// begin
|
|
// if A > B then Result := A else Result := B;
|
|
// end;
|
|
// function Ceil(A: Double): Integer;
|
|
// begin
|
|
// Result := Integer(Trunc(A));
|
|
// if Frac(A) > 0 then
|
|
// Inc(Result);
|
|
// end;
|
|
//
|
|
// {Calculates the png new size}
|
|
// function newsize: tsize;
|
|
// var
|
|
// fRadians: Extended;
|
|
// fCosine, fSine: Double;
|
|
// fPoint1x, fPoint1y, fPoint2x, fPoint2y, fPoint3x, fPoint3y: Double;
|
|
// fMinx, fMiny, fMaxx, fMaxy: Double;
|
|
// begin
|
|
// {Convert degrees to radians}
|
|
// fRadians := (2 * PI * Angle) / 360;
|
|
//
|
|
// fCosine := abs(cos(fRadians));
|
|
// fSine := abs(sin(fRadians));
|
|
//
|
|
// fPoint1x := (-apng.Height * fSine);
|
|
// fPoint1y := (apng.Height * fCosine);
|
|
// fPoint2x := (apng.Width * fCosine - apng.Height * fSine);
|
|
// fPoint2y := (apng.Height * fCosine + apng.Width * fSine);
|
|
// fPoint3x := (apng.Width * fCosine);
|
|
// fPoint3y := (apng.Width * fSine);
|
|
//
|
|
// fMinx := min(0,min(fPoint1x,min(fPoint2x,fPoint3x)));
|
|
// fMiny := min(0,min(fPoint1y,min(fPoint2y,fPoint3y)));
|
|
// fMaxx := max(fPoint1x,max(fPoint2x,fPoint3x));
|
|
// fMaxy := max(fPoint1y,max(fPoint2y,fPoint3y));
|
|
//
|
|
// Result.cx := ceil(fMaxx-fMinx);
|
|
// Result.cy := ceil(fMaxy-fMiny);
|
|
// end;
|
|
//type
|
|
// TFColor = record b,g,r:Byte end;
|
|
//var
|
|
// Top, Bottom, Left, Right, eww,nsw, fx,fy, wx,wy: Extended;
|
|
// cAngle, sAngle: Double;
|
|
// xDiff, yDiff, ifx,ify, px,py, ix,iy, x,y, cx, cy: Integer;
|
|
// nw,ne, sw,se: TFColor;
|
|
// anw,ane, asw,ase: Byte;
|
|
// P1,P2,P3:Pbytearray;
|
|
// A1,A2,A3: pbytearray;
|
|
// dst: TPNGImage;
|
|
// IsAlpha: Boolean;
|
|
// new_colortype: Integer;
|
|
//begin
|
|
// Result := false;
|
|
// try
|
|
// {Only allows RGB and RGBALPHA images}
|
|
// if not (apng.Header.ColorType in [COLOR_RGBALPHA, COLOR_RGB]) then
|
|
// raise Exception.Create('Only COLOR_RGBALPHA and COLOR_RGB formats' +
|
|
// ' are supported');
|
|
// IsAlpha := apng.Header.ColorType in [COLOR_RGBALPHA];
|
|
// if IsAlpha then new_colortype := COLOR_RGBALPHA else
|
|
// new_colortype := COLOR_RGB;
|
|
//
|
|
// {Creates a copy}
|
|
// dst := tpngobject.Create;
|
|
// with newsize do
|
|
// dst.createblank(new_colortype, 8, cx, cy);
|
|
// cx := dst.width div 2; cy := dst.height div 2;
|
|
//
|
|
// {Gather some variables}
|
|
// Angle:=angle;
|
|
// Angle:=-Angle*Pi/180;
|
|
// sAngle:=Sin(Angle);
|
|
// cAngle:=Cos(Angle);
|
|
// xDiff:=(Dst.Width-apng.Width)div 2;
|
|
// yDiff:=(Dst.Height-apng.Height)div 2;
|
|
//
|
|
// {Iterates over each line}
|
|
// for y:=0 to Dst.Height-1 do
|
|
// begin
|
|
// P3:=Dst.scanline[y];
|
|
// if IsAlpha then A3 := Dst.AlphaScanline[y];
|
|
// py:=2*(y-cy)+1;
|
|
// {Iterates over each column}
|
|
// for x:=0 to Dst.Width-1 do
|
|
// begin
|
|
// px:=2*(x-cx)+1;
|
|
// fx:=(((px*cAngle-py*sAngle)-1)/ 2+cx)-xDiff;
|
|
// fy:=(((px*sAngle+py*cAngle)-1)/ 2+cy)-yDiff;
|
|
// ifx:=Round(fx);
|
|
// ify:=Round(fy);
|
|
//
|
|
// {Only continues if it does not exceed image boundaries}
|
|
// if(ifx>-1)and(ifx<apng.Width)and(ify>-1)and(ify<apng.Height)then
|
|
// begin
|
|
// {Obtains data to paint the new pixel}
|
|
// eww:=fx-ifx;
|
|
// nsw:=fy-ify;
|
|
// iy:=TrimInt(ify+1,0,apng.Height-1);
|
|
// ix:=TrimInt(ifx+1,0,apng.Width-1);
|
|
// P1:=apng.scanline[ify];
|
|
// P2:=apng.scanline[iy];
|
|
// if IsAlpha then A1 := apng.alphascanline[ify];
|
|
// if IsAlpha then A2 := apng.alphascanline[iy];
|
|
// nw.r:=P1[ifx*3];
|
|
// nw.g:=P1[ifx*3+1];
|
|
// nw.b:=P1[ifx*3+2];
|
|
// if IsAlpha then anw:=A1[ifx];
|
|
// ne.r:=P1[ix*3];
|
|
// ne.g:=P1[ix*3+1];
|
|
// ne.b:=P1[ix*3+2];
|
|
// if IsAlpha then ane:=A1[ix];
|
|
// sw.r:=P2[ifx*3];
|
|
// sw.g:=P2[ifx*3+1];
|
|
// sw.b:=P2[ifx*3+2];
|
|
// if IsAlpha then asw:=A2[ifx];
|
|
// se.r:=P2[ix*3];
|
|
// se.g:=P2[ix*3+1];
|
|
// se.b:=P2[ix*3+2];
|
|
// if IsAlpha then ase:=A2[ix];
|
|
//
|
|
//
|
|
// {Defines the new pixel}
|
|
// Top:=nw.b+eww*(ne.b-nw.b);
|
|
// Bottom:=sw.b+eww*(se.b-sw.b);
|
|
// P3[x*3+2]:=IntToByte(Round(Top+nsw*(Bottom-Top)));
|
|
// Top:=nw.g+eww*(ne.g-nw.g);
|
|
// Bottom:=sw.g+eww*(se.g-sw.g);
|
|
// P3[x*3+1]:=IntToByte(Round(Top+nsw*(Bottom-Top)));
|
|
// Top:=nw.r+eww*(ne.r-nw.r);
|
|
// Bottom:=sw.r+eww*(se.r-sw.r);
|
|
// P3[x*3]:=IntToByte(Round(Top+nsw*(Bottom-Top)));
|
|
//
|
|
// {Only for alpha}
|
|
// if IsAlpha then
|
|
// begin
|
|
// Top:=anw+eww*(ane-anw);
|
|
// Bottom:=asw+eww*(ase-asw);
|
|
// A3[x]:=IntToByte(Round(Top+nsw*(Bottom-Top)));
|
|
// end;
|
|
//
|
|
// end;
|
|
// end;
|
|
// end;
|
|
//
|
|
// apng.assign(dst);
|
|
// dst.free;
|
|
// Result := true;
|
|
// except
|
|
// on E: Exception do
|
|
// ETgException.TraceException(E, 'Fail .. RotatePng()');
|
|
// end;
|
|
//end;
|
|
|
|
function RotatePng(var aPng: TPNGImage; Angle: Extended): Boolean;
|
|
|
|
function TrimInt(i, Min, Max: Integer): Integer;
|
|
begin
|
|
if i < Min then Result := Min
|
|
else if i > Max then Result := Max
|
|
else Result := i;
|
|
end;
|
|
|
|
function IntToByte(i: Integer): Byte;
|
|
begin
|
|
if i < 0 then Result := 0
|
|
else if i > 255 then Result := 255
|
|
else Result := i;
|
|
end;
|
|
|
|
function CeilEx(A: Double): Integer;
|
|
begin
|
|
Result := Trunc(A);
|
|
if Frac(A) > 0 then Inc(Result);
|
|
end;
|
|
|
|
function GetRotatedSize: TSize;
|
|
var
|
|
Radian, CosA, SinA: Extended;
|
|
x1, y1, x2, y2, x3, y3, x4, y4: Extended;
|
|
MinX, MaxX, MinY, MaxY: Extended;
|
|
begin
|
|
Radian := Angle * PI / 180;
|
|
CosA := Abs(Cos(Radian));
|
|
SinA := Abs(Sin(Radian));
|
|
|
|
// 원래 이미지 꼭짓점 회전 후 좌표 계산
|
|
x1 := 0 * CosA - 0 * SinA;
|
|
y1 := 0 * SinA + 0 * CosA;
|
|
|
|
x2 := 0 * CosA - aPng.Height * SinA;
|
|
y2 := 0 * SinA + aPng.Height * CosA;
|
|
|
|
x3 := aPng.Width * CosA - 0 * SinA;
|
|
y3 := aPng.Width * SinA + 0 * CosA;
|
|
|
|
x4 := aPng.Width * CosA - aPng.Height * SinA;
|
|
y4 := aPng.Width * SinA + aPng.Height * CosA;
|
|
|
|
MinX := Min(Min(x1, x2), Min(x3, x4));
|
|
MinY := Min(Min(y1, y2), Min(y3, y4));
|
|
MaxX := Max(Max(x1, x2), Max(x3, x4));
|
|
MaxY := Max(Max(y1, y2), Max(y3, y4));
|
|
|
|
Result.cx := CeilEx(MaxX - MinX);
|
|
Result.cy := CeilEx(MaxY - MinY);
|
|
end;
|
|
|
|
type
|
|
TFColor = record r, g, b: Byte; end;
|
|
|
|
var
|
|
dst, src: TPNGImage;
|
|
x, y, cx, cy, SrcW, SrcH, DstW, DstH: Integer;
|
|
fx, fy, px, py: Extended;
|
|
ifx, ify, ix, iy: Integer;
|
|
Radian, SinA, CosA: Extended;
|
|
eww, nsw: Extended;
|
|
IsAlpha: Boolean;
|
|
nw, ne, sw, se: TFColor;
|
|
anw, ane, asw, ase: Byte;
|
|
P1, P2, P3: PByteArray;
|
|
A1, A2, A3: PByteArray;
|
|
newColorType: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
if not (aPng.Header.ColorType in [COLOR_RGB, COLOR_RGBALPHA]) then
|
|
Exit;
|
|
|
|
IsAlpha := aPng.Header.ColorType = COLOR_RGBALPHA;
|
|
if IsAlpha then newColorType := COLOR_RGBALPHA else newColorType := COLOR_RGB;
|
|
|
|
SrcW := aPng.Width;
|
|
SrcH := aPng.Height;
|
|
|
|
Radian := -Angle * PI / 180;
|
|
SinA := Sin(Radian);
|
|
CosA := Cos(Radian);
|
|
|
|
// 원본 백업
|
|
src := TPNGImage.Create;
|
|
src.Assign(aPng);
|
|
|
|
// 새 이미지 생성
|
|
dst := TPNGImage.CreateBlank(newColorType, 8, GetRotatedSize.cx, GetRotatedSize.cy);
|
|
DstW := dst.Width;
|
|
DstH := dst.Height;
|
|
cx := DstW div 2;
|
|
cy := DstH div 2;
|
|
|
|
for y := 0 to DstH - 1 do
|
|
begin
|
|
P3 := dst.ScanLine[y];
|
|
if IsAlpha then A3 := dst.AlphaScanline[y];
|
|
py := 2 * (y - cy) + 1;
|
|
|
|
for x := 0 to DstW - 1 do
|
|
begin
|
|
px := 2 * (x - cx) + 1;
|
|
|
|
fx := (((px * CosA - py * SinA) - 1) / 2 + SrcW / 2);
|
|
fy := (((px * SinA + py * CosA) - 1) / 2 + SrcH / 2);
|
|
|
|
ifx := Floor(fx);
|
|
ify := Floor(fy);
|
|
|
|
if (ifx >= 0) and (ifx < SrcW - 1) and (ify >= 0) and (ify < SrcH - 1) then
|
|
begin
|
|
eww := fx - ifx;
|
|
nsw := fy - ify;
|
|
|
|
ix := ifx + 1;
|
|
iy := ify + 1;
|
|
|
|
P1 := src.ScanLine[ify];
|
|
P2 := src.ScanLine[iy];
|
|
if IsAlpha then
|
|
begin
|
|
A1 := src.AlphaScanline[ify];
|
|
A2 := src.AlphaScanline[iy];
|
|
end;
|
|
|
|
// 색상 복사
|
|
nw.r := P1[ifx * 3]; nw.g := P1[ifx * 3 + 1]; nw.b := P1[ifx * 3 + 2];
|
|
ne.r := P1[ix * 3]; ne.g := P1[ix * 3 + 1]; ne.b := P1[ix * 3 + 2];
|
|
sw.r := P2[ifx * 3]; sw.g := P2[ifx * 3 + 1]; sw.b := P2[ifx * 3 + 2];
|
|
se.r := P2[ix * 3]; se.g := P2[ix * 3 + 1]; se.b := P2[ix * 3 + 2];
|
|
|
|
if IsAlpha then
|
|
begin
|
|
anw := A1[ifx]; ane := A1[ix];
|
|
asw := A2[ifx]; ase := A2[ix];
|
|
end;
|
|
|
|
// 보간 계산
|
|
P3[x * 3 + 2] := IntToByte(Round(nw.b + eww * (ne.b - nw.b) + nsw * ((sw.b + eww * (se.b - sw.b)) - (nw.b + eww * (ne.b - nw.b)))));
|
|
P3[x * 3 + 1] := IntToByte(Round(nw.g + eww * (ne.g - nw.g) + nsw * ((sw.g + eww * (se.g - sw.g)) - (nw.g + eww * (ne.g - nw.g)))));
|
|
P3[x * 3 + 0] := IntToByte(Round(nw.r + eww * (ne.r - nw.r) + nsw * ((sw.r + eww * (se.r - sw.r)) - (nw.r + eww * (ne.r - nw.r)))));
|
|
|
|
if IsAlpha then
|
|
A3[x] := IntToByte(Round(anw + eww * (ane - anw) + nsw * ((asw + eww * (ase - asw)) - (anw + eww * (ane - anw)))));
|
|
end
|
|
else
|
|
begin
|
|
// 경계 바깥은 흰색 또는 투명 처리
|
|
P3[x * 3 + 0] := 255;
|
|
P3[x * 3 + 1] := 255;
|
|
P3[x * 3 + 2] := 255;
|
|
if IsAlpha then A3[x] := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
aPng.Assign(dst);
|
|
dst.Free;
|
|
src.Free;
|
|
Result := True;
|
|
end;
|
|
|
|
function RotatePngFile(sSrcPath, sDestpath: String; Angle: Extended): Boolean;
|
|
var
|
|
png: TPngImage;
|
|
begin
|
|
Result := false;
|
|
try
|
|
Guard(png, TPngImage.Create);
|
|
png.LoadFromFile(sSrcPath);
|
|
if RotatePng(png, Angle) then
|
|
begin
|
|
png.SaveToFile(sDestpath);
|
|
Result := true;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. RotatePngFile()');
|
|
end;
|
|
end;
|
|
|
|
// SetWorldTransform
|
|
procedure RotateBitmap_STF(Bmp: Vcl.Graphics.TBitmap; Rads: Single; AdjustSize: Boolean;
|
|
BkColor: TColor = clNone; nAddWidth: Integer = 0);
|
|
var
|
|
C: Single;
|
|
S: Single;
|
|
XForm: tagXFORM;
|
|
Tmp: Vcl.Graphics.TBitmap;
|
|
begin
|
|
C := Cos(Rads);
|
|
S := Sin(Rads);
|
|
XForm.eM11 := C;
|
|
XForm.eM12 := S;
|
|
XForm.eM21 := -S;
|
|
XForm.eM22 := C;
|
|
Tmp := Vcl.Graphics.TBitmap.Create;
|
|
try
|
|
Tmp.TransparentColor := Bmp.TransparentColor;
|
|
Tmp.TransparentMode := Bmp.TransparentMode;
|
|
Tmp.Transparent := Bmp.Transparent;
|
|
Tmp.Canvas.Brush.Color := BkColor;
|
|
if AdjustSize then
|
|
begin
|
|
Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S)) + nAddWidth;
|
|
Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
|
|
XForm.eDx := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
|
|
XForm.eDy := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
|
|
end else begin
|
|
Tmp.Width := Bmp.Width;
|
|
Tmp.Height := Bmp.Height;
|
|
XForm.eDx := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
|
|
XForm.eDy := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
|
|
end;
|
|
SetGraphicsMode(Tmp.Canvas.Handle, GM_ADVANCED);
|
|
SetWorldTransform(Tmp.Canvas.Handle, XForm);
|
|
BitBlt(Tmp.Canvas.Handle, 0, 0, Tmp.Width, Tmp.Height, Bmp.Canvas.Handle,
|
|
0, 0, SRCCOPY);
|
|
Bmp.Assign(Tmp);
|
|
finally
|
|
Tmp.Free;
|
|
end;
|
|
end;
|
|
|
|
// PlgBlt
|
|
procedure RotateBitmap_PlgBlt(Bmp: Vcl.Graphics.TBitmap; Rads: Single; AdjustSize: Boolean;
|
|
BkColor: TColor = clNone);
|
|
var
|
|
C: Single;
|
|
S: Single;
|
|
Tmp: Vcl.Graphics.TBitmap;
|
|
OffsetX: Single;
|
|
OffsetY: Single;
|
|
Points: array[0..2] of TPoint;
|
|
begin
|
|
try
|
|
C := Cos(Rads);
|
|
S := Sin(Rads);
|
|
Tmp := Vcl.Graphics.TBitmap.Create;
|
|
try
|
|
Tmp.TransparentColor := Bmp.TransparentColor;
|
|
Tmp.TransparentMode := Bmp.TransparentMode;
|
|
Tmp.Transparent := Bmp.Transparent;
|
|
Tmp.Canvas.Brush.Color := BkColor;
|
|
if AdjustSize then
|
|
begin
|
|
Tmp.Width := Round(Bmp.Width * Abs(C) + Bmp.Height * Abs(S));
|
|
Tmp.Height := Round(Bmp.Width * Abs(S) + Bmp.Height * Abs(C));
|
|
OffsetX := (Tmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
|
|
OffsetY := (Tmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
|
|
end
|
|
else
|
|
begin
|
|
Tmp.Width := Bmp.Width;
|
|
Tmp.Height := Bmp.Height;
|
|
OffsetX := (Bmp.Width - Bmp.Width * C + Bmp.Height * S) / 2;
|
|
OffsetY := (Bmp.Height - Bmp.Width * S - Bmp.Height * C) / 2;
|
|
end;
|
|
Points[0].X := Round(OffsetX);
|
|
Points[0].Y := Round(OffsetY);
|
|
Points[1].X := Round(OffsetX + Bmp.Width * C);
|
|
Points[1].Y := Round(OffsetY + Bmp.Width * S);
|
|
Points[2].X := Round(OffsetX - Bmp.Height * S);
|
|
Points[2].Y := Round(OffsetY + Bmp.Height * C);
|
|
PlgBlt(Tmp.Canvas.Handle, Points, Bmp.Canvas.Handle, 0, 0, Bmp.Width,
|
|
Bmp.Height, 0, 0, 0);
|
|
Bmp.Assign(Tmp);
|
|
finally
|
|
Tmp.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. RotateBitmap_PlgBlt()');
|
|
end;
|
|
end;
|
|
|
|
function ScalePercentBmp(bitmp: Vcl.Graphics.TBitmap; iPercent: Integer{100이면 원본}): Boolean;
|
|
var
|
|
TmpBmp: Vcl.Graphics.TBitmap;
|
|
ARect: TRect;
|
|
h, w: Real;
|
|
hi, wi: Integer;
|
|
begin
|
|
Result := False;
|
|
try
|
|
TmpBmp := Vcl.Graphics.TBitmap.Create;
|
|
try
|
|
// h := bitmp.Height * (iPercent / 100);
|
|
// w := bitmp.Width * (iPercent / 100);
|
|
// hi := StrToInt(FormatFloat('#', h)) + bitmp.Height;
|
|
// wi := StrToInt(FormatFloat('#', w)) + bitmp.Width;
|
|
hi := Round((iPercent / 100) * bitmp.Height);
|
|
wi := Round((iPercent / 100) * bitmp.Width);
|
|
TmpBmp.Width := wi;
|
|
TmpBmp.Height := hi;
|
|
ARect := System.Classes.Rect(0, 0, wi, hi);
|
|
TmpBmp.Canvas.StretchDraw(ARect, Bitmp);
|
|
bitmp.Assign(TmpBmp);
|
|
finally
|
|
TmpBmp.Free;
|
|
end;
|
|
Result := True;
|
|
except
|
|
{$IFDEF DEBUG}
|
|
ASSERT(false);
|
|
{$ENDIF}
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function MakeColorMatrix(R, G, B, A: Single): TColorMatrix;
|
|
begin
|
|
Result[0, 0] := R; Result[0, 1] := 0; Result[0, 2] := 0; Result[0, 3] := 0; Result[0, 4] := 0;
|
|
Result[1, 0] := 0; Result[1, 1] := G; Result[1, 2] := 0; Result[1, 3] := 0; Result[1, 4] := 0;
|
|
Result[2, 0] := 0; Result[2, 1] := 0; Result[2, 2] := B; Result[2, 3] := 0; Result[2, 4] := 0;
|
|
Result[3, 0] := 0; Result[3, 1] := 0; Result[3, 2] := 0; Result[3, 3] := A; Result[3, 4] := 0;
|
|
Result[4, 0] := 0; Result[4, 1] := 0; Result[4, 2] := 0; Result[4, 3] := 0; Result[4, 4] := 1;
|
|
end;
|
|
|
|
function MakeTransparentMatrix(A: Single): TColorMatrix;
|
|
begin
|
|
ZeroMemory(@Result, SizeOf(Result));
|
|
Result[3, 3] := A;
|
|
end;
|
|
|
|
procedure InitializeGDIPlus;
|
|
var
|
|
StartupInput: TGdiplusStartupInput;
|
|
begin
|
|
StartupInput.GdiplusVersion := 1;
|
|
StartupInput.DebugEventCallback := nil;
|
|
StartupInput.SuppressBackgroundThread := False;
|
|
StartupInput.SuppressExternalCodecs := False;
|
|
|
|
if GdiplusStartup(GdiplusToken, @StartupInput, nil) <> Ok then
|
|
exit;
|
|
// raise Exception.Create('Failed to initialize GDI+');
|
|
end;
|
|
|
|
procedure FinalizeGDIPlus;
|
|
begin
|
|
GdiplusShutdown(GdiplusToken);
|
|
end;
|
|
|
|
function MakePointF(X, Y: Single): TGPPointF;
|
|
begin
|
|
Result.X := X;
|
|
Result.Y := Y;
|
|
end;
|
|
|
|
function DrawBitmapWaterEx(aDestDC: HDC; nX, nY: Integer; aSrcBmp: Vcl.Graphics.TBitmap; pCM: PColorMatrix = nil; nStretchW: Integer = 0; nStretchH: Integer = 0): Boolean;
|
|
var
|
|
GPGraphics: TGPGraphics;
|
|
ms: TMemoryStream;
|
|
// GPStream: IStream;
|
|
GPImg: TGPImage;
|
|
ImageAttributes: TGPImageAttributes;
|
|
GPRect: TGPRect;
|
|
nWW, nHH: Integer;
|
|
Matrix: TGPMatrix;
|
|
begin
|
|
Result := true;
|
|
try
|
|
// Guard() 쓰면 Invalid Pointer 예외뜸 24_0411 13:44:29 kku
|
|
ms := TMemoryStream.Create; // 얘는 (GPStream: IStream) 여기서 알아서 해제함 24_0411 13:51:35 kku
|
|
aSrcBmp.SaveToStream(ms);
|
|
ms.Position := 0;
|
|
|
|
Guard(GPGraphics, TGPGraphics.Create(aDestDC));
|
|
GPGraphics.SetPageScale(0.33);
|
|
|
|
// IStream 메모리 해제를 위해 여기에 변수 선언해야 함 24_0125 10:58:12 kku
|
|
var GPStream: IStream := TStreamAdapter.Create(ms, soOwned) as IStream;
|
|
Guard(GPImg, TGPImage.Create(GPStream));
|
|
Guard(ImageAttributes, TGPImageAttributes.Create);
|
|
|
|
// 흰색 배경 투명처리
|
|
ImageAttributes.SetColorKey(System.UIConsts.MakeColor(255, 255, 255), System.UIConsts.MakeColor(255, 255, 255));
|
|
// 빨간색 배경 투명처리
|
|
// ImageAttributes.SetColorKey(System.UIConsts.MakeColor(255, 0, 0), System.UIConsts.MakeColor(255, 0, 0));
|
|
|
|
// ImageAttributes.SetColorMatrix(MakeColorMatrix(0.1, 0.1, 0.5, 0.2)); // Gray, 하늘색
|
|
// ImageAttributes.SetColorMatrix(MakeColorMatrix(0.1, 0.5, 0.5, 0.2)); // Gray, 녹색
|
|
// ImageAttributes.SetColorMatrix(MakeColorMatrix(0.1, 1.1, 0.5, 0.2)); // Gray, 연두색
|
|
// ImageAttributes.SetColorMatrix(MakeColorMatrix(0.2, 0.2, 0.2, 0.2)); // Gray, 실버
|
|
// ImageAttributes.SetColorMatrix(MakeColorMatrix(0.2, 0.2, 0.2, 0.3)); // Gray, 회색
|
|
if pCM <> nil then
|
|
// ImageAttributes.SetColorMatrix(pCM^, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap)
|
|
ImageAttributes.SetColorMatrix(pCM^)
|
|
else
|
|
ImageAttributes.SetColorMatrix(MakeColorMatrix(0.2, 0.2, 0.2, 0.3)); // Gray, 회색
|
|
// ImageAttributes.SetColorMatrix(MakeTransparentMatrix(0.4)); // 그 외 mode, type 옵션 의미 없음..
|
|
|
|
nWW := GPImg.GetWidth;
|
|
nHH := GPImg.GetHeight;
|
|
|
|
// var GPUnit: TUnit := GPGraphics.GetPageUnit;
|
|
GPRect.X := nX; // nX div 2; // 이렇게 해야 위치가 맞음... 이유는 아직 모름
|
|
GPRect.Y := nY; // nY div 2; // 이렇게 해야 위치가 맞음... 이유는 아직 모름
|
|
GPRect.Width := nWW;
|
|
GPRect.Height := nHH;
|
|
|
|
if nStretchW > 0 then
|
|
GPRect.Width := nStretchW;
|
|
if nStretchH > 0 then
|
|
GPRect.Height := nStretchH;
|
|
|
|
GPGraphics.DrawImage(GPImg, GPRect, 0, 0, Round(nWW), Round(nHH), UnitPixel, ImageAttributes);
|
|
GPGraphics.Flush(FlushIntentionFlush);
|
|
except
|
|
Result := false;
|
|
end;
|
|
end;
|
|
|
|
function DrawBitmapWaterEx2(aDestDC: HDC; nX, nY: Integer; aSrcBmp: Vcl.Graphics.TBitmap; pCM: PColorMatrix = nil; nStretchW: Integer = 0; nStretchH: Integer = 0; fAngle: Single = 0): Boolean;
|
|
var
|
|
GPGraphics: TGPGraphics;
|
|
ms: TMemoryStream;
|
|
GPImg: TGPImage;
|
|
ImageAttributes: TGPImageAttributes;
|
|
GPRect: TGPRect;
|
|
nWW, nHH: Integer;
|
|
offX, offY: Integer;
|
|
SavedDC: Integer;
|
|
Matrix: TGPMatrix;
|
|
begin
|
|
Result := true;
|
|
try
|
|
SavedDC := SaveDC(aDestDC);
|
|
try
|
|
ms := TMemoryStream.Create;
|
|
aSrcBmp.SaveToStream(ms);
|
|
ms.Position := 0;
|
|
|
|
Guard(GPGraphics, TGPGraphics.Create(aDestDC));
|
|
GPGraphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); // 보간법: 고품질
|
|
GPGraphics.SetSmoothingMode(SmoothingModeAntiAlias); // 안티앨리어싱
|
|
GPGraphics.SetPixelOffsetMode(PixelOffsetModeHighQuality); // 픽셀 오프셋 품질
|
|
|
|
// ★ SetPageScale 는 제거하는 걸 추천 (좌표 꼬임 방지)
|
|
// GPGraphics.SetPageScale(0.33);
|
|
// GPGraphics.SetPageUnit(UnitPixel);
|
|
|
|
var GPStream: IStream := TStreamAdapter.Create(ms, soOwned) as IStream;
|
|
Guard(GPImg, TGPImage.Create(GPStream));
|
|
Guard(ImageAttributes, TGPImageAttributes.Create);
|
|
|
|
ImageAttributes.SetColorKey(
|
|
System.UIConsts.MakeColor(255, 255, 255),
|
|
System.UIConsts.MakeColor(255, 255, 255)
|
|
);
|
|
|
|
if pCM <> nil then
|
|
ImageAttributes.SetColorMatrix(pCM^)
|
|
else
|
|
ImageAttributes.SetColorMatrix(MakeColorMatrix(0.2, 0.2, 0.2, 0.3));
|
|
|
|
nWW := GPImg.GetWidth;
|
|
nHH := GPImg.GetHeight;
|
|
|
|
GPRect.X := nX;
|
|
GPRect.Y := nY;
|
|
|
|
GPRect.Width := nWW;
|
|
GPRect.Height := nHH;
|
|
|
|
if nStretchW > 0 then
|
|
GPRect.Width := nStretchW;
|
|
if nStretchH > 0 then
|
|
GPRect.Height := nStretchH;
|
|
|
|
// 프린터 물리 offset 구하기
|
|
offX := GetDeviceCaps(aDestDC, PHYSICALOFFSETX);
|
|
offY := GetDeviceCaps(aDestDC, PHYSICALOFFSETY);
|
|
GPGraphics.TranslateTransform(offX, offY);
|
|
|
|
if fAngle <> 0 then
|
|
begin
|
|
Matrix := TGPMatrix.Create;
|
|
try
|
|
Matrix.RotateAt(fAngle, MakePointF(GPRect.X, GPRect.Y));
|
|
GPGraphics.SetTransform(Matrix);
|
|
finally
|
|
Matrix.Free;
|
|
end;
|
|
end;
|
|
|
|
GPGraphics.DrawImage(GPImg, GPRect, 0, 0, nWW, nHH, UnitPixel, ImageAttributes);
|
|
|
|
GPGraphics.Flush(FlushIntentionFlush);
|
|
finally
|
|
RestoreDC(aDestDC, SavedDC);
|
|
end;
|
|
except
|
|
Result := false;
|
|
end;
|
|
end;
|
|
function DrawBitmapWaterEx3(aDestDC: HDC; nX, nY: Integer; aSrcBmp: Vcl.Graphics.TBitmap; pCM: PColorMatrix = nil; nStretchW: Integer = 0; nStretchH: Integer = 0; fAngle: Single = 0): Boolean;
|
|
var
|
|
GPGraphics: TGPGraphics;
|
|
ms: TMemoryStream;
|
|
GPImg: TGPImage;
|
|
ImageAttributes: TGPImageAttributes;
|
|
GPRect: TGPRect;
|
|
nWW, nHH: Integer;
|
|
offX, offY: Integer;
|
|
SavedDC: Integer;
|
|
Matrix: TGPMatrix;
|
|
begin
|
|
Result := true;
|
|
try
|
|
SavedDC := SaveDC(aDestDC);
|
|
try
|
|
ms := TMemoryStream.Create;
|
|
aSrcBmp.SaveToStream(ms);
|
|
ms.Position := 0;
|
|
|
|
Guard(GPGraphics, TGPGraphics.Create(aDestDC));
|
|
GPGraphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); // 보간법: 고품질
|
|
GPGraphics.SetSmoothingMode(SmoothingModeAntiAlias); // 안티앨리어싱
|
|
GPGraphics.SetPixelOffsetMode(PixelOffsetModeHighQuality); // 픽셀 오프셋 품질
|
|
|
|
// ★ SetPageScale 는 제거하는 걸 추천 (좌표 꼬임 방지)
|
|
// GPGraphics.SetPageScale(0.33);
|
|
GPGraphics.SetPageUnit(UnitPixel);
|
|
|
|
var GPStream: IStream := TStreamAdapter.Create(ms, soOwned) as IStream;
|
|
Guard(GPImg, TGPImage.Create(GPStream));
|
|
Guard(ImageAttributes, TGPImageAttributes.Create);
|
|
|
|
ImageAttributes.SetColorKey(
|
|
System.UIConsts.MakeColor(255, 255, 255),
|
|
System.UIConsts.MakeColor(255, 255, 255)
|
|
);
|
|
|
|
if pCM <> nil then
|
|
ImageAttributes.SetColorMatrix(pCM^)
|
|
else
|
|
ImageAttributes.SetColorMatrix(MakeColorMatrix(0.2, 0.2, 0.2, 0.3));
|
|
|
|
nWW := GPImg.GetWidth;
|
|
nHH := GPImg.GetHeight;
|
|
|
|
GPRect.X := nX;
|
|
GPRect.Y := nY;
|
|
|
|
GPRect.Width := nWW;
|
|
GPRect.Height := nHH;
|
|
|
|
if nStretchW > 0 then
|
|
GPRect.Width := nStretchW;
|
|
if nStretchH > 0 then
|
|
GPRect.Height := nStretchH;
|
|
|
|
// 프린터 물리 offset 구하기
|
|
offX := GetDeviceCaps(aDestDC, PHYSICALOFFSETX);
|
|
offY := GetDeviceCaps(aDestDC, PHYSICALOFFSETY);
|
|
GPGraphics.TranslateTransform(offX, offY);
|
|
|
|
if fAngle <> 0 then
|
|
begin
|
|
Matrix := TGPMatrix.Create;
|
|
try
|
|
Matrix.RotateAt(fAngle, MakePointF(GPRect.X, GPRect.Y));
|
|
GPGraphics.SetTransform(Matrix);
|
|
finally
|
|
Matrix.Free;
|
|
end;
|
|
end;
|
|
|
|
GPGraphics.DrawImage(GPImg, GPRect, 0, 0, nWW, nHH, UnitPixel, ImageAttributes);
|
|
|
|
GPGraphics.Flush(FlushIntentionFlush);
|
|
finally
|
|
RestoreDC(aDestDC, SavedDC);
|
|
end;
|
|
except
|
|
Result := false;
|
|
end;
|
|
end;
|
|
|
|
end.
|