From 9db74a70268a855542fb043f98e605653674d228 Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Mon, 7 Aug 2023 15:06:48 +0900 Subject: [PATCH 01/10] Adding OpenKh.Tools.Kh2FontImageEditor --- OpenKh.Kh2/Contextes/FontContext.cs | 262 ++++++++++++------ OpenKh.Tools.Kh2FontImageEditor/App.xaml | 8 + OpenKh.Tools.Kh2FontImageEditor/App.xaml.cs | 40 +++ .../AssemblyInfo.cs | 10 + .../DependencyInjection/UseExtension.cs | 22 ++ .../OpenKh.Tools.Kh2FontImageEditor.csproj | 24 ++ .../Usecases/ShowErrorMessageUsecase.cs | 18 ++ .../UserControls/ImagerTablet.xaml | 21 ++ .../UserControls/ImagerTablet.xaml.cs | 112 ++++++++ .../ViewModels/MainWindowVM.cs | 240 ++++++++++++++++ .../Views/MainWindow.xaml | 50 ++++ .../Views/MainWindow.xaml.cs | 32 +++ OpenKh.sln | 11 + 13 files changed, 772 insertions(+), 78 deletions(-) create mode 100644 OpenKh.Tools.Kh2FontImageEditor/App.xaml create mode 100644 OpenKh.Tools.Kh2FontImageEditor/App.xaml.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/AssemblyInfo.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/DependencyInjection/UseExtension.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj create mode 100644 OpenKh.Tools.Kh2FontImageEditor/Usecases/ShowErrorMessageUsecase.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml create mode 100644 OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/ViewModels/MainWindowVM.cs create mode 100644 OpenKh.Tools.Kh2FontImageEditor/Views/MainWindow.xaml create mode 100644 OpenKh.Tools.Kh2FontImageEditor/Views/MainWindow.xaml.cs diff --git a/OpenKh.Kh2/Contextes/FontContext.cs b/OpenKh.Kh2/Contextes/FontContext.cs index f360b8e94..6e98a3e81 100644 --- a/OpenKh.Kh2/Contextes/FontContext.cs +++ b/OpenKh.Kh2/Contextes/FontContext.cs @@ -1,5 +1,7 @@ +using OpenKh.Common; using OpenKh.Imaging; using OpenKh.Kh2.SystemData; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -52,83 +54,187 @@ public void Read(IEnumerable entries) } } - private void ReadFont(Bar.Entry entry, ref IImageRead image1, ref IImageRead image2, ref byte[] spacing) - { - switch (entry.Type) - { - case Bar.EntryType.List: - spacing = ReadSpacing(entry); - break; - case Bar.EntryType.RawBitmap: - entry.Stream.Position = 0; - image1 = ReadImagePalette1(entry); - entry.Stream.Position = 0; - image2 = ReadImagePalette2(entry); - break; - } - } - - private void ReadIcon(Bar.Entry entry, ref IImageRead image, ref byte[] spacing, int width, int height) - { - switch (entry.Type) - { - case Bar.EntryType.List: - spacing = ReadSpacing(entry); - break; - case Bar.EntryType.RawBitmap: - entry.Stream.Position = 0; - image = ReadImage8bit(entry, width, height); - break; - } - } - - private static byte[] ReadSpacing(Bar.Entry entry) - { - return new BinaryReader(entry.Stream).ReadBytes((int)entry.Stream.Length); - } - - private static IImageRead ReadImage8bit(Bar.Entry entry, int width, int height) - { - return RawBitmap.Read8bit(entry.Stream, width, height); - } - - private static IImageRead ReadImagePalette1(Bar.Entry entry) - { - DeductImageSize((int)entry.Stream.Length, out var width, out var height); - return RawBitmap.Read4bitPalette1(entry.Stream, width, height); - } - - private static IImageRead ReadImagePalette2(Bar.Entry entry) - { - DeductImageSize((int)entry.Stream.Length, out var width, out var height); - return RawBitmap.Read4bitPalette2(entry.Stream, width, height); - } - - private static bool DeductImageSize(int rawLength, out int width, out int height) - { - if (rawLength < 128 * 1024) - { - width = 512; - height = 256; - } - else if (rawLength < 256 * 1024) - { - width = 512; - height = 512; - } - else if (rawLength < 512 * 1024) - { - width = 512; - height = 1024; - } - else - { - width = 0; - height = 0; - return false; - } - - return true; - } + public IEnumerable WriteFontImage() + { + var list = new Bar(); + + list.Add(new Bar.Entry { Name = "sys", Type = Bar.EntryType.RawBitmap, Stream = WriteFont(imageSystem, imageSystem2), }); + list.Add(new Bar.Entry { Name = "evt", Type = Bar.EntryType.RawBitmap, Stream = WriteFont(imageEvent, imageEvent2), }); + list.Add(new Bar.Entry { Name = "icon", Type = Bar.EntryType.RawBitmap, Stream = WriteIconBitmap(imageIcon), }); + + return list.AsReadOnly(); + } + + private void ReadFont(Bar.Entry entry, ref IImageRead image1, ref IImageRead image2, ref byte[] spacing) + { + switch (entry.Type) + { + case Bar.EntryType.List: + spacing = ReadSpacing(entry); + break; + case Bar.EntryType.RawBitmap: + entry.Stream.Position = 0; + image1 = ReadImagePalette1(entry); + entry.Stream.Position = 0; + image2 = ReadImagePalette2(entry); + break; + } + } + + private MemoryStream WriteFont(IImageRead image1, IImageRead image2) + { + if (false + || image1 == null + || image2 == null + || image1.Size.Width != 512 + || image1.Size != image2.Size + ) + { + throw new Exception("Font image width expected 512. And two font images expect to be same size."); + } + + var source1 = image1.ToBgra32(); + var source2 = image2.ToBgra32(); + + var height = image1.Size.Height; + var pixels = new byte[256 * height]; + + for (int y = 0; y < height; y++) + { + var readOfs = 512 * 4 * y; + var writeOfs = 256 * y; + + for (int x = 0; x < 512; x += 2, readOfs += 8, writeOfs += 1) + { + var plane1FirstBlue = source1[readOfs]; + var plane1FirstGreen = source1[readOfs + 1]; + var plane1FirstRed = source1[readOfs + 2]; + var plane1SecondBlue = source1[readOfs + 4]; + var plane1SecondGreen = source1[readOfs + 5]; + var plane1SecondRed = source1[readOfs + 6]; + var plane2FirstBlue = source2[readOfs]; + var plane2FirstGreen = source2[readOfs + 1]; + var plane2FirstRed = source2[readOfs + 2]; + var plane2SecondBlue = source2[readOfs + 4]; + var plane2SecondGreen = source2[readOfs + 5]; + var plane2SecondRed = source2[readOfs + 6]; + // At 4bpp bitmap, lo and hi are swapped. lo first, hi second. + pixels[writeOfs] = MakePixel( + CutTo2Bit(plane1FirstBlue, plane1FirstGreen, plane1FirstRed) | (CutTo2Bit(plane2FirstBlue, plane2FirstGreen, plane2FirstRed) << 2), + CutTo2Bit(plane1SecondBlue, plane1SecondGreen, plane1SecondRed) | (CutTo2Bit(plane2SecondBlue, plane2SecondGreen, plane2SecondRed) << 2) + ); + } + } + + return new MemoryStream(pixels); + } + + private static byte MakePixel(int lo, int hi) + { + return (byte)((hi << 4) | (lo & 15)); + } + + private static int CutTo2Bit(byte blue, byte green, byte red) + { + var intensity = (blue + (int)green + red) / 3; + + if (0xe0 <= intensity) + { + return 3; + } + else if (0xb0 <= intensity) + { + return 2; + } + else if (0x90 <= intensity) + { + return 1; + } + else + { + return 0; + } + } + + private void ReadIcon(Bar.Entry entry, ref IImageRead image, ref byte[] spacing, int width, int height) + { + switch (entry.Type) + { + case Bar.EntryType.List: + spacing = ReadSpacing(entry); + break; + case Bar.EntryType.RawBitmap: + entry.Stream.Position = 0; + image = ReadImage8bit(entry, width, height); + break; + } + } + + private MemoryStream WriteIconBitmap(IImageRead image) + { + if (false + || image == null + || image.PixelFormat != PixelFormat.Indexed8 + || image.Size.Width != 256 + || image.Size.Height != 160 + ) + { + throw new Exception("Icon image expects 256 x 160 (Indexed8)"); + } + + var stream = new MemoryStream(256 * 160 + 4 * 256); + stream.Write(image.GetData()); + stream.Write(image.GetClut()); + return stream; + } + + private static byte[] ReadSpacing(Bar.Entry entry) + { + return new BinaryReader(entry.Stream).ReadBytes((int)entry.Stream.Length); + } + + private static IImageRead ReadImage8bit(Bar.Entry entry, int width, int height) + { + return RawBitmap.Read8bit(entry.Stream, width, height); + } + + private static IImageRead ReadImagePalette1(Bar.Entry entry) + { + DeductImageSize((int)entry.Stream.Length, out var width, out var height); + return RawBitmap.Read4bitPalette1(entry.Stream, width, height); + } + + private static IImageRead ReadImagePalette2(Bar.Entry entry) + { + DeductImageSize((int)entry.Stream.Length, out var width, out var height); + return RawBitmap.Read4bitPalette2(entry.Stream, width, height); + } + + private static bool DeductImageSize(int rawLength, out int width, out int height) + { + if (rawLength < 128 * 1024) + { + width = 512; + height = 256; + } + else if (rawLength < 256 * 1024) + { + width = 512; + height = 512; + } + else if (rawLength < 512 * 1024) + { + width = 512; + height = 1024; + } + else + { + width = 0; + height = 0; + return false; + } + + return true; + } } } diff --git a/OpenKh.Tools.Kh2FontImageEditor/App.xaml b/OpenKh.Tools.Kh2FontImageEditor/App.xaml new file mode 100644 index 000000000..7ca406e47 --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/OpenKh.Tools.Kh2FontImageEditor/App.xaml.cs b/OpenKh.Tools.Kh2FontImageEditor/App.xaml.cs new file mode 100644 index 000000000..a827141a2 --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/App.xaml.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.DependencyInjection; +using OpenKh.Tools.Kh2FontImageEditor.DependencyInjection; +using OpenKh.Tools.Kh2FontImageEditor.Views; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace OpenKh.Tools.Kh2FontImageEditor +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + private ServiceProvider? _container; + + protected override void OnExit(ExitEventArgs e) + { + _container?.Dispose(); + + base.OnExit(e); + } + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + var builder = new ServiceCollection(); + builder.UseKh2FontImageEditor(); + _container = builder.BuildServiceProvider(); + ShutdownMode = ShutdownMode.OnMainWindowClose; + MainWindow = _container.GetRequiredService(); + MainWindow.Show(); + } + } +} diff --git a/OpenKh.Tools.Kh2FontImageEditor/AssemblyInfo.cs b/OpenKh.Tools.Kh2FontImageEditor/AssemblyInfo.cs new file mode 100644 index 000000000..74087a1fd --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/OpenKh.Tools.Kh2FontImageEditor/DependencyInjection/UseExtension.cs b/OpenKh.Tools.Kh2FontImageEditor/DependencyInjection/UseExtension.cs new file mode 100644 index 000000000..23fc44c25 --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/DependencyInjection/UseExtension.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.DependencyInjection; +using OpenKh.Tools.Kh2FontImageEditor.Usecases; +using OpenKh.Tools.Kh2FontImageEditor.ViewModels; +using OpenKh.Tools.Kh2FontImageEditor.Views; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenKh.Tools.Kh2FontImageEditor.DependencyInjection +{ + internal static class UseExtension + { + public static void UseKh2FontImageEditor(this ServiceCollection container) + { + container.AddSingleton(); + container.AddSingleton(); + container.AddSingleton(); + } + } +} diff --git a/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj b/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj new file mode 100644 index 000000000..0fcb271e1 --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj @@ -0,0 +1,24 @@ + + + + WinExe + net6.0-windows + enable + true + Font image editor + Font image editor - OpenKH + + + + + + + + + + + + + + + diff --git a/OpenKh.Tools.Kh2FontImageEditor/Usecases/ShowErrorMessageUsecase.cs b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ShowErrorMessageUsecase.cs new file mode 100644 index 000000000..71efc27be --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ShowErrorMessageUsecase.cs @@ -0,0 +1,18 @@ +using OpenKh.Tools.Common.Wpf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenKh.Tools.Kh2FontImageEditor.Usecases +{ + public class ShowErrorMessageUsecase + { + public void Show(Exception ex) + { + new MessageDialog($"There is a critical error:\n\n{ex}") + .ShowDialog(); + } + } +} diff --git a/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml b/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml new file mode 100644 index 000000000..e6f64fb2f --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml @@ -0,0 +1,21 @@ + + + + + diff --git a/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindow.xaml.cs b/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindow.xaml.cs new file mode 100644 index 000000000..b8f8661ab --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindow.xaml.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace OpenKh.Tools.Kh2FontImageEditor.Views +{ + /// + /// Interaction logic for SpacingWindow.xaml + /// + public partial class SpacingWindow : Window + { + public SpacingWindow( + SpacingWindowVM vm + ) + { + InitializeComponent(); + DataContext = vm; + + _image.MouseDown += (object sender, MouseButtonEventArgs e) => + { + var spacingDelta = (e.LeftButton == MouseButtonState.Pressed) ? -1 + : (e.RightButton == MouseButtonState.Pressed) ? +1 + : 0; + var point = e.GetPosition(_image); + + vm.State?.AdjustSpacing((int)point.X, (int)point.Y, spacingDelta); + }; + } + } +} diff --git a/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindowVM.cs b/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindowVM.cs new file mode 100644 index 000000000..f8b19d64b --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/Views/SpacingWindowVM.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using System.Windows.Media; +using Xe.Tools; + +namespace OpenKh.Tools.Kh2FontImageEditor.Views +{ + public class SpacingWindowVM : BaseNotifyPropertyChanged + { + public record StateModel( + ImageSource Image, + int Width, + int Height, + ICommand SaveCommand, + Action AdjustSpacing + ); + + #region State property + private StateModel? _state; + public StateModel? State + { + get => _state; + set + { + if (_state != value) + { + _state = value; + OnPropertyChanged(); + } + } + } + #endregion + + } +} From b46e4dd53804563b0c9d44f575acf25b1b184bd4 Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Sat, 18 Nov 2023 23:09:08 +0900 Subject: [PATCH 05/10] Spacing also icon sprite --- .../ApplySpacingToImageReadUsecase.cs | 102 +++++++++++++++--- .../Views/MainWindowVM.cs | 24 +++-- 2 files changed, 104 insertions(+), 22 deletions(-) diff --git a/OpenKh.Tools.Kh2FontImageEditor/Usecases/ApplySpacingToImageReadUsecase.cs b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ApplySpacingToImageReadUsecase.cs index 51b8d4f70..bafdfd7bd 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/Usecases/ApplySpacingToImageReadUsecase.cs +++ b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ApplySpacingToImageReadUsecase.cs @@ -1,6 +1,8 @@ +using OpenKh.Engine.Extensions; using OpenKh.Imaging; using OpenKh.Tools.Kh2FontImageEditor.Helpers; using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -19,7 +21,7 @@ public ApplySpacingToImageReadUsecase( _copyArrayUsecase = copyArrayUsecase; } - public IImageRead Apply(IImageRead image, Func getSpacing, GlyphCell[] glyphCells) + public IImageRead ApplyToIndexed4(IImageRead image, Func getSpacing, GlyphCell[] glyphCells) { if (image.PixelFormat != PixelFormat.Indexed4) { @@ -27,16 +29,16 @@ public IImageRead Apply(IImageRead image, Func getSpacing, GlyphCell[ } var data = _copyArrayUsecase.Copy(image.GetData()); - var clut = new byte[] + var clut = new byte[] // RR GG BB AA { - 0, 0, 0, 255, - 85, 0, 0, 255, - 170, 0, 0, 255, - 255, 0, 0, 255, - 0, 0, 255, 255, - 85, 85, 85, 255, - 170, 170, 170, 255, - 255, 255, 255, 255, + 0, 0, 0, 255, // Unprotected glyph intensity 0 (black) + 85, 0, 0, 255, // Unprotected glyph intensity 1 (RED) --> glyph overflow + 170, 0, 0, 255, // Unprotected glyph intensity 2 (RED) --> glyph overflow + 255, 0, 0, 255, // Unprotected glyph intensity 3 (RED) --> glyph overflow + 0, 0, 255, 255, // Protected glyph intensity 0 (blue) + 85, 85, 85, 255, // Protected glyph intensity 1 (gray) + 170, 170, 170, 255, // Protected glyph intensity 2 (gray) + 255, 255, 255, 255, // Protected glyph intensity 3 (white) 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, @@ -47,12 +49,12 @@ public IImageRead Apply(IImageRead image, Func getSpacing, GlyphCell[ 0, 0, 0, 255, }; - var width = image.Size.Width; - var height = image.Size.Height; + var bitmapWidth = image.Size.Width; + var bitmapHeight = image.Size.Height; - var stride = width / 2; + var stride = bitmapWidth / 2; - void ApplyBitOrRect(int fromX, int fromY, byte width, int height) + void ProtectRect(int fromX, int fromY, byte width, int height) { for (int y = 0; y < height; y++) { @@ -80,7 +82,7 @@ void ApplyBitOrRect(int fromX, int fromY, byte width, int height) { var glyph = glyphCells[idx]; - ApplyBitOrRect( + ProtectRect( glyph.Cell.X, glyph.Cell.Y, getSpacing(glyph.SpacingIndex), @@ -88,8 +90,76 @@ void ApplyBitOrRect(int fromX, int fromY, byte width, int height) ); } - return new SimpleImage(width, height, PixelFormat.Indexed4, data, PixelFormat.Rgba8888, clut); + return new SimpleImage(bitmapWidth, bitmapHeight, PixelFormat.Indexed4, data, PixelFormat.Rgba8888, clut); } + public IImageRead ApplyToFullColored(IImageRead image, Func getSpacing, GlyphCell[] glyphCells) + { + var data = _copyArrayUsecase.Copy(image.ToBgra32()); + + var bitmapWidth = image.Size.Width; + var bitmapHeight = image.Size.Height; + + var protect = new BitArray(bitmapWidth * bitmapHeight); + + { + void ProtectRect(int fromX, int fromY, byte width, int height) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + protect[bitmapWidth * (fromY + y) + fromX + x] = true; + } + } + } + + for (int idx = 0, max = glyphCells.Length; idx < max; idx++) + { + var glyph = glyphCells[idx]; + + ProtectRect( + glyph.Cell.X, + glyph.Cell.Y, + getSpacing(glyph.SpacingIndex), + glyph.Cell.Height + ); + } + } + + var ofs = 0; + + for (int index = 0; index < protect.Length; index++, ofs += 4) + { + if (protect[index]) + { + if (data[ofs + 0] == 0 && data[ofs + 1] == 0 && data[ofs + 2] == 0) + { + // make pixel blue as valid pixel inside cell + data[ofs + 0] = 255; + data[ofs + 1] = 0; + data[ofs + 2] = 0; + data[ofs + 3] = 255; + } + } + else + { + // make pixel red as invalid pixel outside cell + + var b = data[ofs + 0]; + var g = data[ofs + 1]; + var r = data[ofs + 2]; + + var intensity = Math.Max(Math.Max(b, g), r); + + data[ofs + 0] = 0; + data[ofs + 1] = 0; + data[ofs + 2] = intensity; + data[ofs + 3] = 255; + } + } + + return new SimpleImage(bitmapWidth, bitmapHeight, PixelFormat.Rgba8888, data, PixelFormat.Rgba8888); + } } } diff --git a/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs b/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs index 8b6270caa..2ca7a6453 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs +++ b/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs @@ -392,12 +392,24 @@ void ApplyFontMetrics( ImageSource CreateImage() { - return _applySpacingToImageReadUsecase.Apply( - image, - index => (index < spacing.Length) ? spacing[index] : (byte)0, - glyphCells - ) - .GetBimapSource(); + if (imager.IsFontImage) + { + return _applySpacingToImageReadUsecase.ApplyToIndexed4( + image, + index => (index < spacing.Length) ? spacing[index] : (byte)0, + glyphCells + ) + .GetBimapSource(); + } + else + { + return _applySpacingToImageReadUsecase.ApplyToFullColored( + image, + index => (index < spacing.Length) ? spacing[index] : (byte)0, + glyphCells + ) + .GetBimapSource(); + } } Action refreshImage = () => { }; From 9cb0c825ed3e23f62da25f40dfd4d473cf08fefc Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Sat, 18 Nov 2023 23:14:52 +0900 Subject: [PATCH 06/10] Update spacing.png --- docs/kh2/file/type/images/spacing.png | Bin 836 -> 1130 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/kh2/file/type/images/spacing.png b/docs/kh2/file/type/images/spacing.png index 8ad3e1e82be1df5f7f18915f64ddb35b93c1e1c8..bc90099298119eb71ca34f2b703da79929101bc9 100644 GIT binary patch delta 1084 zcmX@Y_KIVIBnKM<14EzSr?nFmHR=yBFz_8>6xHx*U|?YW@9E+gQW5udj-mf;2a)6P zr&=Vmrk1U=TE-(9@+w3#a#x$q%uj-IChFvP7^Iw@)bm6+<&@AL&J9;qGArME(YE49 z!wdo2*~K13@82AGU$bw)_sKf*Stnlb|Gxcx^~M!8+zltsT34nO9b)R#aoG{H@`UJt z`j;k@58HCS!MM*np_+0eB6pw zCYuQE-zv4vV~hHQ`3}?U69tUS9x~~9Y*D)qzHL7Hyzt4d!!IS(6ci|gub-lOagu|| zzBul4{kIor$b3zZIAz+DEwq+{r`=X{U)*ZOfLy0&Lx}?wvyuWHOmg97a-PvQeg0lK zmwzpe8LQY7CH#*rzf|a&^y;*0z1zP9$M=7DUe59TlhL!!HWh+{-=x-s!JB)jRf9PkCKZ zR#qnV)1D5tEjBynD6dsJGbz2B ziB)N9{94`>_KwWv9ox#*o#@u^Q@FJ2>Mh^E9T^#0Tnab$+`6?>MSqb(qasiB>SN}P z!jJD2gbD9b(_bXf_)uiZElbsXoEbgMhA*ThZ>oRlDxzdCr*G=c_^cY?3lfjl&gK53 z#3j(NH81j`Z^nU-4)<@lUYc}R^bGeI$@?u?!ZRsJ^AMu<^!?d)Ahx`tq5q z7J9DdJbh$?Lr%_opSUpA`4`q0zuVp!2~v^WX??qDfuUQLlemFaKqA+O7S>4&Klo!U?lPMkk}?676AYfNelF{r5}E)<7yw)V delta 788 zcmV+v1MB?i2*d`E83+OZ0004VW%`jJD1XBM01m?e$8V@)0008nNkl>NC z6o6sQ#esZ{y#lj0NKfsWA~Ou_wj9PQOaF^)8Cy@VNLr!4B2P-jUjUipFnm}ITzeDv zAn|pjHE;u0Rs%P1Wi@aES5^ZzaAh@c16Ni9H*jS&@Zs>Q)VR>sYTyq9^K~)7t$zmm z=TYC1*#Ng<@b!9yTR6Dyj{FGe*!8S!hf*@y{{(zL`COZVJOx_V=7AZ)Ee+gve7m1o z?5HlMHJLfw!oZterP%S-nI*Vofp@$LF*s>HF^}OE1zvi)&O5z!#mFRYXdc5Y3G8|M z)d6-q-P!5|OJZ6$*wfjkQzgbEF@Lo&DiZw2s}KW{m`Y4b1V8o$#Aqa@8^J9Eyye{y zqm0P}~{k!w|g44yWP;g5C2fWJ-zfChSI~2h! z037sATSn~|Vpes*%@0<1-6pU$BW6V%+}vRANZRlY!{rRswY_mOxS7E^?|=C6or(9h zZU;9nSmU+tRuf{j#*5)*1&6(0ZMXi5jjl*B+??Q$7pUa#_dcM6f}0V%Jf8ilM=9Ca zOJcbBzR$;)y?(PFr1!CWO; z@v<0MtQc-mu)RjCW22X)CyErqO$Z)O8l-v>r1v79fsX~3*y4Q>K33%P^)K5&(RY~X4GIR}484P1A_@D~ZsgvKL; S-(vs(002ovPDHLkU;%>gGI^K) From 9bda7b94af0d0aba39a6cb73832dea2ff79b783e Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Sat, 18 Nov 2023 23:21:48 +0900 Subject: [PATCH 07/10] Some fixes --- OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs | 3 +-- docs/kh2/file/type/fontinfo.md | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs b/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs index 2ca7a6453..f67371446 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs +++ b/OpenKh.Tools.Kh2FontImageEditor/Views/MainWindowVM.cs @@ -174,7 +174,7 @@ void ApplyFontMetrics( _lastOpenedFontImage = fontImageFile; - switch (MessageBox.Show("FontEuropean?", "", MessageBoxButton.YesNoCancel)) + switch (MessageBox.Show("FontEuropean?\n\n- Answer yes to use FontEuropean\n- Answer no to use FontJapanese", "", MessageBoxButton.YesNoCancel)) { case MessageBoxResult.Yes: ApplyFontMetrics( @@ -196,7 +196,6 @@ void ApplyFontMetrics( Constants.FontJapaneseEventWidth, Constants.FontJapaneseEventHeight ); - break; } }, OpenFontImageFilters); diff --git a/docs/kh2/file/type/fontinfo.md b/docs/kh2/file/type/fontinfo.md index 856120022..bd69f6315 100644 --- a/docs/kh2/file/type/fontinfo.md +++ b/docs/kh2/file/type/fontinfo.md @@ -10,8 +10,14 @@ Tag | Type | Purpose The entire of spacing data is array of `byte`. -Each character unit size is 24x24 or such (size is square). +Each character unit size is fixed. -The spacing declares each character size in pixel unit spreading from bottom left origin point to top right end. +The spacing declares each character width in pixel unit spreading from left to right. ![](images/spacing.png) + +The ordering of spacing data is a little bit difficult. +Basically it advances left to right, and then top to bottom. +However if there is a 2 blocks in vertical direction, +at first, the index advances from front plane to back plane, +and then advance to front plane of next block. From dbd645daf7c8c612ea458c121bf93c2e1c4e5694 Mon Sep 17 00:00:00 2001 From: kenjiuno Date: Sun, 19 Nov 2023 00:43:04 +0900 Subject: [PATCH 08/10] Adjust some --- .../Helpers/CommandWithCanExecute.cs | 42 ++ .../OpenKh.Tools.Kh2FontImageEditor.csproj | 1 + .../Usecases/ConvertFontDataUsecase.cs | 23 + .../UserControls/ImagerTablet.xaml | 5 +- .../Views/MainWindow.xaml | 6 +- .../Views/MainWindowVM.cs | 413 +++++++++++------- 6 files changed, 318 insertions(+), 172 deletions(-) create mode 100644 OpenKh.Tools.Kh2FontImageEditor/Helpers/CommandWithCanExecute.cs diff --git a/OpenKh.Tools.Kh2FontImageEditor/Helpers/CommandWithCanExecute.cs b/OpenKh.Tools.Kh2FontImageEditor/Helpers/CommandWithCanExecute.cs new file mode 100644 index 000000000..80aa9cc85 --- /dev/null +++ b/OpenKh.Tools.Kh2FontImageEditor/Helpers/CommandWithCanExecute.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace OpenKh.Tools.Kh2FontImageEditor.Helpers +{ + public class CommandWithCanExecute : ICommand + { + public CommandWithCanExecute(Action action) + { + _action = action; + } + + private bool _isExecutable; + private readonly Action _action; + + public bool IsExecutable + { + get => _isExecutable; + set + { + _isExecutable = value; + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } + + public event EventHandler? CanExecuteChanged; + + public bool CanExecute(object? parameter) + { + return _isExecutable; + } + + public void Execute(object? parameter) + { + _action(parameter); + } + } +} diff --git a/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj b/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj index 0fcb271e1..26059efed 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj +++ b/OpenKh.Tools.Kh2FontImageEditor/OpenKh.Tools.Kh2FontImageEditor.csproj @@ -11,6 +11,7 @@ + diff --git a/OpenKh.Tools.Kh2FontImageEditor/Usecases/ConvertFontDataUsecase.cs b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ConvertFontDataUsecase.cs index 9c0393f9e..fb76d01ac 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/Usecases/ConvertFontDataUsecase.cs +++ b/OpenKh.Tools.Kh2FontImageEditor/Usecases/ConvertFontDataUsecase.cs @@ -3,6 +3,7 @@ using OpenKh.Tools.Kh2FontImageEditor.Helpers; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -34,5 +35,27 @@ public FontInfoData Decode(IEnumerable fontInfoBar) return new FontInfoData(sys, evt, icon); } + + public IEnumerable Encode(FontInfoData fontInfoData) + { + var newBar = new List(); + + if (fontInfoData.System is byte[] sys) + { + newBar.Add(new Bar.Entry { Name = "sys", Type = Bar.EntryType.List, Stream = new MemoryStream(sys) }); + } + + if (fontInfoData.Event is byte[] evt) + { + newBar.Add(new Bar.Entry { Name = "evt", Type = Bar.EntryType.List, Stream = new MemoryStream(evt) }); + } + + if (fontInfoData.Icon is byte[] icon) + { + newBar.Add(new Bar.Entry { Name = "icon", Type = Bar.EntryType.List, Stream = new MemoryStream(icon) }); + } + + return newBar.AsReadOnly(); + } } } diff --git a/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml b/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml index 8edd0ecdf..5bb768194 100644 --- a/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml +++ b/OpenKh.Tools.Kh2FontImageEditor/UserControls/ImagerTablet.xaml @@ -8,9 +8,10 @@ x:Name="self" d:DesignHeight="450" d:DesignWidth="450"> + + -