Part 8: Tile System
This commit is contained in:
parent
02a911ef9f
commit
1096c7eac2
|
@ -19,3 +19,5 @@ if (ret != ESP_OK) {
|
||||||
* `IDF_TOOLS_PATH` is the path to the toolchain
|
* `IDF_TOOLS_PATH` is the path to the toolchain
|
||||||
* `source export.sh`
|
* `source export.sh`
|
||||||
|
|
||||||
|
![Current Status](https://austinmorlan.com/posts/embedded_game_programming_8/media/demo.gif)
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
JASC-PAL
|
||||||
|
0100
|
||||||
|
4
|
||||||
|
255 255 255
|
||||||
|
171 171 171
|
||||||
|
84 84 84
|
||||||
|
0 0 0
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
266
game/src/main.c
266
game/src/main.c
|
@ -15,6 +15,139 @@ static const char* LOG_TAG = "Main";
|
||||||
static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT];
|
static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT];
|
||||||
|
|
||||||
|
|
||||||
|
static const uint16_t palette[4] =
|
||||||
|
{
|
||||||
|
0xFFFF,
|
||||||
|
0x55AD,
|
||||||
|
0xAA52,
|
||||||
|
0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t tiles[][16*16] =
|
||||||
|
{
|
||||||
|
// White
|
||||||
|
{
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Light Grey
|
||||||
|
{
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
|
||||||
|
0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dark Grey
|
||||||
|
{
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,
|
||||||
|
0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Black
|
||||||
|
{
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,
|
||||||
|
0,0,3,3,3,3,3,3,3,3,3,3,3,3,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tileBuffer[15][40] =
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
|
||||||
|
{0, 0, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
|
||||||
|
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void DrawTile(int index, int x, int y)
|
||||||
|
{
|
||||||
|
int startX = x * 16;
|
||||||
|
int startY = y * 16;
|
||||||
|
|
||||||
|
for (int row = 0; row < 16; ++row)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < 16; ++col)
|
||||||
|
{
|
||||||
|
uint8_t paletteIndex = tiles[index][row * 16 + col];
|
||||||
|
|
||||||
|
int screenY = startY + row;
|
||||||
|
int screenX = startX + col;
|
||||||
|
|
||||||
|
uint16_t color = palette[paletteIndex];
|
||||||
|
|
||||||
|
gFramebuffer[screenY * LCD_WIDTH + screenX] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
Odroid_InitializeInput();
|
Odroid_InitializeInput();
|
||||||
|
@ -23,113 +156,63 @@ void app_main(void)
|
||||||
Odroid_InitializeBatteryReader();
|
Odroid_InitializeBatteryReader();
|
||||||
Odroid_InitializeAudio();
|
Odroid_InitializeAudio();
|
||||||
|
|
||||||
// Load sprite
|
|
||||||
uint16_t* sprite = (uint16_t*)malloc(64 * 64 * sizeof(uint16_t));
|
|
||||||
{
|
|
||||||
FILE* spriteFile = fopen("/sdcard/key", "r");
|
|
||||||
assert(spriteFile);
|
|
||||||
|
|
||||||
for (int i = 0; i < 64; ++i)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 64; ++j)
|
|
||||||
{
|
|
||||||
fread(sprite, sizeof(uint16_t), 64 * 64, spriteFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(spriteFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load sound effect
|
|
||||||
uint16_t* soundBuffer;
|
|
||||||
int soundEffectLength = 1441;
|
|
||||||
{
|
|
||||||
FILE* soundFile = fopen("/sdcard/jump", "r");
|
|
||||||
assert(soundFile);
|
|
||||||
|
|
||||||
uint8_t* soundEffect = malloc(soundEffectLength);
|
|
||||||
assert(soundEffect);
|
|
||||||
|
|
||||||
soundBuffer = malloc(soundEffectLength*2);
|
|
||||||
assert(soundBuffer);
|
|
||||||
|
|
||||||
fread(soundEffect, soundEffectLength, 1, soundFile);
|
|
||||||
|
|
||||||
for (int i = 0; i < soundEffectLength; ++i)
|
|
||||||
{
|
|
||||||
// 16 bits required but only MSB is actually sent to the DAC
|
|
||||||
soundBuffer[i] = (soundEffect[i] << 8u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop");
|
ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop");
|
||||||
|
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
|
|
||||||
uint16_t color = 0xffff;
|
uint8_t frameIndex = 0;
|
||||||
|
char snapFilename[20];
|
||||||
int lastState = 0;
|
int xLeft = 0;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
memset(gFramebuffer, 0, 320 * 240 * 2);
|
memset(gFramebuffer, 0xff, 320*240*2);
|
||||||
|
|
||||||
Odroid_Input input = Odroid_PollInput();
|
Odroid_Input input = Odroid_PollInput();
|
||||||
|
|
||||||
if (input.left) { x -= 20; }
|
if (input.left)
|
||||||
else if (input.right) { x += 20; }
|
|
||||||
|
|
||||||
if (input.up) { y -= 20; }
|
|
||||||
else if (input.down) { y += 20; }
|
|
||||||
|
|
||||||
if (input.a) { color = SWAP_ENDIAN_16(RGB565(0xff, 0, 0)); }
|
|
||||||
else if (input.b) { color = SWAP_ENDIAN_16(RGB565(0, 0xff, 0)); }
|
|
||||||
else if (input.start) { color = SWAP_ENDIAN_16(RGB565(0, 0, 0xff)); }
|
|
||||||
else if (input.select) { color = SWAP_ENDIAN_16(RGB565(0xff, 0xff, 0xff)); }
|
|
||||||
|
|
||||||
|
|
||||||
int thisState = input.volume;
|
|
||||||
|
|
||||||
if ((thisState == 1) && (thisState != lastState))
|
|
||||||
{
|
{
|
||||||
Odroid_PlayAudio(soundBuffer, soundEffectLength*2);
|
xLeft -= 1;
|
||||||
|
|
||||||
|
if (xLeft < 0)
|
||||||
|
{
|
||||||
|
xLeft = 39;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastState = thisState;
|
else if (input.right)
|
||||||
|
|
||||||
|
|
||||||
DrawText(gFramebuffer, "The Quick Brown Fox", 19, 0, 5, SWAP_ENDIAN_16(RGB565(0xFF, 0, 0)));
|
|
||||||
DrawText(gFramebuffer, "Jumped Over The:", 16, 0, 6, SWAP_ENDIAN_16(RGB565(0, 0xFF, 0)));
|
|
||||||
DrawText(gFramebuffer, "Lazy Dog?!", 10, 0, 7, SWAP_ENDIAN_16(RGB565(0, 0, 0xFF)));
|
|
||||||
|
|
||||||
|
|
||||||
int spriteRow = 0;
|
|
||||||
int spriteCol = 0;
|
|
||||||
|
|
||||||
for (int row = y; row < y + 64; ++row)
|
|
||||||
{
|
{
|
||||||
spriteCol = 0;
|
xLeft += 1;
|
||||||
|
|
||||||
for (int col = x; col < x + 64; ++col)
|
if (xLeft > 39)
|
||||||
{
|
{
|
||||||
uint16_t pixelColor = sprite[64 * spriteRow + spriteCol];
|
xLeft = 0;
|
||||||
|
}
|
||||||
if (pixelColor != 0)
|
|
||||||
{
|
|
||||||
gFramebuffer[row * LCD_WIDTH + col] = color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++spriteCol;
|
for (int tileY = 0; tileY < 15; ++tileY)
|
||||||
|
{
|
||||||
|
for (int tileX = xLeft; tileX < xLeft + 20; ++tileX)
|
||||||
|
{
|
||||||
|
int tile = tileX % 40;
|
||||||
|
|
||||||
|
int tileIndex = tileBuffer[tileY][tile];
|
||||||
|
|
||||||
|
DrawTile(tileIndex, tileX - xLeft, tileY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++spriteRow;
|
|
||||||
}
|
char string[5];
|
||||||
|
snprintf(string, 5, "%02d", xLeft);
|
||||||
|
DrawText(gFramebuffer, string, 2, 0, 0, palette[3]);
|
||||||
|
|
||||||
|
int tileRight = (xLeft + 20) % 40;
|
||||||
|
snprintf(string, 5, "%02d", tileRight);
|
||||||
|
DrawText(gFramebuffer, string, 2, 18, 0, palette[3]);
|
||||||
|
|
||||||
if (input.menu)
|
if (input.menu)
|
||||||
{
|
{
|
||||||
const char* snapFilename = "/sdcard/framebuf";
|
snprintf(snapFilename, 20, "/sdcard/frame%02d", frameIndex);
|
||||||
|
|
||||||
ESP_LOGI(LOG_TAG, "Writing snapshot to %s", snapFilename);
|
ESP_LOGI(LOG_TAG, "Writing snapshot to %s", snapFilename);
|
||||||
|
|
||||||
|
@ -139,19 +222,8 @@ void app_main(void)
|
||||||
fwrite(gFramebuffer, 1, LCD_WIDTH * LCD_HEIGHT * sizeof(gFramebuffer[0]), snapFile);
|
fwrite(gFramebuffer, 1, LCD_WIDTH * LCD_HEIGHT * sizeof(gFramebuffer[0]), snapFile);
|
||||||
|
|
||||||
fclose(snapFile);
|
fclose(snapFile);
|
||||||
}
|
|
||||||
|
|
||||||
|
++frameIndex;
|
||||||
uint32_t batteryLevel = Odroid_ReadBatteryLevel();
|
|
||||||
ESP_LOGI(LOG_TAG, "Battery level: %u\n", batteryLevel);
|
|
||||||
|
|
||||||
if (batteryLevel < 3600)
|
|
||||||
{
|
|
||||||
Odroid_EnableBatteryLight();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Odroid_DisableBatteryLight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ static const int MAX_GLYPHS_PER_COL = LCD_HEIGHT / GLYPH_HEIGHT;
|
||||||
|
|
||||||
void DrawText(uint16_t* framebuffer, char* string, int length, int x, int y, uint16_t color)
|
void DrawText(uint16_t* framebuffer, char* string, int length, int x, int y, uint16_t color)
|
||||||
{
|
{
|
||||||
assert(x + length < MAX_GLYPHS_PER_ROW);
|
assert(x + length <= MAX_GLYPHS_PER_ROW);
|
||||||
assert(y < MAX_GLYPHS_PER_COL);
|
assert(y <= MAX_GLYPHS_PER_COL);
|
||||||
|
|
||||||
for (int charIndex = 0; charIndex < length; ++charIndex)
|
for (int charIndex = 0; charIndex < length; ++charIndex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,5 +4,7 @@ project(tools)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wall -Werror")
|
set(CMAKE_C_FLAGS "-Wall -Werror")
|
||||||
|
|
||||||
add_executable(font_generator src/font_generator.c)
|
add_executable(font_processor src/font_processor.c)
|
||||||
|
add_executable(palette_processor src/palette_processor.c)
|
||||||
|
add_executable(tile_processor src/tile_processor.c)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <input palette> <output header>\n", argv[0]);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* inFilename = argv[1];
|
||||||
|
const char* outFilename = argv[2];
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
uint16_t* palette;
|
||||||
|
int paletteSize;
|
||||||
|
{
|
||||||
|
FILE* inFile = fopen(inFilename, "r");
|
||||||
|
assert(inFile);
|
||||||
|
|
||||||
|
char* line;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
getline(&line, &len, inFile);
|
||||||
|
assert(strncmp(line, "JASC-PAL", 8) == 0);
|
||||||
|
|
||||||
|
getline(&line, &len, inFile);
|
||||||
|
assert(strncmp(line, "0100", 4) == 0);
|
||||||
|
|
||||||
|
getline(&line, &len, inFile);
|
||||||
|
paletteSize = atoi(line);
|
||||||
|
|
||||||
|
palette = malloc(sizeof(*palette) * paletteSize);
|
||||||
|
assert(palette);
|
||||||
|
|
||||||
|
// Each line is of form R G B
|
||||||
|
for (int i = 0; i < paletteSize; ++i)
|
||||||
|
{
|
||||||
|
getline(&line, &len, inFile);
|
||||||
|
|
||||||
|
char* tok = strtok(line, " ");
|
||||||
|
int red = atoi(tok);
|
||||||
|
|
||||||
|
tok = strtok(NULL, " ");
|
||||||
|
int green = atoi(tok);
|
||||||
|
|
||||||
|
tok = strtok(NULL, " ");
|
||||||
|
int blue = atoi(tok);
|
||||||
|
|
||||||
|
uint16_t rgb565 =
|
||||||
|
((red >> 3u) << 11u)
|
||||||
|
| ((green >> 2u) << 5u)
|
||||||
|
| (blue >> 3u);
|
||||||
|
|
||||||
|
uint16_t endianSwap = ((rgb565 & 0xFFu) << 8u) | (rgb565 >> 8u);
|
||||||
|
|
||||||
|
palette[i] = endianSwap;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(inFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("Input: %s\n", inFilename);
|
||||||
|
printf("Output: %s\n", outFilename);
|
||||||
|
printf("Palette Size: %d\n", paletteSize);
|
||||||
|
|
||||||
|
|
||||||
|
// Output to the file
|
||||||
|
{
|
||||||
|
FILE* outFile = fopen(outFilename, "w");
|
||||||
|
assert(outFile);
|
||||||
|
|
||||||
|
fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, "#pragma once\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
|
||||||
|
fprintf(outFile, "uint16_t palette[%d] =\n", paletteSize);
|
||||||
|
fprintf(outFile, "{\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < paletteSize; ++i)
|
||||||
|
{
|
||||||
|
fprintf(outFile, " 0x%04X,\n", palette[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(outFile, "};\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
|
||||||
|
fclose(outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("DONE\n");
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <input image> <output header>\n", argv[0]);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* inFilename = argv[1];
|
||||||
|
const char* outFilename = argv[2];
|
||||||
|
|
||||||
|
|
||||||
|
int tileWidth;
|
||||||
|
int tileHeight;
|
||||||
|
uint8_t* tileBuffer;
|
||||||
|
{
|
||||||
|
FILE* inFile = fopen(inFilename, "rb");
|
||||||
|
assert(inFile);
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct BmpHeader
|
||||||
|
{
|
||||||
|
char magic[2];
|
||||||
|
uint32_t totalSize;
|
||||||
|
uint32_t reserved;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t headerSize;
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
uint16_t planes;
|
||||||
|
uint16_t depth;
|
||||||
|
uint32_t compression;
|
||||||
|
uint32_t imageSize;
|
||||||
|
int32_t horizontalResolution;
|
||||||
|
int32_t verticalResolution;
|
||||||
|
uint32_t paletteColorCount;
|
||||||
|
uint32_t importantColorcount;
|
||||||
|
} bmpHeader;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
// Read the BMP header so we know where the image data is located
|
||||||
|
fread(&bmpHeader, 1, sizeof(bmpHeader), inFile);
|
||||||
|
assert(bmpHeader.magic[0] == 'B' && bmpHeader.magic[1] == 'M');
|
||||||
|
assert(bmpHeader.depth == 8);
|
||||||
|
assert(bmpHeader.headerSize == 40);
|
||||||
|
|
||||||
|
// Go to location in file of image data
|
||||||
|
fseek(inFile, bmpHeader.offset, SEEK_SET);
|
||||||
|
|
||||||
|
// Read in the image data
|
||||||
|
tileBuffer = malloc(bmpHeader.imageSize);
|
||||||
|
assert(tileBuffer);
|
||||||
|
fread(tileBuffer, 1, bmpHeader.imageSize, inFile);
|
||||||
|
|
||||||
|
tileWidth = bmpHeader.width;
|
||||||
|
tileHeight = bmpHeader.height;
|
||||||
|
|
||||||
|
fclose(inFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("Input: %s\n", inFilename);
|
||||||
|
printf("Output: %s\n", outFilename);
|
||||||
|
printf("Width: %d\n", tileWidth);
|
||||||
|
printf("Height: %d\n", tileHeight);
|
||||||
|
|
||||||
|
FILE* outFile = fopen(outFilename, "w");
|
||||||
|
assert(outFile);
|
||||||
|
|
||||||
|
|
||||||
|
// Generate the preamble
|
||||||
|
{
|
||||||
|
fprintf(outFile, "// AUTOMATICALLY GENERATED. DO NOT EDIT.\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, "#pragma once\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, "#include <stdint.h>\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Generate the font map with the calculated glyph bytes
|
||||||
|
{
|
||||||
|
fprintf(outFile, "static const uint8_t tile[%d][%d] =\n", tileHeight, tileWidth);
|
||||||
|
fprintf(outFile, "{\n");
|
||||||
|
fprintf(outFile, " ");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int row = 0; row < tileHeight; ++row)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < tileWidth; ++col)
|
||||||
|
{
|
||||||
|
// BMP is laid out bottom-to-top, but we want top-to-bottom (0-indexed)
|
||||||
|
int y = tileHeight - row - 1;
|
||||||
|
|
||||||
|
uint8_t paletteIndex = tileBuffer[y * tileWidth + col];
|
||||||
|
|
||||||
|
fprintf(outFile, "%d,", paletteIndex);
|
||||||
|
++count;
|
||||||
|
|
||||||
|
// Put a newline after sixteen values to keep it orderly
|
||||||
|
if ((count % 16) == 0)
|
||||||
|
{
|
||||||
|
fprintf(outFile, "\n");
|
||||||
|
fprintf(outFile, " ");
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(outFile, "};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(outFile);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("DONE\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue