Skip to content

Commit 06dd7ef

Browse files
committed
Fixed a tracing issue that affects images on transparent background.
1 parent 9d6fb72 commit 06dd7ef

12 files changed

+154
-1780
lines changed

SvgFileType.ThirdPartyNotices.txt

Lines changed: 0 additions & 1648 deletions
This file was deleted.

SvgFileType/Export/SvgExport.cs

Lines changed: 86 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
using PaintDotNet.AppModel;
1313
using PaintDotNet.PropertySystem;
1414
using PaintDotNet.PropertySystem.Extensions;
15+
using SvgFileTypePlugin.Extensions;
1516
using SvgFileTypePlugin.Localization;
17+
using static PaintDotNet.UserBlendOps;
1618

1719
namespace SvgFileTypePlugin.Export;
1820

@@ -101,11 +103,11 @@ public static void Export(Surface input, Stream output, PropertyCollection props
101103
backend.Rx /= scale;
102104
backend.Ry /= scale;
103105

104-
bool dialogVisible = UIHelper.IsSaveConfigDialogVisible();
106+
bool isSaveConfigDialogVisible = UIHelper.IsSaveConfigDialogVisible();
105107
Surface surface;
106108
const int maxDimForPreview = 1280;
107109

108-
if (dialogVisible && previewMode == PreviewMode.Fast && input.Width * input.Height > maxDimForPreview * maxDimForPreview)
110+
if (isSaveConfigDialogVisible && previewMode == PreviewMode.Fast && input.Width * input.Height > maxDimForPreview * maxDimForPreview)
109111
{
110112
// Preview Only
111113
// Image downscaled to speed up preview generation.
@@ -121,109 +123,111 @@ public static void Export(Surface input, Stream output, PropertyCollection props
121123
surface.FitSurface(ResamplingAlgorithm.Cubic, input);
122124
}
123125
else
126+
{
124127
surface = input.Clone();
128+
}
129+
130+
using var _ = surface;
131+
surface.BlendOnto<NormalBlendOp>(ColorBgra.White);
125132

126-
using (surface)
127-
using (CancellationTokenSource cts = new CancellationTokenSource())
133+
using CancellationTokenSource cts = new CancellationTokenSource();
134+
float full = isSaveConfigDialogVisible ? 90f : 100f;
135+
136+
CancellationToken cancellationToken = cts.Token;
137+
if (scanMode == ScanMode.Opaque)
128138
{
129-
float full = dialogVisible ? 90f : 100f;
139+
backend.Opaque = true;
140+
backend.FillColor = fillcolor;
141+
}
130142

131-
void OnProgress(float prog)
132-
{
133-
try
134-
{
135-
progressCallback.Invoke(null, new ProgressEventArgs(prog * full));
136-
}
137-
catch (OperationCanceledException)
138-
{
139-
try
140-
{
141-
cts.Cancel();
142-
}
143-
catch (ObjectDisposedException)
144-
{
145-
// ignore
146-
}
147-
}
148-
}
143+
if (highpass == 0)
144+
{
145+
bm = PotraceBitmap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height, c: brightnessCutoff);
146+
}
147+
else
148+
{
149+
using Greymap gm = Greymap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height);
150+
gm.HighPassFilter(lambda: highpass);
149151

150-
CancellationToken cancellationToken = cts.Token;
151-
if (scanMode == ScanMode.Opaque)
152-
{
153-
backend.Opaque = true;
154-
backend.FillColor = fillcolor;
155-
}
152+
if (lowpass > 0)
153+
gm.LowPassFilter(lambda: lowpass);
156154

157-
if (highpass == 0)
155+
if (gmScale > 1)
158156
{
159-
bm = PotraceBitmap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height, c: brightnessCutoff);
157+
bm = gm.InterpolateCubicBilevel(gmScale, c: brightnessCutoff);
158+
backend.Rx *= gmScale;
159+
backend.Ry *= gmScale;
160160
}
161161
else
162-
{
163-
using Greymap gm = Greymap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height);
164-
gm.HighPassFilter(lambda: highpass);
162+
bm = gm.Threshold(c: brightnessCutoff);
163+
}
164+
165+
using (bm)
166+
{
167+
if (invert)
168+
bm.Invert();
165169

166-
if (lowpass > 0)
167-
gm.LowPassFilter(lambda: lowpass);
170+
if (enclose)
171+
bm.Enclose();
168172

169-
if (gmScale > 1)
170-
{
171-
bm = gm.InterpolateCubicBilevel(gmScale, c: brightnessCutoff);
172-
backend.Rx *= gmScale;
173-
backend.Ry *= gmScale;
174-
}
175-
else
176-
bm = gm.Threshold(c: brightnessCutoff);
177-
}
173+
Progress<ProgressArgs> progress = new Progress<ProgressArgs>((prog) => OnProgress(ConvertProgressValue(prog)));
174+
trace = bm.Trace(turdsize, turnpolicy, alphamax, opttolerance, progress, cancellationToken);
175+
imginfo = bm.Info;
176+
imginfo.Tight = tight;
177+
}
178+
imginfo.Angle = angle;
179+
if (trace == null)
180+
{
181+
if (UIHelper.IsSaveConfigDialogVisible())
182+
return;
183+
else
184+
throw new InvalidOperationException(StringResources.NoPath);
185+
}
178186

179-
using (bm)
187+
if (isSaveConfigDialogVisible && scanMode == ScanMode.Transparent && shapePath.Length > 0)
188+
{
189+
PdnShapeBackEnd pdnbackend = new PdnShapeBackEnd
180190
{
181-
if (invert)
182-
bm.Invert();
191+
ColumnWidth = int.MaxValue,
192+
DisplayName = shapeName
193+
};
194+
using (FileStream shapeStream = File.Open(shapePath, FileMode.Create, FileAccess.Write))
195+
pdnbackend.Save(shapeStream, trace, imginfo, cancellationToken);
183196

184-
if (enclose)
185-
bm.Enclose();
197+
StringBuilder msg = new StringBuilder();
198+
msg.AppendFormat(StringResources.ShapeSaved, shapePath);
186199

187-
Progress<ProgressArgs> progress = new Progress<ProgressArgs>((prog) => OnProgress(ConvertProgressValue(prog)));
188-
trace = bm.Trace(turdsize, turnpolicy, alphamax, opttolerance, progress, cancellationToken);
189-
imginfo = bm.Info;
190-
imginfo.Tight = tight;
191-
}
192-
imginfo.Angle = angle;
193-
if (trace == null)
200+
if (ShapesDirectory.Value != null && Path.GetDirectoryName(shapePath)?.StartsWith(ShapesDirectory.Value, StringComparison.OrdinalIgnoreCase) == true)
194201
{
195-
if (UIHelper.IsSaveConfigDialogVisible())
196-
return;
197-
else
198-
throw new InvalidOperationException(StringResources.NoPath);
202+
msg.AppendLine();
203+
msg.AppendLine();
204+
msg.AppendLine(StringResources.ShapeSavedRestart);
199205
}
200206

201-
if (dialogVisible && scanMode == ScanMode.Transparent && shapePath.Length > 0)
207+
UIHelper.RunOnUIThread(() =>
202208
{
203-
PdnShapeBackEnd pdnbackend = new PdnShapeBackEnd
204-
{
205-
ColumnWidth = int.MaxValue,
206-
DisplayName = shapeName
207-
};
208-
using (FileStream shapeStream = File.Open(shapePath, FileMode.Create, FileAccess.Write))
209-
pdnbackend.Save(shapeStream, trace, imginfo, cancellationToken);
210-
211-
StringBuilder msg = new StringBuilder();
212-
msg.AppendFormat(StringResources.ShapeSaved, shapePath);
209+
MessageBox.Show(msg.ToString(), StringResources.ShapeSavedCaption, MessageBoxButtons.OK, MessageBoxIcon.Information);
210+
});
211+
}
212+
backend.Save(output, trace, imginfo, cancellationToken);
213213

214-
if (ShapesDirectory.Value != null && Path.GetDirectoryName(shapePath)?.StartsWith(ShapesDirectory.Value, StringComparison.OrdinalIgnoreCase) == true)
214+
void OnProgress(float prog)
215+
{
216+
try
217+
{
218+
progressCallback.Invoke(null, new ProgressEventArgs(prog * full));
219+
}
220+
catch (OperationCanceledException)
221+
{
222+
try
215223
{
216-
msg.AppendLine();
217-
msg.AppendLine();
218-
msg.AppendLine(StringResources.ShapeSavedRestart);
224+
cts.Cancel();
225+
}
226+
catch (ObjectDisposedException)
227+
{
228+
// ignore
219229
}
220-
221-
UIHelper.RunOnUIThread(() =>
222-
{
223-
MessageBox.Show(msg.ToString(), StringResources.ShapeSavedCaption, MessageBoxButtons.OK, MessageBoxIcon.Information);
224-
});
225230
}
226-
backend.Save(output, trace, imginfo, cancellationToken);
227231
}
228232
}
229233

SvgFileType/Extensions/SurfaceExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,19 @@ public static bool IsEmpty(this Surface surface)
6161
}
6262
return true;
6363
}
64+
65+
public static void BlendOnto<T>(this Surface surface, ColorBgra backgroundColor) where T : UserBlendOp, new()
66+
{
67+
ArgumentNullException.ThrowIfNull(surface);
68+
69+
using Surface tmp = surface.Clone();
70+
int w = tmp.Width;
71+
int h = tmp.Height;
72+
int stride = tmp.Stride;
73+
surface.Fill(backgroundColor);
74+
unsafe
75+
{
76+
new T().UnsafeApply(w, h, (ColorBgra*)surface.Scan0.Pointer, stride, (ColorBgra*)tmp.Scan0.Pointer, stride);
77+
}
78+
}
6479
}

SvgFileType/Import/MyBaseForm.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
namespace SvgFileTypePlugin.Import;
1414

15-
internal abstract class MyBaseForm : Form
15+
internal class MyBaseForm : Form
1616
{
1717
#region Properties
1818

SvgFileType/Import/ResvgSvgRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private void RenderSvgDocument(SvgElement element, Surface surface, ResvgOptions
145145
}
146146
}
147147

148-
private static ResvgTransform CalculateTransform(SizeF svgsize, SvgImportConfig config, float tolerance = .99f)
148+
private static ResvgTransform CalculateTransform(SizeF svgsize, SvgImportConfig config, float tolerance = 1f)
149149
{
150150
float ratioX, ratioY;
151151
ratioX = config.RasterWidth / svgsize.Width * tolerance;

SvgFileType/Import/SvgImport.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ private static string Open(Stream input)
6565
ms.Write(buf);
6666
input.CopyTo(ms);
6767

68-
// We are not closing the original input stream.
68+
// Do not close the source stream.
69+
// It also can be used to track cancellation.
6970

7071
ms.Position = 0;
71-
ReadOnlySpan<byte> gzipMagicBytes = [0x1f, 0x8b, 0x8];
72+
73+
ReadOnlySpan<byte> gzipMagicBytes = [0x1F, 0x8B, 0x8];
7274
input = buf.SequenceEqual(gzipMagicBytes)
7375
? new GZipStream(ms, CompressionMode.Decompress, leaveOpen: false)
7476
: ms;

0 commit comments

Comments
 (0)