Replace ImageSharp with Magick.NET in the fixture generator
ImageSharp 4.x / ImageSharp.Drawing 3.x add a build-time license check that fails Release builds without a Six Labors license key, and the older 3.x / 2.x line is the last one without it. Magick.NET is Apache 2.0, bundles the native ImageMagick library for win/linux/osx, and covers every encoder the fixtures need (PNG/BMP/JPG/GIF), so the generator moves to it instead of carrying a license key. The checked-in binary fixtures are regenerated with the new encoders; the rendered shape (square, circle, diagonal stripe, border) is the same, only the encoded bytes differ. Assisted-by: Claude:claude-fable-5-1:Claude Code
@@ -29,24 +29,17 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Resources;
|
||||
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.Formats;
|
||||
using SixLabors.ImageSharp.Formats.Bmp;
|
||||
using SixLabors.ImageSharp.Formats.Gif;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using ImageMagick;
|
||||
using ImageMagick.Drawing;
|
||||
|
||||
var dir = Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "fixtures");
|
||||
dir = Path.GetFullPath(dir);
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
WriteImage(Path.Combine(dir, "logo.png"), new PngEncoder());
|
||||
WriteImage(Path.Combine(dir, "logo.bmp"), new BmpEncoder());
|
||||
WriteImage(Path.Combine(dir, "logo.jpg"), new JpegEncoder { Quality = 92 });
|
||||
WriteImage(Path.Combine(dir, "logo.gif"), new GifEncoder());
|
||||
WriteImage(Path.Combine(dir, "logo.png"), MagickFormat.Png);
|
||||
WriteImage(Path.Combine(dir, "logo.bmp"), MagickFormat.Bmp);
|
||||
WriteImage(Path.Combine(dir, "logo.jpg"), MagickFormat.Jpeg, quality: 92);
|
||||
WriteImage(Path.Combine(dir, "logo.gif"), MagickFormat.Gif);
|
||||
|
||||
// Multi-frame .ico: 16x16, 32x32, 48x48, each PNG-encoded inside the ICO container so Avalonia
|
||||
// can decode the largest frame for preview while a savvy viewer can pick any.
|
||||
@@ -73,29 +66,35 @@ Console.WriteLine($"Wrote fixtures into {dir}");
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
static void WriteImage(string path, IImageEncoder encoder, int size = 64)
|
||||
static void WriteImage(string path, MagickFormat format, int size = 64, uint quality = 0)
|
||||
{
|
||||
using var img = RenderShape(size);
|
||||
using var fs = File.Create(path);
|
||||
img.Save(fs, encoder);
|
||||
if (quality > 0)
|
||||
img.Quality = quality;
|
||||
img.Write(path, format);
|
||||
}
|
||||
|
||||
static Image<Rgba32> RenderShape(int size)
|
||||
static MagickImage RenderShape(int size)
|
||||
{
|
||||
var img = new Image<Rgba32>(size, size, new Rgba32(0xFF, 0xF6, 0xE5));
|
||||
img.Mutate(ctx => {
|
||||
var img = new MagickImage(new MagickColor("#FFF6E5"), (uint)size, (uint)size);
|
||||
float radius = size * 0.35f;
|
||||
// Drawables is a single fluent command list: stroke/fill settings persist across the
|
||||
// primitives that follow, so each shape resets the ones it does not want.
|
||||
new Drawables()
|
||||
// Filled blue circle in the centre.
|
||||
ctx.Fill(new Rgba32(0x33, 0x99, 0xCC),
|
||||
new SixLabors.ImageSharp.Drawing.EllipsePolygon(size / 2f, size / 2f, size * 0.35f));
|
||||
.FillColor(new MagickColor("#3399CC"))
|
||||
.StrokeColor(MagickColors.None)
|
||||
.Ellipse(size / 2.0, size / 2.0, radius, radius, 0, 360)
|
||||
// Red diagonal stripe.
|
||||
ctx.DrawLine(new Rgba32(0xCC, 0x33, 0x33),
|
||||
Math.Max(2, size / 16f),
|
||||
new PointF(0, size),
|
||||
new PointF(size, 0));
|
||||
.FillColor(MagickColors.None)
|
||||
.StrokeColor(new MagickColor("#CC3333"))
|
||||
.StrokeWidth(Math.Max(2, size / 16.0))
|
||||
.Line(0, size, size, 0)
|
||||
// 1px dark border so the bounds are obvious against any background.
|
||||
ctx.Draw(new Rgba32(0x33, 0x33, 0x33), 1,
|
||||
new RectangleF(0, 0, size - 1, size - 1));
|
||||
});
|
||||
.StrokeColor(new MagickColor("#333333"))
|
||||
.StrokeWidth(1)
|
||||
.Rectangle(0, 0, size - 1, size - 1)
|
||||
.Draw(img);
|
||||
return img;
|
||||
}
|
||||
|
||||
@@ -105,13 +104,10 @@ static Image<Rgba32> RenderShape(int size)
|
||||
static byte[] BuildIcoOrCur(bool isCursor, int[] sizes)
|
||||
{
|
||||
var frames = new List<byte[]>(sizes.Length);
|
||||
var pngEncoder = new PngEncoder();
|
||||
foreach (var sz in sizes)
|
||||
{
|
||||
using var img = RenderShape(sz);
|
||||
using var ms = new MemoryStream();
|
||||
img.Save(ms, pngEncoder);
|
||||
frames.Add(ms.ToArray());
|
||||
frames.Add(img.ToByteArray(MagickFormat.Png));
|
||||
}
|
||||
var fileMs = new MemoryStream();
|
||||
var w = new BinaryWriter(fileMs);
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
<RootNamespace>TestFixtures.Generate</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- This generator runs once to (re)build the binary fixtures and isn't part of the
|
||||
solution's CPM scope; pin SkiaSharp inline so the parent Directory.Packages.props
|
||||
solution's CPM scope; pin the image library inline so the parent Directory.Packages.props
|
||||
doesn't need to learn about a one-shot tool. -->
|
||||
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- ImageSharp covers every encoder we need (PNG/JPG/BMP/GIF) cross-platform; SkiaSharp's
|
||||
encoder list doesn't include BMP/GIF. -->
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" />
|
||||
<!-- Magick.NET (Apache 2.0, bundles the ImageMagick native library for win/linux/osx) covers every
|
||||
encoder we need (PNG/JPG/BMP/GIF). SkiaSharp's encoder list doesn't include BMP/GIF. -->
|
||||
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.17.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 718 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 777 B |
|
Before Width: | Height: | Size: 796 B After Width: | Height: | Size: 556 B |
@@ -2,26 +2,19 @@
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net10.0": {
|
||||
"SixLabors.ImageSharp": {
|
||||
"Magick.NET-Q8-AnyCPU": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.1.11, )",
|
||||
"resolved": "3.1.11",
|
||||
"contentHash": "JfPLyigLthuE50yi6tMt7Amrenr/fA31t2CvJyhy/kQmfulIBAqo5T/YFUSRHtuYPXRSaUHygFeh6Qd933EoSw=="
|
||||
},
|
||||
"SixLabors.ImageSharp.Drawing": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.1.4, )",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "p4VwtAABggDUhUS5zXldCZHVGfjJl76+SrBHY4biNQ8+890igFK6RL87qIv9GqvEjMcYOar1sPkf2iMQ6uq9/g==",
|
||||
"requested": "[14.17.2, )",
|
||||
"resolved": "14.17.2",
|
||||
"contentHash": "AIvpBH4fvP3/NUwvWsVj9SxAA6hrU4p5/RNfColyrDn4jipl7loR6y4ZgYLXfBQgjmSEc7m2D2DbkWMnZt+brg==",
|
||||
"dependencies": {
|
||||
"SixLabors.Fonts": "2.0.4",
|
||||
"SixLabors.ImageSharp": "3.1.5"
|
||||
"Magick.NET.Core": "14.17.2"
|
||||
}
|
||||
},
|
||||
"SixLabors.Fonts": {
|
||||
"Magick.NET.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.4",
|
||||
"contentHash": "4+NKz8W36injp98lmmM07ncp08HAK8c6FZz8vLoKxRPfJeEVWpBHlLYEbQa5rcqKKYqxUv/RVCrb8XcPhfMKUQ=="
|
||||
"resolved": "14.17.2",
|
||||
"contentHash": "KKvR6eBUcfgkZpfHfN/UKRcK/FjHW6NXXoRRf5HJdhFk5bYLEEFuKu6op0zfNo6Msah51Yw42p599pz3A0CDQQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||