Skip to content

Commit

Permalink
ver. 3.2.3
Browse files Browse the repository at this point in the history
- Built with Delphi 12.2
- Update Image32 Engine to fix rendering of some SVG Icons
  • Loading branch information
carloBarazzetta committed Sep 14, 2024
1 parent 669fa8f commit ad7544f
Show file tree
Hide file tree
Showing 22 changed files with 1,389 additions and 175 deletions.
301 changes: 225 additions & 76 deletions Ext/SVGIconImageList/Image32/source/Img32.Draw.pas

Large diffs are not rendered by default.

83 changes: 63 additions & 20 deletions Ext/SVGIconImageList/Image32/source/Img32.Extra.pas
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ procedure EraseOutsidePath(img: TImage32; const path: TPathD;
fillRule: TFillRule; const outsideBounds: TRect);
procedure EraseOutsidePaths(img: TImage32; const paths: TPathsD;
fillRule: TFillRule; const outsideBounds: TRect;
rendererCache: TCustomColorRendererCache = nil); overload;
rendererCache: TCustomRendererCache = nil); overload;

procedure Draw3D(img: TImage32; const polygon: TPathD;
fillRule: TFillRule; height, blurRadius: double;
Expand Down Expand Up @@ -938,43 +938,86 @@ procedure EraseInsidePaths(img: TImage32; const paths: TPathsD; fillRule: TFillR
end;
//------------------------------------------------------------------------------

procedure EraseOutsideRect(img: TImage32; const r, outsideBounds: TRect);
begin
// Fill the parts, that are in outsideBounds but not in r with zeros

// whole top block
if r.Top > outsideBounds.Top then
img.FillRect(Rect(outsideBounds.Left, outsideBounds.Top, outsideBounds.Right, r.Top - 1), 0);
// whole bottom block
if r.Bottom < outsideBounds.Bottom then
img.FillRect(Rect(outsideBounds.Left, r.Bottom + 1, outsideBounds.Right, outsideBounds.Bottom), 0);

// remaining left block
if r.Left > outsideBounds.Left then
img.FillRect(Rect(outsideBounds.Left, r.Top, r.Left - 1, r.Bottom), 0);
// remaining right block
if r.Right < outsideBounds.Right then
img.FillRect(Rect(r.Right + 1, r.Top, outsideBounds.Right, r.Bottom), 0);
end;
//------------------------------------------------------------------------------

procedure EraseOutsidePath(img: TImage32; const path: TPathD;
fillRule: TFillRule; const outsideBounds: TRect);
var
mask: TImage32;
p: TPathD;
w,h: integer;
w, h: integer;
renderer: TMaskRenderer;
r: TRect;
polygons: TPathsD;
begin
if not assigned(path) then Exit;
RectWidthHeight(outsideBounds, w,h);
mask := TImage32.Create(w, h);
RectWidthHeight(outsideBounds, w, h);
if (w <= 0) or (h <= 0) then Exit;

// We can skip the costly polygon rasterization if the path is
// a rectangle
if (fillRule in [frEvenOdd, frNonZero]) and IsSimpleRectanglePath(path, r) then
begin
EraseOutsideRect(img, r, outsideBounds);
Exit;
end;

renderer := TMaskRenderer.Create;
try
p := TranslatePath(path, -outsideBounds.Left, -outsideBounds.top);
DrawPolygon(mask, p, fillRule, clBlack32);
img.CopyBlend(mask, mask.Bounds, outsideBounds, BlendMaskLine);
SetLength(polygons, 1);
polygons[0] := path;
Rasterize(img, polygons, outsideBounds, fillRule, renderer);
finally
mask.Free;
renderer.Free;
end;
end;
//------------------------------------------------------------------------------

procedure EraseOutsidePaths(img: TImage32; const paths: TPathsD;
fillRule: TFillRule; const outsideBounds: TRect;
rendererCache: TCustomColorRendererCache);
rendererCache: TCustomRendererCache);
var
mask: TImage32;
pp: TPathsD;
w,h: integer;
w, h: integer;
renderer: TMaskRenderer;
r: TRect;
begin
if not assigned(paths) then Exit;
RectWidthHeight(outsideBounds, w,h);
mask := TImage32.Create(w, h);
RectWidthHeight(outsideBounds, w, h);
if (w <= 0) or (h <= 0) then Exit;

// We can skip the costly polygon rasterization if the path is
// a rectangle.
if (fillRule in [frEvenOdd, frNonZero]) and IsSimpleRectanglePath(paths, r) then
begin
EraseOutsideRect(img, r, outsideBounds);
Exit;
end;

if rendererCache = nil then
renderer := TMaskRenderer.Create
else
renderer := rendererCache.MaskRenderer;
try
pp := TranslatePath(paths, -outsideBounds.Left, -outsideBounds.top);
DrawPolygon(mask, pp, fillRule, clBlack32, rendererCache);
img.CopyBlend(mask, mask.Bounds, outsideBounds, BlendMaskLine);
Rasterize(img, paths, outsideBounds, fillRule, renderer);
finally
mask.Free;
if rendererCache = nil then
renderer.Free;
end;
end;
//------------------------------------------------------------------------------
Expand Down
24 changes: 12 additions & 12 deletions Ext/SVGIconImageList/Image32/source/Img32.SVG.Reader.pas
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ TSvgReader = class
fClassStyles : TClassStylesList;
fLinGradRenderer : TLinearGradientRenderer;
fRadGradRenderer : TSvgRadialGradientRenderer;
fCustomColorRendererCache: TCustomColorRendererCache;
fCustomRendererCache: TCustomRendererCache;
fRootElement : TSvgElement;
fFontCache : TFontCache;
fUsePropScale : Boolean;
Expand Down Expand Up @@ -1016,9 +1016,9 @@ procedure TGroupElement.Draw(image: TImage32; drawDat: TDrawData);
begin
if fDrawData.fillRule = frNegative then
EraseOutsidePaths(tmpImg, clipPaths, frNonZero, clipRec,
fReader.fCustomColorRendererCache) else
fReader.fCustomRendererCache) else
EraseOutsidePaths(tmpImg, clipPaths, fDrawData.fillRule, clipRec,
fReader.fCustomColorRendererCache);
fReader.fCustomRendererCache);
end;
image.CopyBlend(tmpImg, clipRec, clipRec, BlendToAlphaLine);
finally
Expand Down Expand Up @@ -2122,7 +2122,7 @@ procedure TFeGaussElement.Apply;

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

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -2395,9 +2395,9 @@ procedure TShapeElement.Draw(image: TImage32; drawDat: TDrawData);
begin
if fDrawData.fillRule = frNegative then
EraseOutsidePaths(img, clipPaths, frNonZero, clipRec2,
fReader.fCustomColorRendererCache) else
fReader.fCustomRendererCache) else
EraseOutsidePaths(img, clipPaths, fDrawData.fillRule, clipRec2,
fReader.fCustomColorRendererCache);
fReader.fCustomRendererCache);
end;

if usingTempImage and (img <> image) then
Expand Down Expand Up @@ -2540,14 +2540,14 @@ procedure TShapeElement.DrawFilled(img: TImage32; drawDat: TDrawData);
else if drawDat.fillColor = clInvalid then
begin
DrawPolygon(img, fillPaths, drawDat.fillRule, clBlack32,
fReader.fCustomColorRendererCache);
fReader.fCustomRendererCache);
end
else
with drawDat do
begin
DrawPolygon(img, fillPaths, fillRule,
MergeColorAndOpacity(fillColor, fillOpacity),
fReader.fCustomColorRendererCache);
fReader.fCustomRendererCache);
end;
end;
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -2629,7 +2629,7 @@ procedure TShapeElement.DrawStroke(img: TImage32;
strokePaths := MatrixApply(drawPathsO, drawDat.matrix);
DrawDashedLine(img, strokePaths, dashArray,
@dashOffset, sw * scale, strokeClr, endStyle, jsAuto,
fReader.fCustomColorRendererCache);
fReader.fCustomRendererCache);
Exit;
end;
strokePaths := RoughOutline(drawPathsO, sw, joinStyle, endStyle, lim);
Expand Down Expand Up @@ -2662,7 +2662,7 @@ procedure TShapeElement.DrawStroke(img: TImage32;
end;
end else
begin
DrawPolygon(img, strokePaths, frNonZero, strokeClr, fReader.fCustomColorRendererCache);
DrawPolygon(img, strokePaths, frNonZero, strokeClr, fReader.fCustomRendererCache);
end;
end;

Expand Down Expand Up @@ -4902,7 +4902,7 @@ constructor TSvgReader.Create;
fClassStyles := TClassStylesList.Create;
fLinGradRenderer := TLinearGradientRenderer.Create;
fRadGradRenderer := TSvgRadialGradientRenderer.Create;
fCustomColorRendererCache := TCustomColorRendererCache.Create;
fCustomRendererCache := TCustomRendererCache.Create;
fIdList := TStringList.Create;
fIdList.Duplicates := dupIgnore;
fIdList.CaseSensitive := false;
Expand All @@ -4925,7 +4925,7 @@ destructor TSvgReader.Destroy;

fLinGradRenderer.Free;
fRadGradRenderer.Free;
fCustomColorRendererCache.Free;
fCustomRendererCache.Free;
FreeAndNil(fFontCache);
fSimpleDrawList.Free;

Expand Down
75 changes: 71 additions & 4 deletions Ext/SVGIconImageList/Image32/source/Img32.Vector.pas
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ interface

function IsClockwise(const path: TPathD): Boolean;

// IsSimpleRectanglePath returns true if the specified path has only one polygon
// with 4 points that describe a rectangle.
function IsSimpleRectanglePath(const paths: TPathsD; var R: TRect): Boolean; overload;
function IsSimpleRectanglePath(const path: TPathD; var R: TRect): Boolean; overload;

function Area(const path: TPathD): Double; overload;

function RectsEqual(const rec1, rec2: TRect): Boolean;
Expand Down Expand Up @@ -791,6 +796,63 @@ function IsClockwise(const path: TPathD): Boolean;
end;
//------------------------------------------------------------------------------

function IsSimpleRectanglePath(const path: TPathD; var R: TRect): Boolean;
type
TLastMatch = (lmX, lmY);
var
i: Integer;
lastMatch: TLastMatch;
begin
Result := False;
// If we have a single path with 4 points, it could be a rectangle
if Length(path) = 4 then
begin
// For a rectangle the X and Y coordinates of the points alternate
// in being equal
if path[0].X = path[3].X then
lastMatch := lmX
else if path[0].Y = path[3].Y then
lastMatch := lmY
else
Exit;

R.Left := Trunc(path[0].X);
R.Top := Trunc(path[0].Y);
R.Right := Ceil(path[0].X);
R.Bottom := Ceil(path[0].Y);
for i := 1 to 3 do
begin
case lastMatch of
lmY: // now the X-coordinates must be equal
begin
if path[i].X <> path[i - 1].X then Exit;
lastMatch := lmX;
R.Top := Min(R.Top, Trunc(path[i].Y));
R.Bottom := Max(R.Bottom, Ceil(path[i].Y));
end;
lmX: // now the Y-coordinates must be equal
begin
if path[i].Y <> path[i - 1].Y then Exit;
lastMatch := lmY;
R.Left := Min(R.Left, Trunc(path[i].X));
R.Right := Max(R.Right, Ceil(path[i].X));
end;
end;
end;
Result := True;
end;
end;

//------------------------------------------------------------------------------
function IsSimpleRectanglePath(const paths: TPathsD; var R: TRect): Boolean;
begin
if (Length(paths) = 1) and (Length(paths[0]) = 4) then
Result := IsSimpleRectanglePath(paths[0], r)
else
Result := False;
end;
//------------------------------------------------------------------------------

function Area(const path: TPathD): Double;
var
i, j, highI: Integer;
Expand Down Expand Up @@ -1915,7 +1977,7 @@ procedure ConcatPaths(var dstPath: TPathD; const paths: TPathsD);
pathLen := Length(paths[i]);
if pathLen > 0 then
begin
// Skip the start-point if is matches the previous path's end-point
// Skip the start-point if it matches the previous path's end-point
if (i > 0) and PointsEqual(paths[i][0], paths[i -1][high(paths[i -1])]) then
dec(pathLen);
inc(len, pathLen);
Expand All @@ -1931,14 +1993,19 @@ procedure ConcatPaths(var dstPath: TPathD; const paths: TPathsD);
if pathLen > 0 then
begin
offset := 0;
// Skip the start-point if is matches the previous path's end-point
// Skip the start-point if it matches the previous path's end-point
if (i > 0) and PointsEqual(paths[i][0], paths[i -1][high(paths[i -1])]) then
begin
dec(pathLen);
offset := 1;
end;
Move(paths[i][offset], dstPath[len], pathLen * SizeOf(TPointD));
inc(len, pathLen);
// Skip if we have a path with only one point and that point also matches
// the previous path's end-point.
if pathLen > 0 then
begin
Move(paths[i][offset], dstPath[len], pathLen * SizeOf(TPointD));
inc(len, pathLen);
end;
end;
end;
end;
Expand Down
27 changes: 23 additions & 4 deletions Ext/SVGIconImageList/Image32/source/Img32.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,8 @@ function BlendMask(bgColor, alphaMask: TColor32): TColor32;
{$RANGECHECKS OFF} // negative array index is used

procedure BlendMaskLine(bgColor, alphaMask: PColor32; width: nativeint);
label
SkipNone32;
var
a: byte;
begin
Expand All @@ -1283,13 +1285,22 @@ procedure BlendMaskLine(bgColor, alphaMask: PColor32; width: nativeint);
// common values.
while width < 0 do
begin
// MulTable[0, fgA] -> 0, if bgColor is already 0 => skip
while PStaticARGBArray(bgColor)[width].Color = 0 do
begin
SkipNone32:
inc(width);
if width = 0 then exit;
end;
a := PStaticARGBArray(bgColor)[width].A;
// MulTable[0, fgA] -> 0 => replace color with 0
while a = 0 do
begin
PStaticColor32Array(bgColor)[width] := 0;
inc(width);
if width = 0 then exit;
if PStaticARGBArray(bgColor)[width].Color = 0 then
goto SkipNone32;
a := PStaticARGBArray(bgColor)[width].A;
end;
// MulTable[255, fgA] -> fgA => replace alpha with fgA
Expand Down Expand Up @@ -3707,12 +3718,15 @@ procedure TImage32.ReduceOpacity(opacity: Byte);
var
i: Integer;
c: PARGB;
a: Byte;
begin
if opacity = 255 then Exit;
c := PARGB(PixelBase);
for i := 0 to Width * Height -1 do
begin
c.A := MulTable[c.A, opacity];
a := c.A;
if a <> 0 then
c.A := MulTable[a, opacity];
inc(c);
end;
Changed;
Expand All @@ -3723,19 +3737,24 @@ procedure TImage32.ReduceOpacity(opacity: Byte; rec: TRect);
var
i,j, rw: Integer;
c: PARGB;
a: Byte;
lineOffsetInBytes: integer;
begin
Types.IntersectRect(rec, rec, bounds);
if IsEmptyRect(rec) then Exit;
rw := RectWidth(rec);
c := @Pixels[rec.Top * Width + rec.Left];
for i := rec.Top to rec.Bottom -1 do
lineOffsetInBytes := (Width - rw) * SizeOf(TARGB);
for i := rec.Top to rec.Bottom - 1 do
begin
for j := 1 to rw do
begin
c.A := MulTable[c.A, opacity];
a := c.A;
if a <> 0 then
c.A := MulTable[a, opacity];
inc(c);
end;
inc(c, Width - rw);
inc(PByte(c), lineOffsetInBytes);
end;
Changed;
end;
Expand Down
Loading

0 comments on commit ad7544f

Please sign in to comment.