Skip to content

Commit 281c743

Browse files
DerpDerp
authored andcommitted
Updated to dotnet 6.0, replacing resampling filter from bicubic to lanczos3 and added an option for custom filename formatting
1 parent 01cff10 commit 281c743

18 files changed

+93
-54
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
################################################################################
2+
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
3+
################################################################################
4+
5+
/.vs
6+
/obj
7+
/Tiler.csproj.user
8+
/bin/Debug
9+
/bin/Release

BuildBinaries.bat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ ren binaries\Tiler Tiler-Linux-arm64
2828
dotnet publish -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true /p:PublishTrimmed=true -p:PublishReadyToRun=false -o binaries
2929
del binaries\Tiler-OSX-x64
3030
ren binaries\Tiler Tiler-OSX-x64
31+
dotnet publish -c Release -r osx-arm64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true /p:PublishTrimmed=true -p:PublishReadyToRun=false -o binaries
32+
del binaries\Tiler-OSX-arm64
33+
ren binaries\Tiler Tiler-OSX-arm64

Program.cs

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
using System.Text.RegularExpressions;
25
using System.Linq;
36
using SixLabors.ImageSharp;
47
using SixLabors.ImageSharp.PixelFormats;
@@ -12,67 +15,84 @@ class Program
1215
{
1316
static void Main(string[] args)
1417
{
15-
var commandline =
16-
args.Select((x, i) => new { index = i, option = x })
17-
.Where(x => x.option.StartsWith("-")).Select(x => new
18+
var commandline = args.
19+
Select((x, i) => new { index = i, option = x }).
20+
Where(x => x.option.StartsWith("-")).
21+
Select(x => new
1822
{
19-
option = x.option.Substring(1),
20-
arguments = args.Skip(x.index + 1)
21-
.TakeWhile(m => !m.StartsWith("-")).ToArray()
22-
})
23-
.ToDictionary(x => x.option.ToLower(), x => x.arguments);
23+
option = x.option[1..],
24+
arguments = args.
25+
Skip(x.index + 1).
26+
TakeWhile(m => !m.StartsWith("-")).ToArray()
27+
}).
28+
ToDictionary(x => x.option.ToLower(), x => x.arguments);
2429

2530
if (args.Length == 0 || commandline.ContainsKey("h") || commandline.ContainsKey("help"))
2631
{
27-
Console.WriteLine("Tiler XYZ tile maker v1.0");
28-
Console.WriteLine("Usage:");
29-
Console.WriteLine("Tiler.exe image.jpg");
30-
Console.WriteLine(" or");
31-
Console.WriteLine("Tiler.exe -input image.jpg -output output_folder -size 256 -zoom 6");
32+
Console.Write("Tiler XYZ tile maker v1.0\n" +
33+
"Usage:\n" +
34+
"Tiler.exe image.jpg\n" +
35+
" or\n" +
36+
$"Tiler.exe -input image.jpg -size 256 -zoom 6 -filename \"image.jpg-tiles{Path.DirectorySeparatorChar}{{z}}_{{x}}_{{y}}.png\"\n");
3237
return;
3338
}
3439

3540
var sourceFile = commandline.Count == 0 && args.Length > 0 ? args[0] : commandline["input"].First();
3641
int tile_size = commandline.ContainsKey("size") ? int.Parse(commandline["size"].First()) : 256;
37-
var Directory = commandline.ContainsKey("output") ? commandline["output"].First() : new System.IO.FileInfo(sourceFile).Directory.FullName + System.IO.Path.DirectorySeparatorChar + string.Format("{0}-tiles", commandline.ContainsKey("stdin") ? "STDIN" : new System.IO.FileInfo(sourceFile).Name);
38-
System.IO.DirectoryInfo OutputDirectory = System.IO.Directory.CreateDirectory(Directory);
39-
var OriginalSource = commandline.ContainsKey("stdin") ? Image<Rgba32>.Load(Console.OpenStandardInput()) : Image<Rgba32>.Load(sourceFile);
42+
using var OriginalSource = commandline.ContainsKey("stdin") ? Image.Load(Console.OpenStandardInput()) : Image.Load(sourceFile);
43+
4044
int maxzoom = commandline.ContainsKey("zoom") ? int.Parse(commandline["zoom"].First()) :
4145
((int)Math.Ceiling(Math.Log(Math.Max(OriginalSource.Width, OriginalSource.Height) / (double)tile_size)) + 1); // auto detect max level
4246

43-
var Sampler = new BicubicResampler();
44-
4547
for (int zoom = 0; zoom <= maxzoom; zoom++)
4648
{
4749
int tiles = (int)Math.Pow(2.0, zoom);
4850
int size = tiles * tile_size;
49-
using (var Scaled = new Image<Rgba32>(size, size))
51+
using var Scaled = new Image<Rgba32>(size, size);
52+
Scaled.Mutate(i => i.Clear(Color.Transparent)); // Clear the background to transparent
53+
54+
int width, height;
55+
56+
if (OriginalSource.Width > OriginalSource.Height) // Calculate the right scale ratio
57+
(width, height) = (Scaled.Width, Scaled.Width * OriginalSource.Height / OriginalSource.Width);
58+
else
59+
(height, width) = (Scaled.Height, Scaled.Height * OriginalSource.Width / OriginalSource.Height);
60+
61+
62+
var Sampler = // Use Lanczos3 for reducing, Bicubic for upscaling
63+
width < OriginalSource.Width || height < OriginalSource.Height
64+
? (IResampler)new LanczosResampler(3.0f)
65+
: (IResampler)new BicubicResampler();
66+
67+
// Scale the entire image
68+
using (Image<Rgba32> Rescaled = new(OriginalSource.Width, OriginalSource.Height))
5069
{
51-
Scaled.Mutate(i => i.Clear(Color.Transparent));
52-
{
53-
54-
if (OriginalSource.Width > OriginalSource.Height)
55-
{
56-
int width = Scaled.Width;
57-
int height = width * OriginalSource.Height / OriginalSource.Width;
58-
Scaled.Mutate(x => x.DrawImage(OriginalSource.Clone(x => x.Resize(width, height, Sampler)), new Point(0, (Scaled.Height - height) / 2), new GraphicsOptions()));
59-
}
60-
else
61-
{
62-
int height = Scaled.Height;
63-
int width = height * OriginalSource.Width / OriginalSource.Height;
64-
Scaled.Mutate(x => x.DrawImage(OriginalSource.Clone(x => x.Resize(width, height, Sampler)), new Point((Scaled.Width - width) / 2, 0), new GraphicsOptions()));
65-
}
66-
}
67-
Enumerable.Range(0, tiles).SelectMany(x => Enumerable.Range(0, tiles).Select(y => new { x, y })).AsParallel().ForAll((c) =>
68-
{
69-
using var Tile = new Image<Rgba32>(tile_size, tile_size);
70-
lock (Scaled)
71-
Tile.Mutate(x => x.DrawImage(Scaled.Clone(x => x.Crop(new Rectangle(c.x * tile_size, c.y * tile_size, tile_size, tile_size))), new Point(0, 0), new GraphicsOptions()));
72-
var l = OutputDirectory.FullName + System.IO.Path.DirectorySeparatorChar + zoom.ToString() + "_" + c.x.ToString() + "_" + c.y.ToString() + ".png";
73-
Tile.Save(l);
74-
});
70+
Rescaled.Mutate(x => x.DrawImage(OriginalSource, new Point(0, 0), 1.0f).Resize(width, height, Sampler));
71+
Scaled.Mutate(x => x.DrawImage(Rescaled, new Point((Scaled.Width - width) / 2, (Scaled.Height - height) / 2), new GraphicsOptions()));
7572
}
73+
74+
// Take tiles from it
75+
Enumerable.Range(0, tiles).SelectMany(x => Enumerable.Range(0, tiles).Select(y => new { x, y })).AsParallel().ForAll((c) =>
76+
{
77+
using var Tile = new Image<Rgba32>(tile_size, tile_size);
78+
lock (Scaled)
79+
Tile.Mutate(x => x.DrawImage(Scaled.Clone(x => x.Crop(new Rectangle(c.x * tile_size, c.y * tile_size, tile_size, tile_size))), new Point(0, 0), new GraphicsOptions()));
80+
81+
var Variables = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase) { { "z", zoom }, { "x", c.x }, { "y", c.y } };
82+
83+
var Filename = Regex.Replace(
84+
commandline.ContainsKey("filename") && commandline["filename"].Length > 0
85+
? commandline["filename"].First()
86+
: $"{sourceFile}-tiles{Path.DirectorySeparatorChar}{{z}}_{{x}}_{{y}}.png",
87+
@"{(?<var>[^}]+)}", new MatchEvaluator(m => $"{Variables[m.Groups["var"].Value]}"));
88+
89+
var OutputDirectory = Path.GetDirectoryName(Filename);
90+
91+
if (OutputDirectory != null && !Directory.Exists(OutputDirectory))
92+
Directory.CreateDirectory(OutputDirectory);
93+
94+
Tile.Save(Filename);
95+
});
7696
}
7797
}
7898
}

Readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ Tiler
33

44
## XYZ Tile creator
55

6-
Tiler is an XYZ tile creator written in C# (dotnet core 3.0 preview) that works on Windows, Linux, OSX.
6+
Tiler is an XYZ tile creator written in C# (dotnet 6.0) that works on Windows, Linux, OSX.
77
It can quickly process large images into XYZ tiles that can be used with leaflet/slippy/etc.
88

9-
Because the output is PNG format, a transparent background is used to center the image into a square frame and subdivided. If you wish to use a different output format or naming convention create your own script to resave the format and name. The default naming format is *{zoom}\_{x}\_{y}.png*.
9+
A transparent background is used to center the image into a square frame and subdivided for any output format that supports transparency. Consider pre-sizing your input image to a square size (to your preferred alignment) if you do not want your image centered. If you wish to use a different (unsupported) output format or naming convention create your own script to resave the format and name. The default naming format is *{z}\_{x}\_{y}.png*.
1010

1111
Tile size defaults to 256 pixels squared (matching the default of leaflet and slippy). 2<sup>*n*</sup> tiles squared are created for each zoom depth *n*.
1212

@@ -27,11 +27,11 @@ Compiled binary (portable) versions are available in the **binaries/** directory
2727

2828
This usage of Tiler requires only the file name for the command line arguments - This style looks different so it can handle drag-and-drop in an explorer context
2929
```
30-
Tiler.exe <image file>
30+
Tiler.exe <input image file>
3131
```
3232
**or explicit option overrides can be set**
3333
```
34-
Tiler.exe -input <image file> [-size <tile pixel size>] [-zoom <max zoom level>] [-output <output directory no slash>]
34+
Tiler.exe -input <input image file> [-size <tile pixel size>] [-zoom <max zoom level>] [-filename "output/{z}_{x}_{y}.png"]
3535
```
3636
* -zoom determines maximum zoom level/layers; if blank it will determine the correct maximum level
3737
* -size determines the tile size in pixels, defaults to 256 (square)

0 commit comments

Comments
 (0)