mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-14 09:19:22 +02:00
fpvectorial: Add gradients with more than two colors
git-svn-id: trunk@51194 -
This commit is contained in:
parent
8ff7b2a5c9
commit
288db7b666
@ -123,9 +123,16 @@ type
|
|||||||
end;
|
end;
|
||||||
PvPen = ^TvPen;
|
PvPen = ^TvPen;
|
||||||
|
|
||||||
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient, vkOtherLinearGradient, bkRadialGradient);
|
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient, bkOtherLinearGradient, bkRadialGradient);
|
||||||
TvCoordinateUnit = (vcuDocumentUnit, vcuPercentage);
|
TvCoordinateUnit = (vcuDocumentUnit, vcuPercentage);
|
||||||
|
|
||||||
|
TvGradientColor = record
|
||||||
|
Color: TFPColor;
|
||||||
|
Position: Double;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TvGradientColors = array of TvGradientColor;
|
||||||
|
|
||||||
TvBrush = record
|
TvBrush = record
|
||||||
Color: TFPColor;
|
Color: TFPColor;
|
||||||
Style: TFPBrushStyle;
|
Style: TFPBrushStyle;
|
||||||
@ -133,7 +140,7 @@ type
|
|||||||
// Gradient filling support
|
// Gradient filling support
|
||||||
Gradient_cx, Gradient_cy, Gradient_r, Gradient_fx, Gradient_fy: Double;
|
Gradient_cx, Gradient_cy, Gradient_r, Gradient_fx, Gradient_fy: Double;
|
||||||
Gradient_cx_Unit, Gradient_cy_Unit, Gradient_r_Unit, Gradient_fx_Unit, Gradient_fy_Unit: TvCoordinateUnit;
|
Gradient_cx_Unit, Gradient_cy_Unit, Gradient_r_Unit, Gradient_fx_Unit, Gradient_fy_Unit: TvCoordinateUnit;
|
||||||
Gradient_colors: array of TFPColor;
|
Gradient_colors: TvGradientColors;
|
||||||
end;
|
end;
|
||||||
PvBrush = ^TvBrush;
|
PvBrush = ^TvBrush;
|
||||||
|
|
||||||
@ -3633,29 +3640,83 @@ procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient(ADest: TFPCustomCanva
|
|||||||
var
|
var
|
||||||
lPoints: TPointsArray;
|
lPoints: TPointsArray;
|
||||||
lColor, lColor1, lColor2: TFPColor;
|
lColor, lColor1, lColor2: TFPColor;
|
||||||
i, j: Integer;
|
i, j, c: Integer;
|
||||||
|
i1, i2: Integer;
|
||||||
|
p, p1, p2: Double;
|
||||||
begin
|
begin
|
||||||
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
|
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
lColor1 := Brush.Gradient_colors[1];
|
if Length(Brush.Gradient_colors) = 1 then
|
||||||
lColor2 := Brush.Gradient_colors[0];
|
|
||||||
ADest.Pen.Style := psSolid;
|
|
||||||
if Brush.Kind = bkVerticalGradient then // horizontal (!) lines have same color
|
|
||||||
begin
|
begin
|
||||||
for i := y1 to y2 do
|
lColor1 := Brush.Gradient_colors[0].Color;
|
||||||
|
lColor2 := Brush.Gradient_colors[0].Color;
|
||||||
|
c := 0;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
c := 0;
|
||||||
|
lColor1 := Brush.Gradient_colors[0].Color;
|
||||||
|
lColor2 := Brush.Gradient_colors[1].Color;
|
||||||
|
p1 := Brush.Gradient_colors[0].Position;
|
||||||
|
p2 := Brush.Gradient_colors[1].Position;
|
||||||
|
end;
|
||||||
|
|
||||||
|
case Brush.Kind of
|
||||||
|
bkVerticalGradient:
|
||||||
|
begin // horizontal (!) lines have same color
|
||||||
|
i1 := y1;
|
||||||
|
i2 := y2;
|
||||||
|
end;
|
||||||
|
bkHorizontalGradient:
|
||||||
|
begin // vertical lines have same color
|
||||||
|
i1 := x1;
|
||||||
|
i2 := x2;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
ADest.Pen.Style := psSolid;
|
||||||
|
for i := i1 to i2 do
|
||||||
|
begin
|
||||||
|
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints,
|
||||||
|
Brush.Kind = bkHorizontalGradient);
|
||||||
|
if Length(lPoints) < 2 then Continue;
|
||||||
|
p := (i-i1) / (i2-i1) * 100.0; // p = "percent"
|
||||||
|
// Use first color below first position
|
||||||
|
if p < Brush.Gradient_colors[0].Position then
|
||||||
|
lColor := Brush.Gradient_colors[0].Color
|
||||||
|
else
|
||||||
|
// Use last color above last position
|
||||||
|
if p > Brush.Gradient_colors[High(Brush.Gradient_colors)].Position then
|
||||||
|
lColor := Brush.Gradient_colors[High(Brush.Gradient_colors)].Color
|
||||||
|
else
|
||||||
|
if (c < High(Brush.Gradient_colors)) then
|
||||||
begin
|
begin
|
||||||
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, False);
|
// Use current color pair if percentage is below next position
|
||||||
if Length(lPoints) < 2 then Continue;
|
if (p < Brush.Gradient_colors[c+1].Position) then
|
||||||
lColor := MixColors(lColor1, lColor2, i-y1, y2-y1);
|
lColor := MixColors(lColor2, lColor1, p-p1, p2-p1)
|
||||||
ADest.Pen.FPColor := lColor;
|
else
|
||||||
j := 0;
|
// Next next color pair if percentage is above next position
|
||||||
while j < High(lPoints) do
|
|
||||||
begin
|
begin
|
||||||
ADest.Line(lPoints[j].X, i, lPoints[j+1].X, i);
|
inc(c);
|
||||||
inc(j, 2);
|
p1 := p2;
|
||||||
|
p2 := Brush.Gradient_colors[c+1].Position;
|
||||||
|
lColor1 := lColor2;
|
||||||
|
lColor2 := Brush.Gradient_colors[c+1].Color;
|
||||||
|
lColor := MixColors(lColor2, lColor1, p-p1, p2-p1);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
ADest.Pen.FPColor := lColor;
|
||||||
|
j := 0;
|
||||||
|
while j < High(lPoints) do
|
||||||
|
begin
|
||||||
|
case Brush.Kind of
|
||||||
|
bkVerticalGradient : ADest.Line(lPoints[j].X, i, lPoints[j+1].X, i);
|
||||||
|
bkHorizontalGradient: ADest.Line(i, lPoints[j].Y, i, lPoints[j+1].Y);
|
||||||
|
end;
|
||||||
|
inc(j, 2);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{
|
||||||
end
|
end
|
||||||
else if Brush.Kind = bkHorizontalGradient then // vertical (!) lines have same color
|
else if Brush.Kind = bkHorizontalGradient then // vertical (!) lines have same color
|
||||||
begin
|
begin
|
||||||
@ -3663,7 +3724,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, True);
|
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, True);
|
||||||
if Length(lPoints) < 2 then Continue;
|
if Length(lPoints) < 2 then Continue;
|
||||||
lColor := MixColors(lColor1, lColor2, i-x1, x2-x1);
|
lColor := MixColors(lColor2, lColor1, i-x1, x2-x1);
|
||||||
ADest.Pen.FPColor := lColor;
|
ADest.Pen.FPColor := lColor;
|
||||||
j := 0;
|
j := 0;
|
||||||
while (j < High(lPoints)) do
|
while (j < High(lPoints)) do
|
||||||
@ -3673,6 +3734,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Fills the entity's shape with a gradient.
|
{ Fills the entity's shape with a gradient.
|
||||||
@ -3711,8 +3773,8 @@ begin
|
|||||||
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
|
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
lColor1 := Brush.Gradient_colors[1];
|
lColor1 := Brush.Gradient_colors[1].Color;
|
||||||
lColor2 := Brush.Gradient_colors[0];
|
lColor2 := Brush.Gradient_colors[0].Color;
|
||||||
if Brush.Kind = bkVerticalGradient then
|
if Brush.Kind = bkVerticalGradient then
|
||||||
begin
|
begin
|
||||||
for i := y1 to y2 do
|
for i := y1 to y2 do
|
||||||
@ -4239,7 +4301,7 @@ begin
|
|||||||
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
|
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
|
||||||
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
|
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
|
||||||
begin
|
begin
|
||||||
dy := APoints[j].Y - APoints[j].Y; // can't be zero here
|
dy := APoints[j+1].Y - APoints[j].Y; // can't be zero here
|
||||||
dx := APoints[j+1].X - APoints[j].X;
|
dx := APoints[j+1].X - APoints[j].X;
|
||||||
xval := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
xval := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
|
||||||
list.Add(pointer(PtrInt(round(xval))));
|
list.Add(pointer(PtrInt(round(xval))));
|
||||||
|
@ -100,6 +100,7 @@ type
|
|||||||
// debug symbols
|
// debug symbols
|
||||||
FPathNumber: Integer;
|
FPathNumber: Integer;
|
||||||
function ReadSVGColor(AValue: string): TFPColor;
|
function ReadSVGColor(AValue: string): TFPColor;
|
||||||
|
function ReadSVGGradientColorStyle(AValue: STring): TFPColor;
|
||||||
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
|
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
|
||||||
function ReadSVGStyleToStyleLists(AValue: string; AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
|
function ReadSVGStyleToStyleLists(AValue: string; AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
|
||||||
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
|
||||||
@ -720,6 +721,37 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// style="stop-color:rgb(255,255,10);stop-opacity:1.0"
|
||||||
|
function TvSVGVectorialReader.ReadSVGGradientColorStyle(AValue: String): TFPColor;
|
||||||
|
var
|
||||||
|
lStr, lStyleKeyStr, lStyleValueStr: String;
|
||||||
|
lStrings: TStringList;
|
||||||
|
i: Integer;
|
||||||
|
p: Integer;
|
||||||
|
begin
|
||||||
|
Result := colBlack;
|
||||||
|
if AValue = '' then Exit;
|
||||||
|
lStrings := TStringList.Create;
|
||||||
|
try
|
||||||
|
lStrings.Delimiter := ';';
|
||||||
|
lStrings.StrictDelimiter := True;
|
||||||
|
lStrings.DelimitedText := LowerCase(AValue);
|
||||||
|
for i := 0 to lStrings.Count-1 do
|
||||||
|
begin
|
||||||
|
lStr := lStrings.Strings[i];
|
||||||
|
p := Pos(':', lStr);
|
||||||
|
lStyleKeyStr := Trim(Copy(lStr, 1, p-1));
|
||||||
|
lStyleValueStr := Trim(Copy(lStr, p+1, MaxInt));
|
||||||
|
if lStyleKeyStr = 'stop-color' then
|
||||||
|
Result := ReadSVGColor(lStyleValueStr)
|
||||||
|
else if lStyleKeyStr = 'stop-opacity' then
|
||||||
|
Result.Alpha := Round(StrToFloat(lStyleValueStr, FPointSeparator)*$FFFF);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
lStrings.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
// style="fill:none;stroke:black;stroke-width:3"
|
// style="fill:none;stroke:black;stroke-width:3"
|
||||||
function TvSVGVectorialReader.ReadSVGStyle(AValue: string;
|
function TvSVGVectorialReader.ReadSVGStyle(AValue: string;
|
||||||
ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil;
|
ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil;
|
||||||
@ -901,7 +933,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
Len := Length(ADestEntity.Brush.Gradient_colors);
|
Len := Length(ADestEntity.Brush.Gradient_colors);
|
||||||
SetLength(ADestEntity.Brush.Gradient_colors, Len+1);
|
SetLength(ADestEntity.Brush.Gradient_colors, Len+1);
|
||||||
ADestEntity.Brush.Gradient_colors[Len] := ReadSVGColor(AValue);
|
ADestEntity.Brush.Gradient_colors[Len].Color := ReadSVGColor(AValue);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1198,11 +1230,11 @@ var
|
|||||||
lPreviousLayer: TvEntityWithSubEntities;
|
lPreviousLayer: TvEntityWithSubEntities;
|
||||||
lAttrName, lAttrValue, lNodeName: DOMString;
|
lAttrName, lAttrValue, lNodeName: DOMString;
|
||||||
lLayerName: String;
|
lLayerName: String;
|
||||||
i: Integer;
|
i, len: Integer;
|
||||||
lCurNode, lCurSubNode: TDOMNode;
|
lCurNode, lCurSubNode: TDOMNode;
|
||||||
lBrushEntity: TvEntityWithPenAndBrush;
|
lBrushEntity: TvEntityWithPenAndBrush;
|
||||||
lCurEntity: TvEntity;
|
lCurEntity: TvEntity;
|
||||||
lOffset: Double;
|
lGradientColor: TvGradientColor;
|
||||||
x1, x2, y1, y2: string;
|
x1, x2, y1, y2: string;
|
||||||
begin
|
begin
|
||||||
lCurNode := ANode.FirstChild;
|
lCurNode := ANode.FirstChild;
|
||||||
@ -1290,8 +1322,12 @@ begin
|
|||||||
else if lAttrName = 'y2' then
|
else if lAttrName = 'y2' then
|
||||||
y2 := lAttrValue;
|
y2 := lAttrValue;
|
||||||
end;
|
end;
|
||||||
if x2 = x1 then lBrushEntity.Brush.Kind := bkVerticalGradient
|
if x2 = x1 then
|
||||||
else lBrushEntity.Brush.Kind := bkHorizontalGradient;
|
lBrushEntity.Brush.Kind := bkVerticalGradient
|
||||||
|
else if y2=y1 then
|
||||||
|
lBrushEntity.Brush.Kind := bkHorizontalGradient
|
||||||
|
else
|
||||||
|
lBrushEntity.Brush.Kind := bkOtherLinearGradient;
|
||||||
|
|
||||||
// <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
|
// <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
|
||||||
// <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
|
// <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
|
||||||
@ -1299,19 +1335,22 @@ begin
|
|||||||
while Assigned(lCurSubNode) do
|
while Assigned(lCurSubNode) do
|
||||||
begin
|
begin
|
||||||
lNodeName := LowerCase(lCurSubNode.NodeName);
|
lNodeName := LowerCase(lCurSubNode.NodeName);
|
||||||
|
if lNodeName = 'stop' then begin
|
||||||
for i := 0 to lCurSubNode.Attributes.Length - 1 do
|
for i := 0 to lCurSubNode.Attributes.Length - 1 do
|
||||||
begin
|
|
||||||
lAttrName := lCurSubNode.Attributes.Item[i].NodeName;
|
|
||||||
lAttrValue := lCurSubNode.Attributes.Item[i].NodeValue;
|
|
||||||
if lAttrName = 'offset' then
|
|
||||||
begin
|
begin
|
||||||
lOffset := StringWithUnitToFloat(lAttrValue);
|
lAttrName := lCurSubNode.Attributes.Item[i].NodeName;
|
||||||
end
|
lAttrValue := lCurSubNode.Attributes.Item[i].NodeValue;
|
||||||
else if lAttrName = 'style' then
|
if lAttrName = 'offset' then
|
||||||
ReadSVGStyle(lAttrValue, lBrushEntity);
|
lGradientColor.Position := StringWithUnitToFloat(lAttrValue)
|
||||||
|
else if lAttrName = 'style' then
|
||||||
|
lGradientColor.Color := ReadSVGGradientColorStyle(lAttrValue)
|
||||||
|
else if lAttrName = 'stop-color' then
|
||||||
|
lGradientColor.Color := ReadSVGColor(lAttrValue);
|
||||||
|
end;
|
||||||
|
len := Length(lBrushEntity.Brush.Gradient_colors);
|
||||||
|
SetLength(lBrushEntity.Brush.Gradient_colors, Len+1);
|
||||||
|
lBrushEntity.Brush.Gradient_colors[len] := lGradientColor;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
lCurSubNode := lCurSubNode.NextSibling;
|
lCurSubNode := lCurSubNode.NextSibling;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user