fpvectorial: Add gradients with more than two colors

git-svn-id: trunk@51194 -
This commit is contained in:
wp 2016-01-04 21:32:49 +00:00
parent 8ff7b2a5c9
commit 288db7b666
2 changed files with 137 additions and 36 deletions

View File

@ -123,9 +123,16 @@ type
end;
PvPen = ^TvPen;
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient, vkOtherLinearGradient, bkRadialGradient);
TvBrushKind = (bkSimpleBrush, bkHorizontalGradient, bkVerticalGradient, bkOtherLinearGradient, bkRadialGradient);
TvCoordinateUnit = (vcuDocumentUnit, vcuPercentage);
TvGradientColor = record
Color: TFPColor;
Position: Double;
end;
TvGradientColors = array of TvGradientColor;
TvBrush = record
Color: TFPColor;
Style: TFPBrushStyle;
@ -133,7 +140,7 @@ type
// Gradient filling support
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_colors: array of TFPColor;
Gradient_colors: TvGradientColors;
end;
PvBrush = ^TvBrush;
@ -3633,29 +3640,83 @@ procedure TvEntityWithPenAndBrush.DrawPolygonBrushGradient(ADest: TFPCustomCanva
var
lPoints: TPointsArray;
lColor, lColor1, lColor2: TFPColor;
i, j: Integer;
i, j, c: Integer;
i1, i2: Integer;
p, p1, p2: Double;
begin
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
Exit;
lColor1 := Brush.Gradient_colors[1];
lColor2 := Brush.Gradient_colors[0];
ADest.Pen.Style := psSolid;
if Brush.Kind = bkVerticalGradient then // horizontal (!) lines have same color
if Length(Brush.Gradient_colors) = 1 then
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
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, False);
if Length(lPoints) < 2 then Continue;
lColor := MixColors(lColor1, lColor2, i-y1, y2-y1);
ADest.Pen.FPColor := lColor;
j := 0;
while j < High(lPoints) do
// Use current color pair if percentage is below next position
if (p < Brush.Gradient_colors[c+1].Position) then
lColor := MixColors(lColor2, lColor1, p-p1, p2-p1)
else
// Next next color pair if percentage is above next position
begin
ADest.Line(lPoints[j].X, i, lPoints[j+1].X, i);
inc(j, 2);
inc(c);
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;
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
else if Brush.Kind = bkHorizontalGradient then // vertical (!) lines have same color
begin
@ -3663,7 +3724,7 @@ begin
begin
lPoints := GetLinePolygonIntersectionPoints(i, APolyPoints, True);
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;
j := 0;
while (j < High(lPoints)) do
@ -3673,6 +3734,7 @@ begin
end;
end;
end;
}
end;
{ Fills the entity's shape with a gradient.
@ -3711,8 +3773,8 @@ begin
if not (Brush.Kind in [bkVerticalGradient, bkHorizontalGradient]) then
Exit;
lColor1 := Brush.Gradient_colors[1];
lColor2 := Brush.Gradient_colors[0];
lColor1 := Brush.Gradient_colors[1].Color;
lColor2 := Brush.Gradient_colors[0].Color;
if Brush.Kind = bkVerticalGradient then
begin
for i := y1 to y2 do
@ -4239,7 +4301,7 @@ begin
if ((APoints[j].Y <= ACoord) and (ACoord < APoints[j+1].Y)) or
((APoints[j+1].Y <= ACoord) and (ACoord < APoints[j].Y)) then
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;
xval := APoints[j].X + (ACoord - APoints[j].Y) * dx / dy;
list.Add(pointer(PtrInt(round(xval))));

View File

@ -100,6 +100,7 @@ type
// debug symbols
FPathNumber: Integer;
function ReadSVGColor(AValue: string): TFPColor;
function ReadSVGGradientColorStyle(AValue: STring): TFPColor;
function ReadSVGStyle(AValue: string; ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil; AUseFillAsPen: Boolean = False): TvSetPenBrushAndFontElements;
function ReadSVGStyleToStyleLists(AValue: string; AStyleKeys, AStyleValues: TStringList): TvSetPenBrushAndFontElements;
function ReadSVGPenStyleWithKeyAndValue(AKey, AValue: string; ADestEntity: TvEntityWithPen): TvSetPenBrushAndFontElements;
@ -720,6 +721,37 @@ begin
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"
function TvSVGVectorialReader.ReadSVGStyle(AValue: string;
ADestEntity: TvEntityWithPen; ADestStyle: TvStyle = nil;
@ -901,7 +933,7 @@ begin
begin
Len := Length(ADestEntity.Brush.Gradient_colors);
SetLength(ADestEntity.Brush.Gradient_colors, Len+1);
ADestEntity.Brush.Gradient_colors[Len] := ReadSVGColor(AValue);
ADestEntity.Brush.Gradient_colors[Len].Color := ReadSVGColor(AValue);
end;
end;
@ -1198,11 +1230,11 @@ var
lPreviousLayer: TvEntityWithSubEntities;
lAttrName, lAttrValue, lNodeName: DOMString;
lLayerName: String;
i: Integer;
i, len: Integer;
lCurNode, lCurSubNode: TDOMNode;
lBrushEntity: TvEntityWithPenAndBrush;
lCurEntity: TvEntity;
lOffset: Double;
lGradientColor: TvGradientColor;
x1, x2, y1, y2: string;
begin
lCurNode := ANode.FirstChild;
@ -1290,8 +1322,12 @@ begin
else if lAttrName = 'y2' then
y2 := lAttrValue;
end;
if x2 = x1 then lBrushEntity.Brush.Kind := bkVerticalGradient
else lBrushEntity.Brush.Kind := bkHorizontalGradient;
if x2 = x1 then
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="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
@ -1299,19 +1335,22 @@ begin
while Assigned(lCurSubNode) do
begin
lNodeName := LowerCase(lCurSubNode.NodeName);
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
if lNodeName = 'stop' then begin
for i := 0 to lCurSubNode.Attributes.Length - 1 do
begin
lOffset := StringWithUnitToFloat(lAttrValue);
end
else if lAttrName = 'style' then
ReadSVGStyle(lAttrValue, lBrushEntity);
lAttrName := lCurSubNode.Attributes.Item[i].NodeName;
lAttrValue := lCurSubNode.Attributes.Item[i].NodeValue;
if lAttrName = 'offset' then
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;
lCurSubNode := lCurSubNode.NextSibling;
end;