354 lines
9.2 KiB
ObjectPascal

// SPDX-License-Identifier: LGPL-3.0-linking-exception
{******************************* CONTRIBUTOR(S) ******************************
- Edivando S. Santos Brasil | mailedivando@gmail.com
(Compatibility with delphi VCL 11/2018)
***************************** END CONTRIBUTOR(S) *****************************}
unit BCSVGViewer;
{$I bgracontrols.inc}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, BGRAGraphicControl,
{$IFDEF FPC}LResources, LCLType, {$ENDIF}
{$IFNDEF FPC}Types, BGRAGraphics, GraphType, FPImage, {$ENDIF}
BGRABitmap, BGRABitmapTypes, BGRASVG, BGRAUnits, BCTypes;
type
{ TBCSVGViewer }
TBCSVGViewer = class(TCustomBGRAGraphicControl)
private
FDrawCheckers: boolean;
FHorizAlign: TAlignment;
FProportional: boolean;
FStretchMode: TBCStretchMode;
FDestDPI: single;
FUseSVGAlignment: boolean;
FVertAlign: TTextLayout;
Fx: single;
Fy: single;
function GetSVGString: string;
procedure SetDrawCheckers(AValue: boolean);
procedure SetFDestDPI(AValue: single);
procedure SetSVGString(AValue: string);
procedure SetFx(AValue: single);
procedure SetFy(AValue: single);
procedure SetHorizAlign(AValue: TAlignment);
procedure SetProportional(AValue: boolean);
procedure SetStretchMode(AValue: TBCStretchMode);
procedure SetUseSVGAlignment(AValue: boolean);
procedure SetVertAlign(AValue: TTextLayout);
protected
FSVG: TBGRASVG;
procedure RedrawBitmapContent; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadFromFile(AFileName: string);
procedure LoadFromResource(Resource: string);
function GetSVGRectF: TRectF;
function GetSVGContainerRectF: TRectF;
published
{ Published declarations }
property Align;
property Anchors;
property OnRedraw;
property Bitmap;
property BorderSpacing;
property Constraints;
property SVG: TBGRASVG read FSVG;
property SVGString: string read GetSVGString write SetSVGString;
property DestDPI: single read FDestDPI write SetFDestDPI {$IFDEF FPC} default
96{$ENDIF};
property x: single read Fx write SetFx {$IFDEF FPC} default 0{$ENDIF};
property y: single read Fy write SetFy {$IFDEF FPC} default 0{$ENDIF};
property HorizAlign: TAlignment read FHorizAlign write SetHorizAlign default
taCenter;
property VertAlign: TTextLayout read FVertAlign write SetVertAlign default tlCenter;
property StretchMode: TBCStretchMode
read FStretchMode write SetStretchMode default smStretch;
property Proportional: boolean read FProportional write SetProportional default True;
property DrawCheckers: boolean
read FDrawCheckers write SetDrawCheckers default False;
property UseSVGAlignment: boolean read FUseSVGAlignment write SetUseSVGAlignment default False;
property Color;
property ColorOpacity;
property OnClick;
property OnDblClick;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
{$IFDEF FPC}
property OnPaint;
{$ENDIF}
property OnResize;
property Caption;
end;
{$IFDEF FPC}procedure Register;{$ENDIF}
implementation
uses BGRAVectorize, math;
{$IFDEF FPC}
procedure Register;
begin
RegisterComponents('BGRA Controls', [TBCSVGViewer]);
end;
{$ENDIF}
{ TBCSVGViewer }
procedure TBCSVGViewer.SetFDestDPI(AValue: single);
begin
if FDestDPI = AValue then
Exit;
FDestDPI := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetSVGString(AValue: string);
begin
FSVG.ASUTF8String := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetDrawCheckers(AValue: boolean);
begin
if FDrawCheckers = AValue then
Exit;
FDrawCheckers := AValue;
DiscardBitmap;
end;
function TBCSVGViewer.GetSVGString: string;
begin
Result := FSVG.AsUTF8String;
end;
procedure TBCSVGViewer.SetFx(AValue: single);
begin
if Fx = AValue then
Exit;
Fx := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetFy(AValue: single);
begin
if Fy = AValue then
Exit;
Fy := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetHorizAlign(AValue: TAlignment);
begin
if FHorizAlign = AValue then
Exit;
FHorizAlign := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetProportional(AValue: boolean);
begin
if FProportional = AValue then
Exit;
FProportional := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetStretchMode(AValue: TBCStretchMode);
begin
if FStretchMode = AValue then
Exit;
FStretchMode := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetUseSVGAlignment(AValue: boolean);
begin
if FUseSVGAlignment=AValue then Exit;
FUseSVGAlignment:=AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.SetVertAlign(AValue: TTextLayout);
begin
if FVertAlign = AValue then
Exit;
FVertAlign := AValue;
DiscardBitmap;
end;
procedure TBCSVGViewer.RedrawBitmapContent;
var
r: TRectF;
checkersSize: integer;
begin
if (FBGRA <> nil) and (FBGRA.NbPixels <> 0) then
begin
r := GetSVGRectF;
FBGRA.Fill(ColorToBGRA(ColorToRGB(Color), ColorOpacity));
if FDrawCheckers then
begin
checkersSize := round(8 * DestDPI / 96 * BitmapScale);
with GetSVGContainerRectF do
FBGRA.DrawCheckers(rect(floor(Left), floor(Top),
ceil(right), ceil(Bottom)), CSSWhite, CSSSilver,
checkersSize, checkersSize);
end;
FBGRA.Canvas2D.FontRenderer := TBGRAVectorizedFontRenderer.Create;
FSVG.StretchDraw(FBGRA.Canvas2D, r, UseSVGAlignment);
if Assigned(OnRedraw) then
OnRedraw(self, FBGRA);
end;
end;
constructor TBCSVGViewer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSVG := TBGRASVG.Create(100, 100, TCSSUnit.cuPercent);
FDestDPI := 96;
Fx := 0;
Fy := 0;
FStretchMode := smStretch;
FHorizAlign := taCenter;
FVertAlign := tlCenter;
FProportional := True;
FBitmapAutoScale := False;
FUseSVGAlignment:= false;
end;
destructor TBCSVGViewer.Destroy;
begin
FSVG.Free;
inherited Destroy;
end;
procedure TBCSVGViewer.LoadFromFile(AFileName: string);
begin
FSVG.LoadFromFile(AFileName);
DiscardBitmap;
end;
procedure TBCSVGViewer.LoadFromResource(Resource: string);
begin
FSVG.LoadFromResource(Resource);
DiscardBitmap;
end;
function TBCSVGViewer.GetSVGRectF: TRectF;
var
vbSize: TPointF;
w, h, dpi: single;
containerRect: TRectF;
function NoStretch(AX, AY: single): TRectF;
begin
case HorizAlign of
taCenter: Result.Left := (w - vbSize.x) / 2;
taRightJustify: Result.Left := w - AX - vbSize.x;
else
{taLeftJustify} Result.Left := AX;
end;
case VertAlign of
tlCenter: Result.Top := (h - vbSize.y) / 2;
tlBottom: Result.Top := h - AY - vbSize.y;
else
{tlTop} Result.Top := AY;
end;
Result.Right := Result.Left + vbSize.x;
Result.Bottom := Result.Top + vbSize.y;
end;
begin
if FSVG = nil then exit(EmptyRectF);
containerRect := GetSVGContainerRectF;
w := containerRect.Width;
h := containerRect.Height;
dpi := DestDPI * BitmapScale;
FSVG.Units.ContainerWidth := FloatWithCSSUnit(w * FSVG.Units.DpiX / dpi, cuPixel);
FSVG.Units.ContainerHeight := FloatWithCSSUnit(h * FSVG.Units.DpiY / dpi, cuPixel);
if UseSVGAlignment then
exit(FSVG.GetStretchRectF(containerRect.Left, containerRect.Top, containerRect.Width, containerRect.Height));
vbSize := FSVG.ViewSizeInUnit[cuPixel];
vbSize.x := vbSize.x * (dpi / FSVG.Units.DpiX);
vbSize.y := vbSize.y * (dpi / FSVG.Units.DpiY);
if ((StretchMode = smShrink) and ((vbSize.x > w + 0.1) or (vbSize.y > h + 0.1))) or
(StretchMode in[smStretch, smCover]) then
begin
if Proportional then
Result := FSVG.GetStretchRectF(HorizAlign, VertAlign, 0, 0, w, h, StretchMode = smCover)
else
if StretchMode = smShrink then
begin
NoStretch(0, 0);
if vbSize.x > w then
begin
Result.Left := 0;
Result.Right := w;
end;
if vbSize.y > h then
begin
Result.Top := 0;
Result.Bottom := h;
end;
end
else
Result := RectF(0, 0, w, h);
end
else
result := NoStretch(x, y);
result.Offset(containerRect.Left, containerRect.Top);
end;
function TBCSVGViewer.GetSVGContainerRectF: TRectF;
var
w, h: Integer;
dpi, ratioX, ratioY, ratio: single;
begin
w := BitmapWidth;
h := BitmapHeight;
dpi := DestDPI * BitmapScale;
Result := RectF(0, 0, w, h);
if (FSVG = nil) or not UseSVGAlignment then exit;
FSVG.Units.ContainerWidth := FloatWithCSSUnit(w * FSVG.Units.DpiX / dpi, cuPixel);
FSVG.Units.ContainerHeight := FloatWithCSSUnit(h * FSVG.Units.DpiY / dpi, cuPixel);
if (FSVG = nil) or (FSVG.WidthAsPixel = 0) or
(FSVG.HeightAsPixel = 0) or (BitmapWidth = 0)
or (BitmapHeight = 0) then exit(EmptyRectF);
ratioX := BitmapWidth / FSVG.WidthAsPixel;
ratioY := BitmapHeight / FSVG.HeightAsPixel;
case StretchMode of
smStretch: ratio := min(ratioX, ratioY);
smShrink: ratio := min(1, min(ratioX, ratioY));
smCover: ratio := max(ratioX, ratioY);
else
ratio := 1;
end;
result := RectWithSizeF(0, 0, FSVG.WidthAsPixel * ratio,
FSVG.HeightAsPixel * ratio);
result.Offset((BitmapWidth - result.Width) / 2,
(BitmapHeight - result.Height) / 2);
end;
end.