unit alpha_gradient_main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, BGRABitmapTypes, BGRABitmap, LMessages; type { TForm1 } TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormPaint(Sender: TObject); private { private declarations } public { public declarations } ellipses: array of record x,y,w,h: integer; c: TBGRAPixel; end; pts: array[0..2] of TPointF; MovingPointIndex: integer; MovingOrigin: TPointF; procedure WMEraseBkgnd(var Message: TLMEraseBkgnd); message LM_ERASEBKGND; end; var Form1: TForm1; implementation uses BGRAGradientScanner, BGRATransform; procedure NicePoint(bmp: TBGRABitmap; x, y: single); begin bmp.EllipseAntialias(x,y,4,4,BGRA(0,0,0,192),1); bmp.EllipseAntialias(x,y,3,3,BGRA(255,255,255,192),1); bmp.EllipseAntialias(x,y,2,2,BGRA(0,0,0,192),1); end; {$R *.lfm} type { TMultiplyGradient } TMultiplyGradient = class(TBGRACustomScanner) function ScanAt(X, Y: Single): TBGRAPixel; override; end; { TMultiplyGradient } function TMultiplyGradient.ScanAt(X, Y: Single): TBGRAPixel; var fvalue: single; value: integer; begin fvalue := abs(x*y*255); if fvalue > 255 then value := 255 else value := round(fvalue); result := BGRA(value,value,value,255); end; { TForm1 } procedure TForm1.FormCreate(Sender: TObject); var i: Integer; begin setlength(ellipses,100); for i := 0 to high(ellipses) do with ellipses[i] do begin x := random(65536); y := random(65536); w := random(100)+5; h := random(100)+5; c := BGRA(random(256),random(256),random(256),random(128)+64); end; pts[0] := PointF(250,200); pts[1] := PointF(300,150); pts[2] := PointF(300,250); MovingPointIndex := -1; end; procedure TForm1.FormPaint(Sender: TObject); const ellipseRadius = 160; var bmp: TBGRABitmap; tx,ty: integer; i: Integer; ellipseLayer: TBGRABitmap; gradient: TBGRAGradientScanner; multigrad: TBGRAMultiGradient; affine: TBGRAAffineScannerTransform; multiply: TMultiplyGradient; mask: TBGRABitmap; ellipseLayerOffset: TPointF; begin tx := ClientWidth; ty := ClientHeight; if (tx=0) or (ty=0) then exit; //create background bmp := TBGRABitmap.Create(tx,ty, BGRAWhite); for i := 0 to high(ellipses) do with ellipses[i] do bmp.FillEllipseAntialias(x mod tx,y mod ty,w/2,h/2,c); //create center red-yellow ellipse ellipseLayerOffset := PointF((tx-(2*ellipseRadius+1))/2,(ty-(2*ellipseRadius+1))/2); ellipseLayer := TBGRABitmap.Create(2*ellipseRadius +1 + 1,2*ellipseRadius +1 +1); multiGrad := TBGRAMultiGradient.Create([BGRA(0,64,0),BGRA(160,160,0),BGRA(128,0,0)],[0,1/2,1],True); gradient := TBGRAGradientScanner.Create(multiGrad,gtRadial,PointF(0,0),PointF(1,0)); affine := TBGRAAffineScannerTransform.Create(gradient); affine.Scale(ellipseRadius*1.2,ellipseRadius*0.8); affine.RotateDeg(30); affine.Translate(ellipseRadius+frac(ellipseLayerOffset.X),ellipseRadius+frac(ellipseLayerOffset.Y)); ellipseLayer.FillEllipseAntialias(ellipseRadius+frac(ellipseLayerOffset.X),ellipseRadius+frac(ellipseLayerOffset.Y), ellipseRadius,ellipseRadius,BGRA(192,128,0)); ellipseLayer.FillEllipseAntialias(ellipseRadius+frac(ellipseLayerOffset.X),ellipseRadius+frac(ellipseLayerOffset.Y), ellipseRadius,ellipseRadius,affine); affine.Free; gradient.Free; multiGrad.Free; //apply multiply mask multiply := TMultiplyGradient.Create; affine := TBGRAAffineScannerTransform.Create(multiply); affine.Fit(pts[0],pts[1],pts[2]); affine.Translate(-trunc(ellipseLayerOffset.X),-trunc(ellipseLayerOffset.Y)); mask := TBGRABitmap.Create(ellipseLayer.Width,ellipseLayer.Height); mask.Fill(affine); ellipseLayer.ApplyMask(mask); mask.Free; affine.Free; multiply.Free; bmp.PutImage(trunc(ellipseLayerOffset.X),trunc(ellipseLayerOffset.Y),ellipseLayer,dmDrawWithTransparency); ellipseLayer.Free; for i := 0 to 2 do NicePoint(bmp,pts[i].x,pts[i].y); bmp.Draw(Canvas,0,0); bmp.Free; end; procedure TForm1.WMEraseBkgnd(var Message: TLMEraseBkgnd); begin // end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var maxDist,dist: single; mousePos,vect: TPointF; i: Integer; begin if Button <> mbLeft then exit; //select point to move MovingPointIndex := -1; maxDist := 10; mousePos := PointF(X,Y); for i := 0 to 2 do begin vect := pts[i] - mousePos; dist := sqrt(vect*vect); if dist < maxDist then begin maxDist := dist; MovingPointIndex := i; MovingOrigin := mousePos; end; end; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var mousePos: TPointF; begin if MovingPointIndex <> -1 then begin mousePos := PointF(X,Y); pts[MovingPointIndex].Offset(mousePos-MovingOrigin); Invalidate; MovingOrigin := mousePos; end; end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Button = mbLeft then MovingPointIndex := -1; end; end.