From 288db7b666bd22baf2d5c4d7be2c1c6b36a54c3f Mon Sep 17 00:00:00 2001 From: wp Date: Mon, 4 Jan 2016 21:32:49 +0000 Subject: [PATCH] fpvectorial: Add gradients with more than two colors git-svn-id: trunk@51194 - --- components/fpvectorial/fpvectorial.pas | 102 ++++++++++++++---- components/fpvectorial/svgvectorialreader.pas | 71 +++++++++--- 2 files changed, 137 insertions(+), 36 deletions(-) diff --git a/components/fpvectorial/fpvectorial.pas b/components/fpvectorial/fpvectorial.pas index bda4b517b2..8f17de95a7 100644 --- a/components/fpvectorial/fpvectorial.pas +++ b/components/fpvectorial/fpvectorial.pas @@ -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)))); diff --git a/components/fpvectorial/svgvectorialreader.pas b/components/fpvectorial/svgvectorialreader.pas index 883b1226e3..423b732666 100644 --- a/components/fpvectorial/svgvectorialreader.pas +++ b/components/fpvectorial/svgvectorialreader.pas @@ -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; // // @@ -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;