mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
6.6 KiB
148 lines
6.6 KiB
// Copyright (c) 2026 Siegfried Pammer |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
// Tiny helper that produces all the binary fixture files (PNG / BMP / JPG / GIF / ICO / CUR / |
|
// .resources / .bin) into the fixtures/ directory. Text fixtures (XML / XSD / XSLT / XAML) are |
|
// checked in as-is. Run with `dotnet run --project Generate.csproj` from this directory. |
|
// |
|
// Each image format renders an obvious recognizable shape (coloured square with a circle and a |
|
// diagonal stripe) at 64×64 so manual ILSpy testing can eyeball that the rendering pipeline |
|
// is decoding the bytes — not just rendering a blank tile. |
|
|
|
using System; |
|
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; |
|
|
|
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()); |
|
|
|
// 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. |
|
File.WriteAllBytes(Path.Combine(dir, "favicon.ico"), BuildIcoOrCur(isCursor: false, sizes: new[] { 16, 32, 48 })); |
|
File.WriteAllBytes(Path.Combine(dir, "pointer.cur"), BuildIcoOrCur(isCursor: true, sizes: new[] { 32 })); |
|
|
|
// .resources file with a mix of strings and other-typed entries — the .resources DataGrid view |
|
// splits these into two tables. |
|
var ms = new MemoryStream(); |
|
using (var rw = new ResourceWriter(ms)) |
|
{ |
|
rw.AddResource("greeting", "Hello, world"); |
|
rw.AddResource("city", "Linz"); |
|
rw.AddResource("year", 2026); |
|
rw.AddResource("ratio", 1.61803); |
|
rw.AddResource("flag", true); |
|
} |
|
File.WriteAllBytes(Path.Combine(dir, "Strings.resources"), ms.ToArray()); |
|
|
|
// Unknown extension — should fall through to the generic ResourceTreeNode. |
|
File.WriteAllBytes(Path.Combine(dir, "blob.bin"), new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE }); |
|
|
|
Console.WriteLine($"Wrote fixtures into {dir}"); |
|
|
|
// --- helpers --- |
|
|
|
static void WriteImage(string path, IImageEncoder encoder, int size = 64) |
|
{ |
|
using var img = RenderShape(size); |
|
using var fs = File.Create(path); |
|
img.Save(fs, encoder); |
|
} |
|
|
|
static Image<Rgba32> RenderShape(int size) |
|
{ |
|
var img = new Image<Rgba32>(size, size, new Rgba32(0xFF, 0xF6, 0xE5)); |
|
img.Mutate(ctx => { |
|
// 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)); |
|
// Red diagonal stripe. |
|
ctx.DrawLine(new Rgba32(0xCC, 0x33, 0x33), |
|
Math.Max(2, size / 16f), |
|
new PointF(0, size), |
|
new PointF(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)); |
|
}); |
|
return img; |
|
} |
|
|
|
// Builds an ICO/CUR file containing one PNG-encoded entry per requested size. The on-disk |
|
// layout: ICONDIR + ICONDIRENTRY[*] + concatenated frame bytes; only byte 2 (image type) |
|
// distinguishes ICO (1) from CUR (2). Hotspot for cursors is centred at (size/2, size/2). |
|
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()); |
|
} |
|
var fileMs = new MemoryStream(); |
|
var w = new BinaryWriter(fileMs); |
|
w.Write((ushort)0); // reserved |
|
w.Write((ushort)(isCursor ? 2 : 1)); // type: 1=ICO, 2=CUR |
|
w.Write((ushort)sizes.Length); // image count |
|
int dirSize = 6 + 16 * sizes.Length; |
|
int offset = dirSize; |
|
for (int i = 0; i < sizes.Length; i++) |
|
{ |
|
var sz = sizes[i]; |
|
var dim = (byte)(sz >= 256 ? 0 : sz); |
|
w.Write(dim); // width |
|
w.Write(dim); // height |
|
w.Write((byte)0); // palette count |
|
w.Write((byte)0); // reserved |
|
if (isCursor) |
|
{ |
|
w.Write((ushort)(sz / 2)); // hotspot X |
|
w.Write((ushort)(sz / 2)); // hotspot Y |
|
} |
|
else |
|
{ |
|
w.Write((ushort)1); // colour planes |
|
w.Write((ushort)32); // bits per pixel |
|
} |
|
w.Write((uint)frames[i].Length); // bytes in frame |
|
w.Write((uint)offset); // offset to frame |
|
offset += frames[i].Length; |
|
} |
|
foreach (var frame in frames) |
|
w.Write(frame); |
|
return fileMs.ToArray(); |
|
}
|
|
|