19 using SixLabors.ImageSharp;
20 using SixLabors.ImageSharp.Drawing;
21 using SixLabors.ImageSharp.Processing;
22 using SixLabors.ImageSharp.Drawing.Processing;
23 using System.Collections.Generic;
24 using SixLabors.ImageSharp.Advanced;
30 internal static class MatrixUtils
32 public static System.Numerics.Matrix3x2 ToMatrix(
this double[,] matrix)
34 return new System.Numerics.Matrix3x2((
float)matrix[0, 0], (
float)matrix[1, 0], (
float)matrix[0, 1], (
float)matrix[1, 1], (
float)matrix[0, 2], (
float)matrix[1, 2]);
37 public static double[] Multiply(
double[,] matrix,
double[] vector)
39 double[] tbr =
new double[2];
41 tbr[0] = matrix[0, 0] * vector[0] + matrix[0, 1] * vector[1] + matrix[0, 2];
42 tbr[1] = matrix[1, 0] * vector[0] + matrix[1, 1] * vector[1] + matrix[1, 2];
47 public static Point Multiply(
this double[,] matrix, Point vector)
49 return new Point(matrix[0, 0] * vector.X + matrix[0, 1] * vector.Y + matrix[0, 2], matrix[1, 0] * vector.X + matrix[1, 1] * vector.Y + matrix[1, 2]);
52 public static double[,] Multiply(
double[,] matrix1,
double[,] matrix2)
54 double[,] tbr =
new double[3, 3];
56 for (
int i = 0; i < 3; i++)
58 for (
int j = 0; j < 3; j++)
60 for (
int k = 0; k < 3; k++)
62 tbr[i, j] += matrix1[i, k] * matrix2[k, j];
70 public static double[,] Rotate(
double[,] matrix,
double angle)
72 double[,] rotationMatrix =
new double[3, 3];
73 rotationMatrix[0, 0] = Math.Cos(angle);
74 rotationMatrix[0, 1] = -Math.Sin(angle);
75 rotationMatrix[1, 0] = Math.Sin(angle);
76 rotationMatrix[1, 1] = Math.Cos(angle);
77 rotationMatrix[2, 2] = 1;
79 return Multiply(matrix, rotationMatrix);
82 public static double[,] Translate(
double[,] matrix,
double x,
double y)
84 double[,] translationMatrix =
new double[3, 3];
85 translationMatrix[0, 0] = 1;
86 translationMatrix[0, 2] = x;
87 translationMatrix[1, 1] = 1;
88 translationMatrix[1, 2] = y;
89 translationMatrix[2, 2] = 1;
91 return Multiply(matrix, translationMatrix);
94 public static double[,] Scale(
double[,] matrix,
double scaleX,
double scaleY)
96 double[,] scaleMatrix =
new double[3, 3];
97 scaleMatrix[0, 0] = scaleX;
98 scaleMatrix[1, 1] = scaleY;
99 scaleMatrix[2, 2] = 1;
101 return Multiply(matrix, scaleMatrix);
104 public static double[,] Identity =
new double[,] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
105 public static double[,] Invert(
double[,] m)
107 double[,] tbr =
new double[3, 3];
109 tbr[0, 0] = (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
110 tbr[0, 1] = -(m[0, 1] * m[2, 2] - m[0, 2] * m[2, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
111 tbr[0, 2] = (m[0, 1] * m[1, 2] - m[0, 2] * m[1, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
112 tbr[1, 0] = -(m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
113 tbr[1, 1] = (m[0, 0] * m[2, 2] - m[0, 2] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
114 tbr[1, 2] = -(m[0, 0] * m[1, 2] - m[0, 2] * m[1, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
115 tbr[2, 0] = (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
116 tbr[2, 1] = -(m[0, 0] * m[2, 1] - m[0, 1] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
117 tbr[2, 2] = (m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
122 public static double Determinant(
double[,] matrix)
124 return (matrix[0, 0] * matrix[1, 1] - matrix[1, 0] * matrix[0, 1]) * matrix[2, 2] - (matrix[0, 0] * matrix[1, 2] - matrix[1, 0] * matrix[0, 2]) * matrix[2, 1] + (matrix[0, 1] * matrix[1, 2] - matrix[1, 1] * matrix[0, 2]) * matrix[2, 0];
128 internal class ImageSharpContext : IGraphicsContext
130 public Image<SixLabors.ImageSharp.PixelFormats.Rgba32> Image {
get; }
131 private Image Buffer {
get;
set; }
132 private Image ClipBuffer {
get; }
134 public double Width {
get;
private set; }
136 public double Height {
get;
private set; }
138 public int IntWidth {
get;
private set; }
139 public int IntHeight {
get;
private set; }
141 public Font Font {
get;
set; }
144 public Brush FillStyle {
get;
set; }
146 public Brush StrokeStyle {
get;
set; }
148 public double LineWidth {
get;
set; }
149 public LineCaps LineCap {
get;
set; }
152 public LineDash Dash {
get;
set; }
153 public string Tag {
get;
set; }
155 private double[,] _transform;
156 private readonly Stack<double[,]> states;
158 private readonly Stack<Image> clips;
159 private Image CurrentClip;
161 private List<IDisposable> disposables;
163 PathBuilder currentPath;
164 bool figureInitialised;
168 public ImageSharpContext(
double width,
double height,
double scaleFactor, Colour backgroundColour)
170 currentPath =
new PathBuilder();
171 figureInitialised =
false;
172 currentPoint =
new Point();
174 IntWidth = (int)(width * scaleFactor);
175 IntHeight = (int)(height * scaleFactor);
177 this.scaleFactor = Math.Sqrt(IntWidth / width * IntHeight / height);
179 _transform =
new double[3, 3];
181 _transform[0, 0] = scaleFactor;
182 _transform[1, 1] = scaleFactor;
183 _transform[2, 2] = 1;
185 states =
new Stack<double[,]>();
190 this.Image =
new Image<SixLabors.ImageSharp.PixelFormats.Rgba32>(IntWidth, IntHeight);
191 this.Image.Mutate(x => x.Fill(backgroundColour.ToImageSharpColor()));
193 this.Buffer =
new Image<SixLabors.ImageSharp.PixelFormats.Rgba32>(IntWidth, IntHeight);
194 this.Buffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
196 this.ClipBuffer =
new Image<SixLabors.ImageSharp.PixelFormats.Rgba32>(IntWidth, IntHeight);
197 this.ClipBuffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
199 this.clips =
new Stack<Image>();
200 this.CurrentClip =
null;
202 disposables =
new List<IDisposable>();
205 public void DisposeAllExceptImage()
207 for (
int i = 0; i < disposables.Count; i++)
209 disposables[i].Dispose();
210 this.CurrentClip?.Dispose();
211 this.Buffer.Dispose();
212 this.ClipBuffer.Dispose();
219 this.currentPath.CloseFigure();
220 this.figureInitialised =
false;
223 public void CubicBezierTo(
double p1X,
double p1Y,
double p2X,
double p2Y,
double p3X,
double p3Y)
225 Utils.CoerceNaNAndInfinityToZero(ref p1X, ref p1Y, ref p2X, ref p2Y, ref p3X, ref p3Y);
227 if (figureInitialised)
229 Point p1 =
new Point(p1X, p1Y);
230 Point p2 =
new Point(p2X, p2Y);
231 Point p3 =
new Point(p3X, p3Y);
232 currentPath.AddBezier(currentPoint.ToPointF(1), p1.ToPointF(1), p2.ToPointF(1), p3.ToPointF(1));
237 currentPath.StartFigure();
238 currentPoint =
new Point(p3X, p3Y);
239 figureInitialised =
true;
243 public void DrawRasterImage(
int sourceX,
int sourceY,
int sourceWidth,
int sourceHeight,
double destinationX,
double destinationY,
double destinationWidth,
double destinationHeight, RasterImage image)
251 ReadOnlySpan<byte> data =
new ReadOnlySpan<byte>((
void*)image.ImageDataAddress, image.Width * image.Height * 4);
252 sourceImage = SixLabors.ImageSharp.Image.LoadPixelData<SixLabors.ImageSharp.PixelFormats.Rgba32>(data, image.Width, image.Height);
256 ReadOnlySpan<byte> data =
new ReadOnlySpan<byte>((
void*)image.ImageDataAddress, image.Width * image.Height * 3);
257 sourceImage = SixLabors.ImageSharp.Image.LoadPixelData<SixLabors.ImageSharp.PixelFormats.Rgb24>(data, image.Width, image.Height);
261 DrawImage(sourceX, sourceY, sourceWidth, sourceHeight, destinationX, destinationY, destinationWidth, destinationHeight, image.Interpolate, sourceImage);
264 internal void DrawImage(
int sourceX,
int sourceY,
int sourceWidth,
int sourceHeight,
double destinationX,
double destinationY,
double destinationWidth,
double destinationHeight,
bool interpolate, Image sourceImage)
266 sourceImage.Mutate(x => x.Crop(
new SixLabors.ImageSharp.Rectangle(sourceX, sourceY, sourceWidth, sourceHeight)));
268 Point targetPoint = _transform.Multiply(
new Point(destinationX, destinationY));
269 Point targetPointX = _transform.Multiply(
new Point(destinationX + destinationWidth, destinationY));
270 Point targetPointY = _transform.Multiply(
new Point(destinationX, destinationY + destinationHeight));
271 Point targetPointXY = _transform.Multiply(
new Point(destinationX + destinationWidth, destinationY + destinationHeight));
273 double minX = Math.Min(Math.Min(targetPoint.X, targetPointX.X), Math.Min(targetPointY.X, targetPointXY.X));
274 double minY = Math.Min(Math.Min(targetPoint.Y, targetPointX.Y), Math.Min(targetPointY.Y, targetPointXY.Y));
276 double maxX = Math.Max(Math.Max(targetPoint.X, targetPointX.X), Math.Max(targetPointY.X, targetPointXY.X));
277 double maxY = Math.Max(Math.Max(targetPoint.Y, targetPointX.Y), Math.Max(targetPointY.Y, targetPointXY.Y));
279 double[,] currTransform = MatrixUtils.Multiply(MatrixUtils.Translate(_transform, destinationX, destinationY), MatrixUtils.Scale(MatrixUtils.Identity, destinationWidth / sourceImage.Width, destinationHeight / sourceImage.Height));
281 Point origin = currTransform.Multiply(
new Point(0, 0));
283 double[,] translation = MatrixUtils.Translate(MatrixUtils.Identity, -minX, -minY);
285 double[,] centeredTransform = MatrixUtils.Multiply(translation, currTransform);
287 SixLabors.ImageSharp.Processing.Processors.Transforms.IResampler resampler;
291 resampler = KnownResamplers.Bicubic;
295 resampler = KnownResamplers.NearestNeighbor;
298 sourceImage.Mutate(x => x.Transform(
new SixLabors.ImageSharp.Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), centeredTransform.ToMatrix(),
new SixLabors.ImageSharp.Size((
int)Math.Round(maxX - minX), (
int)Math.Round(maxY - minY)), resampler));
300 if (this.CurrentClip ==
null)
302 this.Image.Mutate(x => x.DrawImage(sourceImage,
new SixLabors.ImageSharp.Point((
int)Math.Round(minX), (
int)Math.Round(minY)), 1));
306 this.ClipBuffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
307 this.ClipBuffer.Mutate(x => x.DrawImage(sourceImage,
new SixLabors.ImageSharp.Point((
int)Math.Round(minX), (
int)Math.Round(minY)), 1));
309 GraphicsOptions opt =
new GraphicsOptions() { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.SrcIn };
311 this.Buffer.Dispose();
313 this.Buffer = this.CurrentClip.Clone(x => x.DrawImage(
this.ClipBuffer, opt));
315 this.Image.Mutate(x => x.DrawImage(
this.Buffer, 1));
321 IPath path = this.currentPath.Build();
323 if (this.CurrentClip ==
null)
325 this.Image.Mutate(x => x.Fill(
new DrawingOptions() { Transform = _transform.ToMatrix() }, this.FillStyle.ToImageSharpBrush(_transform), path));
329 this.ClipBuffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
330 this.ClipBuffer.Mutate(x => x.Fill(
new DrawingOptions() { Transform = _transform.ToMatrix() }, this.FillStyle.ToImageSharpBrush(_transform), path));
332 GraphicsOptions opt =
new GraphicsOptions() { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.SrcIn };
334 this.Buffer.Dispose();
336 this.Buffer = this.CurrentClip.Clone(x => x.DrawImage(
this.ClipBuffer, opt));
338 this.Image.Mutate(x => x.DrawImage(
this.Buffer, 1));
341 this.currentPath =
new PathBuilder();
342 this.figureInitialised =
false;
345 public void FillText(
string text,
double x,
double y)
347 PathText(text, x, y);
351 private void PathText(
string text,
double x,
double y)
353 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
355 GraphicsPath textPath =
new GraphicsPath().AddText(x, y, text, Font, TextBaseline);
357 for (
int j = 0; j < textPath.Segments.Count; j++)
359 switch (textPath.Segments[j].Type)
362 this.MoveTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
365 this.LineTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
368 this.CubicBezierTo(textPath.Segments[j].Points[0].X, textPath.Segments[j].Points[0].Y, textPath.Segments[j].Points[1].X, textPath.Segments[j].Points[1].Y, textPath.Segments[j].Points[2].X, textPath.Segments[j].Points[2].Y);
377 public void LineTo(
double x,
double y)
379 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
381 if (figureInitialised)
383 Point newPoint =
new Point(x, y);
384 currentPath.AddLine(currentPoint.ToPointF(1), newPoint.ToPointF(1));
385 currentPoint = newPoint;
389 currentPath.StartFigure();
390 currentPoint =
new Point(x, y);
391 figureInitialised =
true;
395 public void MoveTo(
double x,
double y)
397 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
399 currentPath.StartFigure();
400 currentPoint =
new Point(x, y);
401 figureInitialised =
true;
404 public void Rectangle(
double x0,
double y0,
double width,
double height)
407 LineTo(x0 + width, y0);
408 LineTo(x0 + width, y0 + height);
409 LineTo(x0, y0 + height);
413 public void Restore()
415 _transform = states.Pop();
417 Image newClip = clips.Pop();
418 if (CurrentClip != newClip)
420 CurrentClip.Dispose();
423 CurrentClip = newClip;
426 public void Rotate(
double angle)
428 Utils.CoerceNaNAndInfinityToZero(ref angle);
430 _transform = MatrixUtils.Rotate(_transform, angle);
432 currentPath =
new PathBuilder();
433 figureInitialised =
false;
438 states.Push((
double[,])_transform.Clone());
439 clips.Push(CurrentClip);
442 public void Scale(
double x,
double y)
444 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
446 _transform = MatrixUtils.Scale(_transform, x, y);
448 currentPath =
new PathBuilder();
449 figureInitialised =
false;
452 public void SetClippingPath()
454 IPath path = this.currentPath.Build();
458 DrawingOptions opt =
new DrawingOptions() { Transform = _transform.ToMatrix() };
460 if (this.CurrentClip ==
null)
462 newClip =
new Image<SixLabors.ImageSharp.PixelFormats.Rgba32>(IntWidth, IntHeight);
463 newClip.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
464 newClip.Mutate(x => x.Fill(opt, Color.FromRgb(0, 0, 0), path));
468 this.Buffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
469 this.Buffer.Mutate(x => x.Fill(opt, Color.FromRgb(0, 0, 0), path));
471 newClip = this.CurrentClip.Clone(x => x.DrawImage(
this.Buffer,
new GraphicsOptions() { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.SrcIn }));
473 disposables.Add(this.CurrentClip);
476 this.CurrentClip = newClip;
478 this.currentPath =
new PathBuilder();
479 this.figureInitialised =
false;
482 public void SetFillStyle((
int r,
int g,
int b,
double a) style)
484 this.FillStyle = Colour.FromRgba(style);
487 public void SetFillStyle(Brush style)
489 this.FillStyle = style;
492 public void SetLineDash(LineDash dash)
497 public void SetStrokeStyle((
int r,
int g,
int b,
double a) style)
499 this.StrokeStyle = Colour.FromRgba(style);
502 public void SetStrokeStyle(Brush style)
504 this.StrokeStyle = style;
509 if (this.CurrentClip ==
null)
511 this.Image.Mutate(x => x.Fill(
new DrawingOptions() { Transform = _transform.ToMatrix() }, this.StrokeStyle.ToImageSharpBrush(_transform), this.currentPath.Build().GenerateOutline((
float)(
this.LineWidth),
this.Dash.ToImageSharpDash(
this.LineWidth),
false,
this.LineJoin.ToImageSharpJoint(),
this.LineCap.ToImageSharpCap())));
515 this.ClipBuffer.Mutate(x => x.Clear(Color.FromRgba(0, 0, 0, 0)));
516 this.ClipBuffer.Mutate(x => x.Fill(
new DrawingOptions() { Transform = _transform.ToMatrix() }, this.StrokeStyle.ToImageSharpBrush(_transform), this.currentPath.Build().GenerateOutline((
float)(
this.LineWidth),
this.Dash.ToImageSharpDash(
this.LineWidth),
false,
this.LineJoin.ToImageSharpJoint(),
this.LineCap.ToImageSharpCap())));
518 GraphicsOptions opt =
new GraphicsOptions() { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.SrcIn };
520 this.Buffer.Dispose();
522 this.Buffer = this.CurrentClip.Clone(x => x.DrawImage(
this.ClipBuffer, opt));
524 this.Image.Mutate(x => x.DrawImage(
this.Buffer, 1));
528 this.currentPath =
new PathBuilder();
529 this.figureInitialised =
false;
532 public void StrokeText(
string text,
double x,
double y)
534 PathText(text, x, y);
538 public void Transform(
double a,
double b,
double c,
double d,
double e,
double f)
540 Utils.CoerceNaNAndInfinityToZero(ref a, ref b, ref c, ref d, ref e, ref f);
542 double[,] transfMatrix =
new double[3, 3] { { a, c, e }, { b, d, f }, { 0, 0, 1 } };
543 _transform = MatrixUtils.Multiply(_transform, transfMatrix);
545 currentPath =
new PathBuilder();
546 figureInitialised =
false;
549 public void Translate(
double x,
double y)
551 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
553 _transform = MatrixUtils.Translate(_transform, x, y);
555 currentPath =
new PathBuilder();
556 figureInitialised =
false;
559 public void DrawFilteredGraphics(Graphics graphics,
IFilter filter)
561 double scale = this.scaleFactor * Math.Sqrt(MatrixUtils.Determinant(_transform));
563 Rectangle bounds = graphics.GetBounds();
567 if (bounds.Size.Width > 0 && bounds.Size.Height > 0)
569 bool rasterisationNeeded =
true;
573 rasterisationNeeded =
false;
575 Page pag =
new Page(1, 1);
576 pag.Graphics.DrawGraphics(0, 0, graphics);
577 pag.Crop(bounds.Location, bounds.Size);
579 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img = ImageSharpContextInterpreter.SaveAsImage(pag, scale);
580 img.Mutate(x => x.GaussianBlur((
float)(gauss.StandardDeviation * scale)));
582 DrawImage(0, 0, img.Width, img.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height,
true, img);
588 rasterisationNeeded =
false;
590 Page pag =
new Page(1, 1);
591 pag.Graphics.DrawGraphics(0, 0, graphics);
592 pag.Crop(bounds.Location, bounds.Size);
594 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img = ImageSharpContextInterpreter.SaveAsImage(pag, scale);
596 img.Mutate(x => x.Filter(
new ColorMatrix((
float)cmf.ColourMatrix.R1, (
float)cmf.ColourMatrix.G1, (
float)cmf.ColourMatrix.B1, (
float)cmf.ColourMatrix.A1, (
float)cmf.ColourMatrix.R2, (
float)cmf.ColourMatrix.G2, (
float)cmf.ColourMatrix.B2, (
float)cmf.ColourMatrix.A2, (
float)cmf.ColourMatrix.R3, (
float)cmf.ColourMatrix.G3, (
float)cmf.ColourMatrix.B3, (
float)cmf.ColourMatrix.A3, (
float)cmf.ColourMatrix.R4, (
float)cmf.ColourMatrix.G4, (
float)cmf.ColourMatrix.B4, (
float)cmf.ColourMatrix.A4, (
float)cmf.ColourMatrix.R5, (
float)cmf.ColourMatrix.G5, (
float)cmf.ColourMatrix.B5, (
float)cmf.ColourMatrix.A5)));
598 DrawImage(0, 0, img.Width, img.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height,
true, img);
604 rasterisationNeeded =
false;
606 Page pag =
new Page(1, 1);
607 pag.Graphics.DrawGraphics(0, 0, graphics);
608 pag.Crop(bounds.Location, bounds.Size);
610 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img = ImageSharpContextInterpreter.SaveAsImage(pag, scale);
612 img.Mutate(x => x.BoxBlur((
int)Math.Round(bbf.BoxRadius * scale)));
614 DrawImage(0, 0, img.Width, img.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height,
true, img);
620 bool allSupported =
true;
622 foreach (
IFilter filter2
in comp.Filters)
626 allSupported =
false;
633 rasterisationNeeded =
false;
635 Page pag =
new Page(1, 1);
636 pag.Graphics.DrawGraphics(0, 0, graphics);
637 pag.Crop(bounds.Location, bounds.Size);
639 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img = ImageSharpContextInterpreter.SaveAsImage(pag, scale);
641 foreach (
IFilter filter2
in comp.Filters)
645 img.Mutate(x => x.GaussianBlur((
float)(gauss2.StandardDeviation * scale)));
649 img.Mutate(x => x.Filter(
new ColorMatrix((
float)cmf2.ColourMatrix.R1, (
float)cmf2.ColourMatrix.G1, (
float)cmf2.ColourMatrix.B1, (
float)cmf2.ColourMatrix.A1, (
float)cmf2.ColourMatrix.R2, (
float)cmf2.ColourMatrix.G2, (
float)cmf2.ColourMatrix.B2, (
float)cmf2.ColourMatrix.A2, (
float)cmf2.ColourMatrix.R3, (
float)cmf2.ColourMatrix.G3, (
float)cmf2.ColourMatrix.B3, (
float)cmf2.ColourMatrix.A3, (
float)cmf2.ColourMatrix.R4, (
float)cmf2.ColourMatrix.G4, (
float)cmf2.ColourMatrix.B4, (
float)cmf2.ColourMatrix.A4, (
float)cmf2.ColourMatrix.R5, (
float)cmf2.ColourMatrix.G5, (
float)cmf2.ColourMatrix.B5, (
float)cmf2.ColourMatrix.A5)));
653 img.Mutate(x => x.BoxBlur((
int)Math.Round(bbf2.BoxRadius * scale)));
658 DrawImage(0, 0, img.Width, img.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height,
true, img);
664 if (rasterisationNeeded)
666 RasterImage rasterised = ImageSharpContextInterpreter.Rasterise(graphics, bounds, scale,
true);
667 RasterImage filtered =
null;
671 filterWithRastParam.RasteriseParameter(ImageSharpContextInterpreter.Rasterise, scale);
676 filtered = locInvFilter.Filter(rasterised, scale);
680 filtered = filterWithLoc.Filter(rasterised, bounds, scale);
683 if (filtered !=
null)
685 rasterised.Dispose();
687 DrawRasterImage(0, 0, filtered.Width, filtered.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height, filtered);
694 internal static class Utils
696 public static PointF ToPointF(
this Point pt,
double scaleFactor)
698 return new PointF((
float)(pt.X * scaleFactor), (
float)(pt.Y * scaleFactor));
701 public static void CoerceNaNAndInfinityToZero(ref
double val)
703 if (
double.IsNaN(val) ||
double.IsInfinity(val) || val ==
double.MinValue || val ==
double.MaxValue)
709 public static void CoerceNaNAndInfinityToZero(ref
double val1, ref
double val2)
711 if (
double.IsNaN(val1) ||
double.IsInfinity(val1) || val1 ==
double.MinValue || val1 ==
double.MaxValue)
716 if (
double.IsNaN(val2) ||
double.IsInfinity(val2) || val2 ==
double.MinValue || val2 ==
double.MaxValue)
722 public static void CoerceNaNAndInfinityToZero(ref
double val1, ref
double val2, ref
double val3, ref
double val4)
724 if (
double.IsNaN(val1) ||
double.IsInfinity(val1) || val1 ==
double.MinValue || val1 ==
double.MaxValue)
729 if (
double.IsNaN(val2) ||
double.IsInfinity(val2) || val2 ==
double.MinValue || val2 ==
double.MaxValue)
734 if (
double.IsNaN(val3) ||
double.IsInfinity(val3) || val3 ==
double.MinValue || val3 ==
double.MaxValue)
739 if (
double.IsNaN(val4) ||
double.IsInfinity(val4) || val4 ==
double.MinValue || val4 ==
double.MaxValue)
745 public static void CoerceNaNAndInfinityToZero(ref
double val1, ref
double val2, ref
double val3, ref
double val4, ref
double val5, ref
double val6)
747 if (
double.IsNaN(val1) ||
double.IsInfinity(val1) || val1 ==
double.MinValue || val1 ==
double.MaxValue)
752 if (
double.IsNaN(val2) ||
double.IsInfinity(val2) || val2 ==
double.MinValue || val2 ==
double.MaxValue)
757 if (
double.IsNaN(val3) ||
double.IsInfinity(val3) || val3 ==
double.MinValue || val3 ==
double.MaxValue)
762 if (
double.IsNaN(val4) ||
double.IsInfinity(val4) || val4 ==
double.MinValue || val4 ==
double.MaxValue)
767 if (
double.IsNaN(val5) ||
double.IsInfinity(val5) || val5 ==
double.MinValue || val5 ==
double.MaxValue)
772 if (
double.IsNaN(val6) ||
double.IsInfinity(val6) || val6 ==
double.MinValue || val6 ==
double.MaxValue)
778 public static Color ToImageSharpColor(
this Colour colour)
780 return Color.FromRgba((
byte)(colour.R * 255), (
byte)(colour.G * 255), (
byte)(colour.B * 255), (
byte)(colour.A * 255));
783 public static IBrush ToImageSharpBrush(
this Brush brush,
double[,] transform)
785 if (brush is SolidColourBrush solid)
787 return new SixLabors.ImageSharp.Drawing.Processing.SolidBrush(solid.Colour.ToImageSharpColor());
789 else if (brush is LinearGradientBrush linear)
791 ColorStop[] colorStops =
new ColorStop[linear.GradientStops.Count];
793 for (
int i = 0; i < linear.GradientStops.Count; i++)
795 colorStops[i] =
new ColorStop((
float)linear.GradientStops[i].Offset, linear.GradientStops[i].Colour.ToImageSharpColor());
798 return new SixLabors.ImageSharp.Drawing.Processing.LinearGradientBrush(transform.Multiply(linear.StartPoint).ToPointF(1), transform.Multiply(linear.EndPoint).ToPointF(1), GradientRepetitionMode.None, colorStops);
800 else if (brush is RadialGradientBrush radial)
802 ColorStop[] colorStops =
new ColorStop[radial.GradientStops.Count];
804 for (
int i = 0; i < radial.GradientStops.Count; i++)
806 colorStops[i] =
new ColorStop((
float)radial.GradientStops[i].Offset, radial.GradientStops[i].Colour.ToImageSharpColor());
809 return new RadialGradientBrushSVGStyle(radial.Centre, radial.FocalPoint, radial.Radius, transform, GradientRepetitionMode.None, colorStops);
813 throw new NotImplementedException();
817 public static float[] ToImageSharpDash(
this LineDash dash,
double lineThickness)
819 if (dash.UnitsOn > 0 || dash.UnitsOff > 0)
823 (float)(dash.UnitsOn / lineThickness),
824 (float)(dash.UnitsOff / lineThickness)
829 return new float[] { 1, 0 };
833 public static JointStyle ToImageSharpJoint(
this LineJoins join)
838 return JointStyle.Round;
840 return JointStyle.Miter;
842 return JointStyle.Square;
844 return JointStyle.Miter;
848 public static EndCapStyle ToImageSharpCap(
this LineCaps cap)
853 return EndCapStyle.Square;
855 return EndCapStyle.Round;
857 return EndCapStyle.Butt;
859 throw new NotImplementedException();
863 internal class RadialGradientBrushSVGStyle : SixLabors.ImageSharp.Drawing.Processing.GradientBrush
865 private readonly Point Centre;
866 private readonly Point FocalPoint;
867 private readonly
double Radius;
868 private readonly
double[,] InverseTransform;
870 public RadialGradientBrushSVGStyle(Point centre, Point focalPoint,
double radius,
double[,] transform, GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) : base(repetitionMode, colorStops)
873 FocalPoint = focalPoint;
875 InverseTransform = MatrixUtils.Invert(transform);
878 public override BrushApplicator<TPixel> CreateApplicator<TPixel>(Configuration configuration, GraphicsOptions options, ImageFrame<TPixel> source, RectangleF region)
880 return new RadialGradientBrushPDFStyleApplicator<TPixel>(configuration, options, source, Centre, FocalPoint, Radius, InverseTransform, ColorStops, RepetitionMode);
883 private class RadialGradientBrushPDFStyleApplicator<TPixel> : GradientBrushApplicator<TPixel> where TPixel : unmanaged, SixLabors.ImageSharp.
PixelFormats.IPixel<TPixel>
885 private readonly Point Centre;
886 private readonly Point FocalPoint;
887 private readonly
double Radius;
888 private readonly
double[,] InverseTransform;
890 private readonly
double a;
892 public RadialGradientBrushPDFStyleApplicator(
893 Configuration configuration,
894 GraphicsOptions options,
895 ImageFrame<TPixel> target,
896 Point centre, Point focalPoint,
double radius,
double[,] inverseTransform,
897 ColorStop[] colorStops,
898 GradientRepetitionMode repetitionMode)
899 : base(configuration, options, target, colorStops, repetitionMode)
901 this.Centre = centre;
902 this.FocalPoint = focalPoint;
903 this.Radius = radius;
904 this.InverseTransform = inverseTransform;
906 a = (centre.X - focalPoint.X) * (centre.X - focalPoint.X) + (centre.Y - focalPoint.Y) * (centre.Y - focalPoint.Y) - radius * radius;
909 Random rnd =
new Random();
911 protected override float PositionOnGradient(
float x,
float y)
913 Point realPoint = InverseTransform.Multiply(
new Point(x, y));
915 double c = (realPoint.X - FocalPoint.X) * (realPoint.X - FocalPoint.X) + (realPoint.Y - FocalPoint.Y) * (realPoint.Y - FocalPoint.Y);
917 double halfB = -((realPoint.X - FocalPoint.X) * (Centre.X - FocalPoint.X) + (realPoint.Y - FocalPoint.Y) * (Centre.Y - FocalPoint.Y));
919 double sqrt = Math.Sqrt(halfB * halfB - a * c);
921 double tbr1 = (-halfB + sqrt) / a;
922 double tbr2 = (-halfB - sqrt) / a;
924 if (tbr1 >= 0 && tbr2 < 0)
928 else if (tbr1 < 0 && tbr2 >= 0)
932 else if (tbr1 < 0 && tbr2 < 0)
938 return (
float)Math.Min(tbr1, tbr2);
943 public override void Apply(Span<float> scanline,
int x,
int y)
945 base.Apply(scanline, x, y);
1009 public static Image<SixLabors.ImageSharp.PixelFormats.Rgba32>
SaveAsImage(
this Page page,
double scale = 1)
1013 ctx.DisposeAllExceptImage();
1029 switch (outputFormat)
1032 image.SaveAsBmp(imageStream);
1035 image.SaveAsGif(imageStream);
1038 image.SaveAsJpeg(imageStream);
1041 image.SaveAsPbm(imageStream);
1044 image.SaveAsPng(imageStream);
1047 image.SaveAsTga(imageStream);
1050 image.SaveAsTiff(imageStream);
1053 image.SaveAsWebp(imageStream);
1070 internal UnknownFormatException(
string format) : base(
"The extension " + format +
" does not correspond to any known file format!")
1087 if (outputFormat.HasValue)
1089 actualOutputFormat = outputFormat.Value;
1093 string extension = System.IO.Path.GetExtension(fileName).ToLower();
1138 using (FileStream imageStream =
new FileStream(fileName, FileMode.Create))
1140 SaveAsImage(page, imageStream, actualOutputFormat, scale);
1155 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img =
SaveAsImage(pag, scale);
1157 int stride = img.Width * 4;
1158 int size = stride * img.Height;
1160 IntPtr tbr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
1161 GC.AddMemoryPressure(size);
1163 IntPtr pointer = tbr;
1167 for (
int y = 0; y < img.Height; y++)
1169 Memory<SixLabors.ImageSharp.PixelFormats.Rgba32> row = img.DangerousGetPixelRowMemory(y);
1171 Span<SixLabors.ImageSharp.PixelFormats.Rgba32> newRow =
new Span<SixLabors.ImageSharp.PixelFormats.Rgba32>(pointer.ToPointer(), row.Length);
1172 row.Span.CopyTo(newRow);
1174 pointer = IntPtr.Add(pointer, stride);
1179 height = img.Height;
1198 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img =
SaveAsImage(pag, scale);
1200 int stride = img.Width * 4;
1201 int size = stride * img.Height;
1203 byte[] tbr =
new byte[size];
1207 for (
int y = 0; y < img.Height; y++)
1209 Memory<SixLabors.ImageSharp.PixelFormats.Rgba32> row = img.DangerousGetPixelRowMemory(y);
1211 Span<byte> bytes = System.Runtime.InteropServices.MemoryMarshal.Cast<SixLabors.ImageSharp.PixelFormats.Rgba32,
byte>(row.Span);
1212 Span<byte> newRow =
new Span<byte>(tbr, y * stride, stride);
1213 bytes.CopyTo(newRow);
1218 height = img.Height;
1239 Image<SixLabors.ImageSharp.PixelFormats.Rgba32> img =
SaveAsImage(pag, scale);
1241 int stride = img.Width * 4;
1242 int size = stride * img.Height;
1244 IntPtr tbr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
1245 GC.AddMemoryPressure(size);
1247 IntPtr pointer = tbr;
1251 for (
int y = 0; y < img.Height; y++)
1253 Memory<SixLabors.ImageSharp.PixelFormats.Rgba32> row = img.DangerousGetPixelRowMemory(y);
1255 Span<SixLabors.ImageSharp.PixelFormats.Rgba32> newRow =
new Span<SixLabors.ImageSharp.PixelFormats.Rgba32>(pointer.ToPointer(), row.Length);
1256 row.Span.CopyTo(newRow);
1258 pointer = IntPtr.Add(pointer, stride);
1262 int width = img.Width;
1263 int height = img.Height;
1269 return new RasterImage(ref disp, width, height,
true, interpolate);