Last active
June 13, 2022 03:11
-
-
Save viniciusfbb/7a85025bc787a83e0b445a38008a237f to your computer and use it in GitHub Desktop.
[Skia4Delphi] Paint gradient color following a stroke path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit StrokePathGradient; | |
interface | |
uses | |
System.UITypes, Skia; | |
procedure DrawPathEndSmothing(const ACanvas: ISkCanvas; const APath: ISkPath; | |
const APaint: ISkPaint; const AInnerColor, AOuterColor: TAlphaColor; | |
AStart, AStop: Single; const AInterpolations: Integer); | |
implementation | |
uses | |
System.Types, System.Math, System.Math.Vectors; | |
procedure DrawPathEndSmothing(const ACanvas: ISkCanvas; const APath: ISkPath; | |
const APaint: ISkPaint; const AInnerColor, AOuterColor: TAlphaColor; | |
AStart, AStop: Single; const AInterpolations: Integer); | |
var | |
LDrawBounds: TRectF; | |
function InterpolateColor(const Start, Stop: TAlphaColor; T: Single): TAlphaColor; | |
begin | |
TAlphaColorRec(Result).A := TAlphaColorRec(Start).A + Trunc((TAlphaColorRec(Stop).A - TAlphaColorRec(Start).A) * T); | |
TAlphaColorRec(Result).R := TAlphaColorRec(Start).R + Trunc((TAlphaColorRec(Stop).R - TAlphaColorRec(Start).R) * T); | |
TAlphaColorRec(Result).G := TAlphaColorRec(Start).G + Trunc((TAlphaColorRec(Stop).G - TAlphaColorRec(Start).G) * T); | |
TAlphaColorRec(Result).B := TAlphaColorRec(Start).B + Trunc((TAlphaColorRec(Stop).B - TAlphaColorRec(Start).B) * T); | |
end; | |
procedure ClipTrimPath(const ACanvas: ISkCanvas; APaint: ISkPaint; | |
const APath: ISkPath; const AStart, AStop: Single); | |
begin | |
APaint := TSkPaint.Create(APaint); | |
APaint.PathEffect := TSkPathEffect.MakeTrim(AStart, AStop, TSkPathEffectTrimMode.Normal); | |
ACanvas.ClipPath(APaint.GetFillPath(APath), TSkClipOp.Intersect, True); | |
end; | |
function GetBeginPicture(const APaint: ISkPaint): ISkPicture; | |
var | |
LPictureRecorder: ISkPictureRecorder; | |
LCanvas: ISkCanvas; | |
LProgress: Single; | |
I: Integer; | |
begin | |
if AStart < TEpsilon.Position then | |
Exit(nil); | |
LPictureRecorder := TSkPictureRecorder.Create; | |
LCanvas := LPictureRecorder.BeginRecording(LDrawBounds); | |
try | |
for I := AInterpolations downto 0 do | |
begin | |
LProgress := I / AInterpolations; | |
APaint.Color := InterpolateColor(AOuterColor, AInnerColor, I / Max(AInterpolations - 1, 1)); | |
APaint.PathEffect := TSkPathEffect.MakeTrim(LProgress * AStart, Min(1 - AStop, LProgress * AStart + AStart / AInterpolations), TSkPathEffectTrimMode.Normal); | |
LCanvas.DrawPath(APath, APaint); | |
end; | |
finally | |
Result := LPictureRecorder.FinishRecording; | |
end; | |
end; | |
function GetEndPicture(const APaint: ISkPaint): ISkPicture; | |
var | |
LPictureRecorder: ISkPictureRecorder; | |
LCanvas: ISkCanvas; | |
LProgress: Single; | |
I: Integer; | |
begin | |
if AStop < TEpsilon.Position then | |
Exit(nil); | |
LPictureRecorder := TSkPictureRecorder.Create; | |
LCanvas := LPictureRecorder.BeginRecording(LDrawBounds); | |
try | |
for I := AInterpolations downto 0 do | |
begin | |
LProgress := I / AInterpolations; | |
APaint.Color := InterpolateColor(AOuterColor, AInnerColor, I / Max(AInterpolations - 1, 1)); | |
APaint.PathEffect := TSkPathEffect.MakeTrim(Max(AStart, 1 - LProgress * AStop - AStop / AInterpolations), 1 - LProgress * AStop, TSkPathEffectTrimMode.Normal); | |
LCanvas.DrawPath(APath, APaint); | |
end; | |
finally | |
Result := LPictureRecorder.FinishRecording; | |
end; | |
end; | |
function GetInnerPicture(APaint: ISkPaint): ISkPicture; | |
var | |
LPictureRecorder: ISkPictureRecorder; | |
LCanvas: ISkCanvas; | |
begin | |
LPictureRecorder := TSkPictureRecorder.Create; | |
LCanvas := LPictureRecorder.BeginRecording(LDrawBounds); | |
try | |
APaint := TSkPaint.Create(APaint); | |
if (AStop >= TEpsilon.Position) and (AStart >= TEpsilon.Position) then | |
APaint.StrokeCap := TSkStrokeCap.Butt; | |
APaint.Color := AInnerColor; | |
APaint.PathEffect := TSkPathEffect.MakeTrim(AStart, 1 - AStop, TSkPathEffectTrimMode.Normal); | |
LCanvas.DrawPath(APath, APaint); | |
finally | |
Result := LPictureRecorder.FinishRecording; | |
end; | |
end; | |
procedure DrawElement(APicture: ISkPicture; const AStart, AStop: Single); | |
begin | |
if APicture = nil then | |
Exit; | |
ACanvas.Save; | |
try | |
ClipTrimPath(ACanvas, APaint, APath, AStart, AStop); | |
ACanvas.DrawPicture(APicture, APaint); | |
finally | |
ACanvas.Restore; | |
end; | |
end; | |
var | |
LSegmentPaint: ISkPaint; | |
begin | |
AStart := EnsureRange(AStart, 0, 1); | |
AStop := EnsureRange(AStop, 0, 1); | |
if (AInterpolations <= 0) or ((AStart < TEpsilon.Position) and (AStop < TEpsilon.Position)) then | |
begin | |
LSegmentPaint := TSkPaint.Create(APaint); | |
LSegmentPaint.Color := AInnerColor; | |
ACanvas.DrawPath(APath, LSegmentPaint); | |
end | |
else | |
begin | |
LSegmentPaint := TSkPaint.Create(APaint); | |
LSegmentPaint.Blender := TSkBlender.MakeMode(TSkBlendMode.Src); | |
LSegmentPaint.StrokeWidth := LSegmentPaint.StrokeWidth + (1 / ACanvas.GetLocalToDeviceAs3x3.ExtractScale.Length); | |
LDrawBounds := APath.Bounds; | |
LDrawBounds.Inflate(2 * LSegmentPaint.StrokeWidth, 2 * LSegmentPaint.StrokeWidth); | |
DrawElement(GetBeginPicture(LSegmentPaint), 0, AStart); | |
DrawElement(GetEndPicture(LSegmentPaint), 1 - AStop, 1); | |
DrawElement(GetInnerPicture(LSegmentPaint), AStart, 1 - AStop); | |
end; | |
end; | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Form1: TForm1 | |
Left = 0 | |
Top = 0 | |
Caption = 'Form1' | |
ClientHeight = 480 | |
ClientWidth = 640 | |
FormFactor.Width = 320 | |
FormFactor.Height = 480 | |
FormFactor.Devices = [Desktop] | |
DesignerMasterStyle = 0 | |
object SkPaintBox1: TSkPaintBox | |
Align = Client | |
HitTest = True | |
Size.Width = 640.000000000000000000 | |
Size.Height = 480.000000000000000000 | |
Size.PlatformDefault = False | |
OnDraw = SkPaintBox1Draw | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit Unit1; | |
interface | |
uses | |
System.SysUtils, System.Types, System.UITypes, System.Classes, FMX.Types, | |
FMX.Controls, FMX.Forms, FMX.Graphics, Skia, Skia.FMX; | |
type | |
TForm1 = class(TForm) | |
SkPaintBox1: TSkPaintBox; | |
procedure SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas; | |
const ADest: TRectF; const AOpacity: Single); | |
private | |
{ Private declarations } | |
public | |
{ Public declarations } | |
end; | |
var | |
Form1: TForm1; | |
implementation | |
{$R *.fmx} | |
uses | |
StrokePathGradient; | |
procedure TForm1.SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas; | |
const ADest: TRectF; const AOpacity: Single); | |
function CreatePath: ISkPath; | |
begin | |
var LPathBuilder: ISkPathBuilder := TSkPathBuilder.Create; | |
LPathBuilder.MoveTo(PointF(36, 48)); | |
LPathBuilder.QuadTo(PointF(66, 88), PointF(120, 36)); | |
LPathBuilder.LineTo(250, 300); | |
LPathBuilder.LineTo(180, 300); | |
LPathBuilder.LineTo(250, 200); | |
Result := LPathBuilder.Detach; | |
end; | |
var | |
LPaint: ISkPaint; | |
begin | |
LPaint := TSkPaint.Create(TSkPaintStyle.Stroke); | |
LPaint.StrokeWidth := 8; | |
LPaint.AntiAlias := True; | |
LPaint.StrokeCap := TSkStrokeCap.Round; | |
//ACanvas.DrawPath(CreatePath, LPaint); | |
//DrawPathEndSmothing(ACanvas, CreatePath, LPaint, TAlphaColors.Tomato, TAlphaColors.Blueviolet, 0.2, 0.3, 30); | |
//DrawPathEndSmothing(ACanvas, CreatePath, LPaint, TAlphaColors.Black, TAlphaColors.Null, 0.15, 0.3, 30); | |
DrawPathEndSmothing(ACanvas, CreatePath, LPaint, TAlphaColors.Tomato, TAlphaColors.Blueviolet, 0, 1, 30); | |
end; | |
end. |
Author
viniciusfbb
commented
Jun 13, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment