pokeemerald/tools/rsfont/font.c
2017-12-03 19:55:01 -06:00

456 lines
13 KiB
C

// Copyright(c) 2015-2016 YamaArashi
//
// 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.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "font.h"
#include "gfx.h"
#include "util.h"
unsigned char gFontPalette[][3] =
{
{0xFF, 0xFF, 0xFF}, // bg (white)
{0x38, 0x38, 0x38}, // fg (dark grey)
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
};
void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[(glyph * 8) + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
}
else
{
// layout type 1
int tile1Offset = glyph * 16;
int tile2Offset = tile1Offset + 8;
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[tile1Offset + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[tile2Offset + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
}
}
}
void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[(glyph * 8) + i] = destRow;
}
}
else
{
// layout type 1
int tile1Offset = glyph * 16;
int tile2Offset = tile1Offset + 8;
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[tile1Offset + i] = destRow;
}
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[tile2Offset + i] = destRow;
}
}
}
}
void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
static unsigned char table[16] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1,
};
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
int offset = glyph * 32;
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[offset + 3] << 24)
| (src[offset + 2] << 16)
| (src[offset + 1] << 8)
| src[offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
offset += 4;
}
}
else
{
int tile1Offset;
int tile2Offset;
if (layout == 1)
{
tile1Offset = glyph * 64;
tile2Offset = tile1Offset + 32;
}
else
{
tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32);
tile2Offset = tile1Offset + 512;
}
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[tile1Offset + 3] << 24)
| (src[tile1Offset + 2] << 16)
| (src[tile1Offset + 1] << 8)
| src[tile1Offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
tile1Offset += 4;
}
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[tile2Offset + 3] << 24)
| (src[tile2Offset + 2] << 16)
| (src[tile2Offset + 1] << 8)
| src[tile2Offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
tile2Offset += 4;
}
}
}
}
void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
static unsigned char table[3] =
{
0, 15, 14,
};
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
int offset = glyph * 32;
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[offset] = destRow & 0xFF;
dest[offset + 1] = (destRow >> 8) & 0xFF;
dest[offset + 2] = (destRow >> 16) & 0xFF;
dest[offset + 3] = (destRow >> 24) & 0xFF;
offset += 4;
}
}
else
{
int tile1Offset;
int tile2Offset;
if (layout == 1)
{
tile1Offset = glyph * 64;
tile2Offset = tile1Offset + 32;
}
else
{
tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32);
tile2Offset = tile1Offset + 512;
}
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[tile1Offset] = destRow & 0xFF;
dest[tile1Offset + 1] = (destRow >> 8) & 0xFF;
dest[tile1Offset + 2] = (destRow >> 16) & 0xFF;
dest[tile1Offset + 3] = (destRow >> 24) & 0xFF;
tile1Offset += 4;
}
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[tile2Offset] = destRow & 0xFF;
dest[tile2Offset + 1] = (destRow >> 8) & 0xFF;
dest[tile2Offset + 2] = (destRow >> 16) & 0xFF;
dest[tile2Offset + 3] = (destRow >> 24) & 0xFF;
tile2Offset += 4;
}
}
}
}
static void SetFontPalette(struct Image *image)
{
image->hasPalette = true;
image->palette.numColors = 3;
for (int i = 0; i < image->palette.numColors; i++)
{
image->palette.colors[i].red = gFontPalette[i][0];
image->palette.colors[i].green = gFontPalette[i][1];
image->palette.colors[i].blue = gFontPalette[i][2];
}
image->hasTransparency = false;
}
int CalcFileSize(int numGlyphs, int bpp, int layout)
{
if (layout == 2)
{
// assume 4 BPP
int numFullRows = numGlyphs / 16;
int remainder = numGlyphs % 16;
int fullRowsSize = numFullRows * 1024;
int remainderSize = 0;
if (remainder != 0)
remainderSize = 1024 - (16 - remainder) * 32;
return fullRowsSize + remainderSize;
}
else
{
int tilesPerGlyph = layout > 0 ? 2 : 1;
int bytesPerTile = 8 * bpp;
return numGlyphs * tilesPerGlyph * bytesPerTile;
}
}
void ReadFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int expectedFileSize = CalcFileSize(numGlyphs, bpp, layout);
if (fileSize != expectedFileSize)
FATAL_ERROR("The file size is %d but should be %d.\n", fileSize, expectedFileSize);
int numRows = (numGlyphs + 15) / 16;
int rowHeight = layout > 0 ? 16 : 8;
image->width = 128;
image->height = numRows * rowHeight;
image->bitDepth = 8;
image->pixels = calloc(image->width * image->height, 1);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
if (bpp == 1)
ConvertFromTiles1Bpp(buffer, image->pixels, numGlyphs, layout);
else
ConvertFromTiles4Bpp(buffer, image->pixels, numGlyphs, layout);
free(buffer);
SetFontPalette(image);
}
void WriteFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout)
{
if (image->width != 128)
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
int numRows = (numGlyphs + 15) / 16;
int rowHeight = layout > 0 ? 16 : 8;
int expectedHeight = numRows * rowHeight;
if (image->height < expectedHeight)
FATAL_ERROR("The height of the font image (%d) is less than %d.\n", image->height, expectedHeight);
int fileSize = CalcFileSize(numGlyphs, bpp, layout);
unsigned char *buffer = calloc(fileSize, 1);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
if (bpp == 1)
ConvertToTiles1Bpp(image->pixels, buffer, numGlyphs, layout);
else
ConvertToTiles4Bpp(image->pixels, buffer, numGlyphs, layout);
WriteWholeFile(path, buffer, fileSize);
free(buffer);
}