Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding OpenKh.Tools.Kh2FontImageEditor #847

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 184 additions & 78 deletions OpenKh.Kh2/Contextes/FontContext.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -52,83 +54,187 @@ public void Read(IEnumerable<Bar.Entry> 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<Bar.Entry> 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;
}
}
}
28 changes: 28 additions & 0 deletions OpenKh.Kh2/FontEditing/FontIconBitmap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using OpenKh.Imaging;
using System;
using System.Collections.Generic;
using System.Text;

namespace OpenKh.Kh2.FontEditing
{
public class FontIconBitmap
{
public static byte[] 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 body = new byte[256 * 160 + 4 * 256];
Buffer.BlockCopy(image.GetData(), 0, body, 0, 256 * 160);
Buffer.BlockCopy(image.GetClut(), 0, body, 256 * 160, 4 * 256);
return body;
}
}
}
Loading
Loading