BSOne.SFC/Tocsg.Lib/VCL/Tocsg.Graphic.pas

818 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
if GDIPlusToken = 0 then
begin
StartupInput.GdiplusVersion := 1;
StartupInput.DebugEventCallback := nil;
StartupInput.SuppressBackgroundThread := False;
StartupInput.SuppressExternalCodecs := False;
if GdiplusStartup(GdiplusToken, @StartupInput, nil) <> Ok then
exit;
end;
// 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.