Skip to content

Commit ad7544f

Browse files
ver. 3.2.3
- Built with Delphi 12.2 - Update Image32 Engine to fix rendering of some SVG Icons
1 parent 669fa8f commit ad7544f

22 files changed

+1389
-175
lines changed

Ext/SVGIconImageList/Image32/source/Img32.Draw.pas

Lines changed: 225 additions & 76 deletions
Large diffs are not rendered by default.

Ext/SVGIconImageList/Image32/source/Img32.Extra.pas

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ procedure EraseOutsidePath(img: TImage32; const path: TPathD;
108108
fillRule: TFillRule; const outsideBounds: TRect);
109109
procedure EraseOutsidePaths(img: TImage32; const paths: TPathsD;
110110
fillRule: TFillRule; const outsideBounds: TRect;
111-
rendererCache: TCustomColorRendererCache = nil); overload;
111+
rendererCache: TCustomRendererCache = nil); overload;
112112

113113
procedure Draw3D(img: TImage32; const polygon: TPathD;
114114
fillRule: TFillRule; height, blurRadius: double;
@@ -938,43 +938,86 @@ procedure EraseInsidePaths(img: TImage32; const paths: TPathsD; fillRule: TFillR
938938
end;
939939
//------------------------------------------------------------------------------
940940

941+
procedure EraseOutsideRect(img: TImage32; const r, outsideBounds: TRect);
942+
begin
943+
// Fill the parts, that are in outsideBounds but not in r with zeros
944+
945+
// whole top block
946+
if r.Top > outsideBounds.Top then
947+
img.FillRect(Rect(outsideBounds.Left, outsideBounds.Top, outsideBounds.Right, r.Top - 1), 0);
948+
// whole bottom block
949+
if r.Bottom < outsideBounds.Bottom then
950+
img.FillRect(Rect(outsideBounds.Left, r.Bottom + 1, outsideBounds.Right, outsideBounds.Bottom), 0);
951+
952+
// remaining left block
953+
if r.Left > outsideBounds.Left then
954+
img.FillRect(Rect(outsideBounds.Left, r.Top, r.Left - 1, r.Bottom), 0);
955+
// remaining right block
956+
if r.Right < outsideBounds.Right then
957+
img.FillRect(Rect(r.Right + 1, r.Top, outsideBounds.Right, r.Bottom), 0);
958+
end;
959+
//------------------------------------------------------------------------------
960+
941961
procedure EraseOutsidePath(img: TImage32; const path: TPathD;
942962
fillRule: TFillRule; const outsideBounds: TRect);
943963
var
944-
mask: TImage32;
945-
p: TPathD;
946-
w,h: integer;
964+
w, h: integer;
965+
renderer: TMaskRenderer;
966+
r: TRect;
967+
polygons: TPathsD;
947968
begin
948969
if not assigned(path) then Exit;
949-
RectWidthHeight(outsideBounds, w,h);
950-
mask := TImage32.Create(w, h);
970+
RectWidthHeight(outsideBounds, w, h);
971+
if (w <= 0) or (h <= 0) then Exit;
972+
973+
// We can skip the costly polygon rasterization if the path is
974+
// a rectangle
975+
if (fillRule in [frEvenOdd, frNonZero]) and IsSimpleRectanglePath(path, r) then
976+
begin
977+
EraseOutsideRect(img, r, outsideBounds);
978+
Exit;
979+
end;
980+
981+
renderer := TMaskRenderer.Create;
951982
try
952-
p := TranslatePath(path, -outsideBounds.Left, -outsideBounds.top);
953-
DrawPolygon(mask, p, fillRule, clBlack32);
954-
img.CopyBlend(mask, mask.Bounds, outsideBounds, BlendMaskLine);
983+
SetLength(polygons, 1);
984+
polygons[0] := path;
985+
Rasterize(img, polygons, outsideBounds, fillRule, renderer);
955986
finally
956-
mask.Free;
987+
renderer.Free;
957988
end;
958989
end;
959990
//------------------------------------------------------------------------------
960991

961992
procedure EraseOutsidePaths(img: TImage32; const paths: TPathsD;
962993
fillRule: TFillRule; const outsideBounds: TRect;
963-
rendererCache: TCustomColorRendererCache);
994+
rendererCache: TCustomRendererCache);
964995
var
965-
mask: TImage32;
966-
pp: TPathsD;
967-
w,h: integer;
996+
w, h: integer;
997+
renderer: TMaskRenderer;
998+
r: TRect;
968999
begin
9691000
if not assigned(paths) then Exit;
970-
RectWidthHeight(outsideBounds, w,h);
971-
mask := TImage32.Create(w, h);
1001+
RectWidthHeight(outsideBounds, w, h);
1002+
if (w <= 0) or (h <= 0) then Exit;
1003+
1004+
// We can skip the costly polygon rasterization if the path is
1005+
// a rectangle.
1006+
if (fillRule in [frEvenOdd, frNonZero]) and IsSimpleRectanglePath(paths, r) then
1007+
begin
1008+
EraseOutsideRect(img, r, outsideBounds);
1009+
Exit;
1010+
end;
1011+
1012+
if rendererCache = nil then
1013+
renderer := TMaskRenderer.Create
1014+
else
1015+
renderer := rendererCache.MaskRenderer;
9721016
try
973-
pp := TranslatePath(paths, -outsideBounds.Left, -outsideBounds.top);
974-
DrawPolygon(mask, pp, fillRule, clBlack32, rendererCache);
975-
img.CopyBlend(mask, mask.Bounds, outsideBounds, BlendMaskLine);
1017+
Rasterize(img, paths, outsideBounds, fillRule, renderer);
9761018
finally
977-
mask.Free;
1019+
if rendererCache = nil then
1020+
renderer.Free;
9781021
end;
9791022
end;
9801023
//------------------------------------------------------------------------------

Ext/SVGIconImageList/Image32/source/Img32.SVG.Reader.pas

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ TSvgReader = class
120120
fClassStyles : TClassStylesList;
121121
fLinGradRenderer : TLinearGradientRenderer;
122122
fRadGradRenderer : TSvgRadialGradientRenderer;
123-
fCustomColorRendererCache: TCustomColorRendererCache;
123+
fCustomRendererCache: TCustomRendererCache;
124124
fRootElement : TSvgElement;
125125
fFontCache : TFontCache;
126126
fUsePropScale : Boolean;
@@ -1016,9 +1016,9 @@ procedure TGroupElement.Draw(image: TImage32; drawDat: TDrawData);
10161016
begin
10171017
if fDrawData.fillRule = frNegative then
10181018
EraseOutsidePaths(tmpImg, clipPaths, frNonZero, clipRec,
1019-
fReader.fCustomColorRendererCache) else
1019+
fReader.fCustomRendererCache) else
10201020
EraseOutsidePaths(tmpImg, clipPaths, fDrawData.fillRule, clipRec,
1021-
fReader.fCustomColorRendererCache);
1021+
fReader.fCustomRendererCache);
10221022
end;
10231023
image.CopyBlend(tmpImg, clipRec, clipRec, BlendToAlphaLine);
10241024
finally
@@ -2122,7 +2122,7 @@ procedure TFeGaussElement.Apply;
21222122

21232123
// FastGaussianBlur is a very good approximation and also very much faster.
21242124
// Empirically stdDev * PI/4 more closely emulates other renderers.
2125-
FastGaussianBlur(dstImg, dstRec, Ceil(stdDev * PI/4 * ParentFilterEl.fScale));
2125+
FastGaussianBlur(dstImg, dstRec, Ceil(stdDev * (PI/4) * ParentFilterEl.fScale));
21262126
end;
21272127

21282128
//------------------------------------------------------------------------------
@@ -2395,9 +2395,9 @@ procedure TShapeElement.Draw(image: TImage32; drawDat: TDrawData);
23952395
begin
23962396
if fDrawData.fillRule = frNegative then
23972397
EraseOutsidePaths(img, clipPaths, frNonZero, clipRec2,
2398-
fReader.fCustomColorRendererCache) else
2398+
fReader.fCustomRendererCache) else
23992399
EraseOutsidePaths(img, clipPaths, fDrawData.fillRule, clipRec2,
2400-
fReader.fCustomColorRendererCache);
2400+
fReader.fCustomRendererCache);
24012401
end;
24022402

24032403
if usingTempImage and (img <> image) then
@@ -2540,14 +2540,14 @@ procedure TShapeElement.DrawFilled(img: TImage32; drawDat: TDrawData);
25402540
else if drawDat.fillColor = clInvalid then
25412541
begin
25422542
DrawPolygon(img, fillPaths, drawDat.fillRule, clBlack32,
2543-
fReader.fCustomColorRendererCache);
2543+
fReader.fCustomRendererCache);
25442544
end
25452545
else
25462546
with drawDat do
25472547
begin
25482548
DrawPolygon(img, fillPaths, fillRule,
25492549
MergeColorAndOpacity(fillColor, fillOpacity),
2550-
fReader.fCustomColorRendererCache);
2550+
fReader.fCustomRendererCache);
25512551
end;
25522552
end;
25532553
//------------------------------------------------------------------------------
@@ -2629,7 +2629,7 @@ procedure TShapeElement.DrawStroke(img: TImage32;
26292629
strokePaths := MatrixApply(drawPathsO, drawDat.matrix);
26302630
DrawDashedLine(img, strokePaths, dashArray,
26312631
@dashOffset, sw * scale, strokeClr, endStyle, jsAuto,
2632-
fReader.fCustomColorRendererCache);
2632+
fReader.fCustomRendererCache);
26332633
Exit;
26342634
end;
26352635
strokePaths := RoughOutline(drawPathsO, sw, joinStyle, endStyle, lim);
@@ -2662,7 +2662,7 @@ procedure TShapeElement.DrawStroke(img: TImage32;
26622662
end;
26632663
end else
26642664
begin
2665-
DrawPolygon(img, strokePaths, frNonZero, strokeClr, fReader.fCustomColorRendererCache);
2665+
DrawPolygon(img, strokePaths, frNonZero, strokeClr, fReader.fCustomRendererCache);
26662666
end;
26672667
end;
26682668

@@ -4902,7 +4902,7 @@ constructor TSvgReader.Create;
49024902
fClassStyles := TClassStylesList.Create;
49034903
fLinGradRenderer := TLinearGradientRenderer.Create;
49044904
fRadGradRenderer := TSvgRadialGradientRenderer.Create;
4905-
fCustomColorRendererCache := TCustomColorRendererCache.Create;
4905+
fCustomRendererCache := TCustomRendererCache.Create;
49064906
fIdList := TStringList.Create;
49074907
fIdList.Duplicates := dupIgnore;
49084908
fIdList.CaseSensitive := false;
@@ -4925,7 +4925,7 @@ destructor TSvgReader.Destroy;
49254925

49264926
fLinGradRenderer.Free;
49274927
fRadGradRenderer.Free;
4928-
fCustomColorRendererCache.Free;
4928+
fCustomRendererCache.Free;
49294929
FreeAndNil(fFontCache);
49304930
fSimpleDrawList.Free;
49314931

Ext/SVGIconImageList/Image32/source/Img32.Vector.pas

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ interface
275275

276276
function IsClockwise(const path: TPathD): Boolean;
277277

278+
// IsSimpleRectanglePath returns true if the specified path has only one polygon
279+
// with 4 points that describe a rectangle.
280+
function IsSimpleRectanglePath(const paths: TPathsD; var R: TRect): Boolean; overload;
281+
function IsSimpleRectanglePath(const path: TPathD; var R: TRect): Boolean; overload;
282+
278283
function Area(const path: TPathD): Double; overload;
279284

280285
function RectsEqual(const rec1, rec2: TRect): Boolean;
@@ -791,6 +796,63 @@ function IsClockwise(const path: TPathD): Boolean;
791796
end;
792797
//------------------------------------------------------------------------------
793798

799+
function IsSimpleRectanglePath(const path: TPathD; var R: TRect): Boolean;
800+
type
801+
TLastMatch = (lmX, lmY);
802+
var
803+
i: Integer;
804+
lastMatch: TLastMatch;
805+
begin
806+
Result := False;
807+
// If we have a single path with 4 points, it could be a rectangle
808+
if Length(path) = 4 then
809+
begin
810+
// For a rectangle the X and Y coordinates of the points alternate
811+
// in being equal
812+
if path[0].X = path[3].X then
813+
lastMatch := lmX
814+
else if path[0].Y = path[3].Y then
815+
lastMatch := lmY
816+
else
817+
Exit;
818+
819+
R.Left := Trunc(path[0].X);
820+
R.Top := Trunc(path[0].Y);
821+
R.Right := Ceil(path[0].X);
822+
R.Bottom := Ceil(path[0].Y);
823+
for i := 1 to 3 do
824+
begin
825+
case lastMatch of
826+
lmY: // now the X-coordinates must be equal
827+
begin
828+
if path[i].X <> path[i - 1].X then Exit;
829+
lastMatch := lmX;
830+
R.Top := Min(R.Top, Trunc(path[i].Y));
831+
R.Bottom := Max(R.Bottom, Ceil(path[i].Y));
832+
end;
833+
lmX: // now the Y-coordinates must be equal
834+
begin
835+
if path[i].Y <> path[i - 1].Y then Exit;
836+
lastMatch := lmY;
837+
R.Left := Min(R.Left, Trunc(path[i].X));
838+
R.Right := Max(R.Right, Ceil(path[i].X));
839+
end;
840+
end;
841+
end;
842+
Result := True;
843+
end;
844+
end;
845+
846+
//------------------------------------------------------------------------------
847+
function IsSimpleRectanglePath(const paths: TPathsD; var R: TRect): Boolean;
848+
begin
849+
if (Length(paths) = 1) and (Length(paths[0]) = 4) then
850+
Result := IsSimpleRectanglePath(paths[0], r)
851+
else
852+
Result := False;
853+
end;
854+
//------------------------------------------------------------------------------
855+
794856
function Area(const path: TPathD): Double;
795857
var
796858
i, j, highI: Integer;
@@ -1915,7 +1977,7 @@ procedure ConcatPaths(var dstPath: TPathD; const paths: TPathsD);
19151977
pathLen := Length(paths[i]);
19161978
if pathLen > 0 then
19171979
begin
1918-
// Skip the start-point if is matches the previous path's end-point
1980+
// Skip the start-point if it matches the previous path's end-point
19191981
if (i > 0) and PointsEqual(paths[i][0], paths[i -1][high(paths[i -1])]) then
19201982
dec(pathLen);
19211983
inc(len, pathLen);
@@ -1931,14 +1993,19 @@ procedure ConcatPaths(var dstPath: TPathD; const paths: TPathsD);
19311993
if pathLen > 0 then
19321994
begin
19331995
offset := 0;
1934-
// Skip the start-point if is matches the previous path's end-point
1996+
// Skip the start-point if it matches the previous path's end-point
19351997
if (i > 0) and PointsEqual(paths[i][0], paths[i -1][high(paths[i -1])]) then
19361998
begin
19371999
dec(pathLen);
19382000
offset := 1;
19392001
end;
1940-
Move(paths[i][offset], dstPath[len], pathLen * SizeOf(TPointD));
1941-
inc(len, pathLen);
2002+
// Skip if we have a path with only one point and that point also matches
2003+
// the previous path's end-point.
2004+
if pathLen > 0 then
2005+
begin
2006+
Move(paths[i][offset], dstPath[len], pathLen * SizeOf(TPointD));
2007+
inc(len, pathLen);
2008+
end;
19422009
end;
19432010
end;
19442011
end;

Ext/SVGIconImageList/Image32/source/Img32.pas

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,8 @@ function BlendMask(bgColor, alphaMask: TColor32): TColor32;
12691269
{$RANGECHECKS OFF} // negative array index is used
12701270

12711271
procedure BlendMaskLine(bgColor, alphaMask: PColor32; width: nativeint);
1272+
label
1273+
SkipNone32;
12721274
var
12731275
a: byte;
12741276
begin
@@ -1283,13 +1285,22 @@ procedure BlendMaskLine(bgColor, alphaMask: PColor32; width: nativeint);
12831285
// common values.
12841286
while width < 0 do
12851287
begin
1288+
// MulTable[0, fgA] -> 0, if bgColor is already 0 => skip
1289+
while PStaticARGBArray(bgColor)[width].Color = 0 do
1290+
begin
1291+
SkipNone32:
1292+
inc(width);
1293+
if width = 0 then exit;
1294+
end;
12861295
a := PStaticARGBArray(bgColor)[width].A;
12871296
// MulTable[0, fgA] -> 0 => replace color with 0
12881297
while a = 0 do
12891298
begin
12901299
PStaticColor32Array(bgColor)[width] := 0;
12911300
inc(width);
12921301
if width = 0 then exit;
1302+
if PStaticARGBArray(bgColor)[width].Color = 0 then
1303+
goto SkipNone32;
12931304
a := PStaticARGBArray(bgColor)[width].A;
12941305
end;
12951306
// MulTable[255, fgA] -> fgA => replace alpha with fgA
@@ -3707,12 +3718,15 @@ procedure TImage32.ReduceOpacity(opacity: Byte);
37073718
var
37083719
i: Integer;
37093720
c: PARGB;
3721+
a: Byte;
37103722
begin
37113723
if opacity = 255 then Exit;
37123724
c := PARGB(PixelBase);
37133725
for i := 0 to Width * Height -1 do
37143726
begin
3715-
c.A := MulTable[c.A, opacity];
3727+
a := c.A;
3728+
if a <> 0 then
3729+
c.A := MulTable[a, opacity];
37163730
inc(c);
37173731
end;
37183732
Changed;
@@ -3723,19 +3737,24 @@ procedure TImage32.ReduceOpacity(opacity: Byte; rec: TRect);
37233737
var
37243738
i,j, rw: Integer;
37253739
c: PARGB;
3740+
a: Byte;
3741+
lineOffsetInBytes: integer;
37263742
begin
37273743
Types.IntersectRect(rec, rec, bounds);
37283744
if IsEmptyRect(rec) then Exit;
37293745
rw := RectWidth(rec);
37303746
c := @Pixels[rec.Top * Width + rec.Left];
3731-
for i := rec.Top to rec.Bottom -1 do
3747+
lineOffsetInBytes := (Width - rw) * SizeOf(TARGB);
3748+
for i := rec.Top to rec.Bottom - 1 do
37323749
begin
37333750
for j := 1 to rw do
37343751
begin
3735-
c.A := MulTable[c.A, opacity];
3752+
a := c.A;
3753+
if a <> 0 then
3754+
c.A := MulTable[a, opacity];
37363755
inc(c);
37373756
end;
3738-
inc(c, Width - rw);
3757+
inc(PByte(c), lineOffsetInBytes);
37393758
end;
37403759
Changed;
37413760
end;

0 commit comments

Comments
 (0)