Created
July 5, 2022 06:16
-
-
Save khang06/74b1293c2ef4592dd1a3285487a1075b to your computer and use it in GitHub Desktop.
Simple atlas generator for Proggy Clean (and other monospace bitmap fonts)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <gdiplus.h> | |
#include <stdio.h> | |
#pragma comment(lib,"gdiplus.lib") | |
using namespace Gdiplus; | |
// https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-retrieving-the-class-identifier-for-an-encoder-use | |
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { | |
UINT num = 0; // number of image encoders | |
UINT size = 0; // size of the image encoder array in bytes | |
ImageCodecInfo* pImageCodecInfo = NULL; | |
GetImageEncodersSize(&num, &size); | |
if (size == 0) | |
return -1; // Failure | |
pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); | |
if (pImageCodecInfo == NULL) | |
return -1; // Failure | |
GetImageEncoders(num, size, pImageCodecInfo); | |
for (UINT j = 0; j < num; ++j) | |
{ | |
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) | |
{ | |
*pClsid = pImageCodecInfo[j].Clsid; | |
free(pImageCodecInfo); | |
return j; // Success | |
} | |
} | |
free(pImageCodecInfo); | |
return -1; // Failure | |
} | |
int main() { | |
// Init GDI+ | |
GdiplusStartupInput gdiplusStartupInput; | |
ULONG_PTR gdiplusToken; | |
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); | |
// Calculate how the texture will be generated | |
constexpr unsigned char_width = 8; | |
constexpr unsigned char_height = 8; | |
constexpr unsigned char_underhang = 4; | |
constexpr unsigned padding = 1; | |
constexpr unsigned per_row = 16; | |
constexpr unsigned rows = (128 - 32) / per_row + ((128 - 32) % per_row != 0); | |
unsigned tex_width = (char_width + padding) * per_row; | |
unsigned tex_height = (char_height + char_underhang + padding) * rows; | |
printf("width: %d, height: %d\n", tex_width, tex_height); | |
// Initialize the GDI objects | |
SolidBrush brush(Color(255, 255, 255, 255)); | |
Bitmap atlas(tex_width, tex_height, PixelFormat32bppARGB); | |
Graphics gfx(&atlas); | |
FontFamily family(L"ProggyCleanTTSZ"); | |
Font font(&family, 16, FontStyleRegular, UnitPixel); | |
// Render the atlas | |
gfx.Clear(Color(0, 0, 0, 0)); | |
gfx.SetTextRenderingHint(TextRenderingHintSingleBitPerPixel); | |
wchar_t buf[2] = {}; | |
for (int y = 0; y < rows; y++) { | |
for (int x = 0; x < per_row; x++) { | |
PointF point((char_width + padding) * x - 3.0f, (char_height + char_underhang + padding) * y - 2.0f); | |
buf[0] = (wchar_t)(y * per_row + x + 32); | |
gfx.DrawString(buf, -1, &font, point, &brush); | |
} | |
} | |
/* | |
// Save as a PNG file | |
CLSID png_clsid; | |
GetEncoderClsid(L"image/png", &png_clsid); | |
atlas.Save(L"atlas.png", &png_clsid, NULL); | |
*/ | |
// Compress the atlas | |
Rect rect(0, 0, tex_width, tex_height); | |
BitmapData data; | |
atlas.LockBits(&rect, ImageLockModeRead, PixelFormat32bppARGB, &data); | |
unsigned compressed_size = tex_width * tex_height / 8 + (tex_width * tex_height % 8 != 0); | |
char* compressed = new char[compressed_size]; | |
unsigned bits_left = tex_width * tex_height; | |
for (unsigned i = 0; i < compressed_size; i++) { | |
char output = 0; | |
for (int j = 0; j < min(8, bits_left); j++) { | |
output <<= 1; | |
output |= ((char*)data.Scan0)[(i * 8 + j) * 4] != 0; | |
} | |
compressed[i] = output; | |
bits_left -= 8; | |
} | |
atlas.UnlockBits(&data); | |
// Save it | |
FILE* output = fopen("atlas.bin", "wb"); | |
fwrite(compressed, compressed_size, 1, output); | |
fclose(output); | |
printf("Done\n"); | |
delete[] compressed; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment