diff --git a/components/fpvectorial/fpvectorial.pas b/components/fpvectorial/fpvectorial.pas index 49a211bfa4..f972a3992d 100644 --- a/components/fpvectorial/fpvectorial.pas +++ b/components/fpvectorial/fpvectorial.pas @@ -37,7 +37,7 @@ uses // lazutils laz2_dom, // LCL - lazutf8 + lazutf8, lazregions {$ifdef USE_LCL_CANVAS} , Graphics, LCLIntf, LCLType, intfgraphics, graphtype {$endif} @@ -547,6 +547,8 @@ type AMulX: Double = 1.0; AMulY: Double = 1.0); procedure DrawPolygonBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; const APoints: TPointsArray; ARect: TRect; AGradientStart, AGradientEnd: T2DPoint); + procedure DrawPolygonBrushRadialGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; + const APoints: TPointsArray; ARect: TRect); public {@@ The global Brush for the entire entity. In the case of paths, individual elements might be able to override this setting. } @@ -554,8 +556,8 @@ type WindingRule: TvClipMode; constructor Create(APage: TvPage); override; procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas); overload; - procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas; ABrush: TvBrush); overload; - procedure AssignBrush(ABrush: TvBrush); + procedure ApplyBrushToCanvas(ADest: TFPCustomCanvas; ABrush: PvBrush); overload; + procedure AssignBrush(ABrush: PvBrush); procedure DrawBrush(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); procedure DrawBrushGradient(ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; @@ -3717,20 +3719,19 @@ end; procedure TvEntityWithPenAndBrush.ApplyBrushToCanvas(ADest: TFPCustomCanvas); begin - ApplyBrushToCanvas(ADest, Brush); + ApplyBrushToCanvas(ADest, @Brush); end; procedure TvEntityWithPenAndBrush.ApplyBrushToCanvas(ADest: TFPCustomCanvas; - ABrush: TvBrush); + ABrush: PvBrush); begin - ADest.Brush.FPColor := ABrush.Color; - ADest.Brush.Style := ABrush.Style; + ADest.Brush.FPColor := ABrush^.Color; + ADest.Brush.Style := ABrush^.Style; end; -procedure TvEntityWithPenAndBrush.AssignBrush(ABrush: TvBrush); +procedure TvEntityWithPenAndBrush.AssignBrush(ABrush: PvBrush); begin - Brush.Style := ABrush.Style; - Brush.Color := ABrush.Color; + Brush := ABrush^; end; { Calculates the canvas coordinates of the gradient vector (i.e. x,y of start @@ -3780,9 +3781,10 @@ end; { Fills the entity with a gradient. Assumes that the boundary is already in canvas units and is specified by polygon APoints. } -procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient(ADest: TFPCustomCanvas; - var ARenderInfo: TvRenderInfo; const APoints: TPointsArray; ARect: TRect; - AGradientStart, AGradientEnd: T2dPoint); +procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient( + ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; + const APoints: TPointsArray; ARect: TRect; AGradientStart, + AGradientEnd: T2DPoint); var lPoints, pts: T2DPointsArray; i, j: Integer; @@ -3798,6 +3800,11 @@ var gstart: Double; // Gradient start point (1-dim) dir: Integer; begin + if Brush.Kind = bkRadialGradient then + begin + DrawPolygonBrushRadialGradient(ADest, ARenderInfo, APoints, ARect); + Exit; + end; if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient, bkOtherLinearGradient]) then Exit; @@ -3938,9 +3945,89 @@ begin end; end; +procedure TvEntityWithPenAndBrush.DrawPolygonBrushRadialGradient( + ADest: TFPCustomCanvas; var ARenderInfo: TvRenderInfo; + const APoints: TPointsArray; ARect: TRect); +var + i, j: Integer; + lx, ly: Integer; + lDist: Double; + lGradient_cx_px, lGradient_cy_px, lGradient_r_px, lGradient_fx_px, lGradient_fy_px: Double; + lWidth, lHeight, lBiggestHalfSide: Integer; + lBiggestSizeIsY: Boolean; + lColor: TFPColor; + + function Gradient_value_to_px(AValue: Double; AUnit: TvCoordinateUnit; AIsY: Boolean): Integer; + var + lSideLen: Integer; + begin + Result := 0; + if AIsY then lSideLen := (ARect.Bottom-ARect.Top) + else lSideLen := (ARect.Right-ARect.Left); + case AUnit of + //vcuDocumentUnit: Result := ; + vcuPercentage: Result := Round(lSideLen * AValue); + end; + end; + + function Distance_To_RadialGradient_Color(ADist: Double): TFPColor; + var + k: Integer; + begin + Result := colTransparent; + for k := 0 to Length(Brush.Gradient_colors)-1 do + begin + if k = 0 then + begin + Result := Brush.Gradient_colors[k].Color; + Continue; + end; + + if ADist < Brush.Gradient_colors[k].Position then + begin + Result := MixColors( + Brush.Gradient_colors[k-1].Color, Brush.Gradient_colors[k].Color, + ADist - Brush.Gradient_colors[k-1].Position, + Brush.Gradient_colors[k].Position - Brush.Gradient_colors[k-1].Position); + Exit; + end; + end; + end; + +begin + lWidth := (ARect.Right-ARect.Left); + lHeight := (ARect.Bottom-ARect.Top); + lBiggestSizeIsY := lHeight > lWidth; + if lBiggestSizeIsY then lBiggestHalfSide := Round(lHeight / 2) + else lBiggestHalfSide := Round(lWidth / 2); + + // Calculate Gradient_X_px + lGradient_cx_px := Gradient_value_to_px(Brush.Gradient_cx, Brush.Gradient_cx_Unit, True); + lGradient_cy_px := Gradient_value_to_px(Brush.Gradient_cy, Brush.Gradient_cy_Unit, False); + lGradient_r_px := Gradient_value_to_px(Brush.Gradient_r, Brush.Gradient_r_Unit, lBiggestSizeIsY); + lGradient_fx_px := Gradient_value_to_px(Brush.Gradient_fx, Brush.Gradient_fx_Unit, True); + lGradient_fy_px := Gradient_value_to_px(Brush.Gradient_fy, Brush.Gradient_fy_Unit, False); + + // pixel-by-pixel version + for i := 0 to lWidth-1 do + begin + for J := 0 to lHeight-1 do + begin + lDist := sqrt(sqr(i-lGradient_cx_px)+sqr(j-lGradient_cy_px)); + lDist := lDist / lBiggestHalfSide; + lDist := Min(Max(0, lDist), 1); + lColor := Distance_To_RadialGradient_Color(lDist); + lx := ARect.Left + i; + ly := ARect.Top + j; + if not IsPointInPolygon(lx, ly, APoints) then Exit; + ADest.Colors[lx, ly] := AlphaBlendColor(ADest.Colors[lx, ly], lColor); + end; + end; +end; + procedure TvEntityWithPenAndBrush.DrawBrushGradient(ADest: TFPCustomCanvas; - var ARenderInfo: TvRenderInfo; x1, y1, x2, y2: Integer; - ADestX, ADestY: Integer; AMulX, AMulY: Double); + var ARenderInfo: TvRenderInfo; x1, y1, x2, y2: Integer; ADestX: Integer; + ADestY: Integer; AMulX: Double; AMulY: Double); var tmpPath: TPath; polypoints: TPointsArray; @@ -4056,7 +4143,8 @@ begin end; *) procedure TvEntityWithPenAndBrush.DrawBrush(ADest: TFPCustomCanvas; - var ARenderInfo: TvRenderInfo; ADestX, ADestY: Integer; AMulX, AMulY: Double); + var ARenderInfo: TvRenderInfo; ADestX: Integer; ADestY: Integer; + AMulX: Double; AMulY: Double); var tmpPath: TPath; polypoints: TPointsArray; @@ -4085,12 +4173,13 @@ function TvEntityWithPenAndBrush.GenerateDebugTree( var lStr: string; begin - lStr := Format('[%s] Name=%s X=%f Y=%f Pen.Color=%s Pen.Style=%s Brush.Color=%s Brush.Style=%s %s', + lStr := Format('[%s] Name=%s X=%f Y=%f Pen=[Color=%s Style=%s] Brush=[Color=%s Style=%s Kind=%s] %s', [Self.ClassName, Self.Name, X, Y, GenerateDebugStrForFPColor(Pen.Color), GetEnumName(TypeInfo(TFPPenStyle), integer(Pen.Style)), GenerateDebugStrForFPColor(Brush.Color), GetEnumName(TypeInfo(TFPBrushStyle), integer(Brush.Style)), + GetEnumName(TypeInfo(TvBrushKind), integer(Brush.Kind)), FExtraDebugStr]); Result := ADestRoutine(lStr, APageItem); end; @@ -4215,7 +4304,7 @@ begin if (Style <> nil) then begin ApplyPenToCanvas(ADest, ARenderInfo, Style.Pen); - ApplyBrushToCanvas(ADest, Style.Brush); + ApplyBrushToCanvas(ADest, @Style.Brush); ApplyFontToCanvas(ADest, ARenderInfo, Style.Font, AMulX); end; end; diff --git a/components/fpvectorial/fpvutils.pas b/components/fpvectorial/fpvutils.pas index f062c6c67c..57b2b00366 100644 --- a/components/fpvectorial/fpvutils.pas +++ b/components/fpvectorial/fpvutils.pas @@ -43,6 +43,7 @@ function FPColorToRGBHexString(AColor: TFPColor): string; function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline; function MixColors(AColor1, AColor2: TFPColor; APos, AMax: Double): TFPColor; function GradientColor(AColors: TvGradientColors; AValue: Double): TFPColor; +function AlphaBlendColor(AColorBase, AColor: TFPColor): TFPColor; // Coordinate Conversion routines function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline; function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer; @@ -165,6 +166,18 @@ begin end; end; +function AlphaBlendColor(AColorBase, AColor: TFPColor): TFPColor; +var + f1, f2: Double; +begin + f1 := 1 - f2; + f2 := AColor.Alpha / alphaOpaque; + Result.Alpha := Round(AColorBase.Alpha * f1 + AColor.Alpha * f2); + Result.Red := Round(AColorBase.Red * f1 + AColor.Red * f2); + Result.Green := Round(AColorBase.Green * f1 + AColor.Green * f2); + Result.Blue := Round(AColorBase.Blue * f1 + AColor.Blue * f2); +end; + {@@ Converts the coordinate system from a TCanvas to FPVectorial The basic difference is that the Y axis is positioned differently and points upwards in FPVectorial and downwards in TCanvas. diff --git a/components/fpvectorial/odgvectorialreader.pas b/components/fpvectorial/odgvectorialreader.pas index 8507d0c50b..dabcddaede 100644 --- a/components/fpvectorial/odgvectorialreader.pas +++ b/components/fpvectorial/odgvectorialreader.pas @@ -682,7 +682,7 @@ begin begin ADest.AssignPen(lCurStyle.Pen); if ADest is TvEntityWithPenAndBrush then - TvEntityWithPenAndBrush(ADest).AssignBrush(lCurStyle.Brush); + TvEntityWithPenAndBrush(ADest).AssignBrush(@lCurStyle.Brush); Exit; end;