mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Add program by @aaaaaa123456789 to generate RGBGFX-able images
This commit is contained in:
3
Makefile
3
Makefile
@@ -126,6 +126,9 @@ rgbfix: ${rgbfix_obj}
|
|||||||
rgbgfx: ${rgbgfx_obj}
|
rgbgfx: ${rgbgfx_obj}
|
||||||
$Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${REALCXXFLAGS} -x c++ src/version.c ${PNGLDLIBS}
|
$Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${REALCXXFLAGS} -x c++ src/version.c ${PNGLDLIBS}
|
||||||
|
|
||||||
|
test/randtilegen: test/randtilegen.c
|
||||||
|
$Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCFLAGS} -Wno-vla ${PNGCFLAGS} ${PNGLDLIBS}
|
||||||
|
|
||||||
# Rules to process files
|
# Rules to process files
|
||||||
|
|
||||||
# We want the Bison invocation to pass through our rules, not default ones
|
# We want the Bison invocation to pass through our rules, not default ones
|
||||||
|
|||||||
4
test/.gitignore
vendored
4
test/.gitignore
vendored
@@ -1,3 +1 @@
|
|||||||
/pokecrystal/
|
/randtilegen
|
||||||
/pokered/
|
|
||||||
/ucity/
|
|
||||||
|
|||||||
240
test/randtilegen.c
Normal file
240
test/randtilegen.c
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Originally:
|
||||||
|
* // This program is hereby released to the public domain.
|
||||||
|
* // ~aaaaaa123456789, released 2022-03-15
|
||||||
|
* https://gist.github.com/aaaaaa123456789/3feccf085ab4f82d144d9a47fb1b4bdf
|
||||||
|
*
|
||||||
|
* This was modified to use libpng instead of libplum, as well as comments and style changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <png.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
uint32_t randBits = 0; // Storage for bits read from the input stream but not yet used
|
||||||
|
uint8_t randCount = 0; // How many bits are currently stored in the above
|
||||||
|
size_t nbRandBytes = 0;
|
||||||
|
|
||||||
|
static uint32_t getRandomBits(uint8_t count) {
|
||||||
|
// Trying to read one more byte with `randCount` at least this high will drop some bits
|
||||||
|
// If the count is no higher than that limit, then the loop will exit without reading more bytes
|
||||||
|
assert(count <= sizeof(randBits) * 8 + 1);
|
||||||
|
|
||||||
|
// Read bytes until we have enough bits to serve the request
|
||||||
|
while (count > randCount) {
|
||||||
|
int data = getchar();
|
||||||
|
if (data == EOF) {
|
||||||
|
fprintf(stderr, "Exit after reading %zu bytes\n", nbRandBytes);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
randBits |= (uint32_t)data << randCount;
|
||||||
|
randCount += 8;
|
||||||
|
++nbRandBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t result = randBits & (((uint32_t)1 << count) - 1);
|
||||||
|
randBits >>= count;
|
||||||
|
randCount -= count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand a 5-bit color component to 8 bits with minimal bias
|
||||||
|
*/
|
||||||
|
static uint8_t _5to8(uint8_t five) {
|
||||||
|
return five << 3 | five >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_random_image(png_structp png, png_infop pngInfo) {
|
||||||
|
#define NB_TILES 100
|
||||||
|
struct {
|
||||||
|
unsigned char palette;
|
||||||
|
unsigned char nbColors;
|
||||||
|
} attributes[NB_TILES];
|
||||||
|
uint8_t tileData[NB_TILES][8][8];
|
||||||
|
// These two are in tiles, not pixels
|
||||||
|
// Both width and height are 4-bit values, so nbTiles is 8-bit (OK!)
|
||||||
|
uint8_t const width = getRandomBits(3) + 3, height = getRandomBits(3) + 3,
|
||||||
|
nbTiles = width * height;
|
||||||
|
|
||||||
|
for (uint8_t p = 0; p < nbTiles; p++) {
|
||||||
|
uint8_t pal;
|
||||||
|
do {
|
||||||
|
pal = getRandomBits(5);
|
||||||
|
} while (pal == 0 || (pal > 29));
|
||||||
|
attributes[p].palette = 2 * pal + getRandomBits(1);
|
||||||
|
// Population count (nb of bits set), the simple way
|
||||||
|
static uint8_t const popcount[] = {1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4};
|
||||||
|
attributes[p].nbColors = popcount[pal - 1];
|
||||||
|
if (attributes[p].nbColors < 2) {
|
||||||
|
memset(tileData[p], 0, sizeof(tileData[p]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t index, total;
|
||||||
|
for (index = 0, total = 0; index < p; index++) {
|
||||||
|
if (attributes[index].nbColors == attributes[p].nbColors) {
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// index == p at exit
|
||||||
|
if (total) {
|
||||||
|
index = getRandomBits(8);
|
||||||
|
if (index < total) {
|
||||||
|
total = index + 1;
|
||||||
|
for (index = 0; total; index++) {
|
||||||
|
if (attributes[index].nbColors == attributes[p].nbColors) {
|
||||||
|
total--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index != p) {
|
||||||
|
unsigned rotation = getRandomBits(2);
|
||||||
|
for (uint8_t y = 0; y < 8; y++) {
|
||||||
|
for (uint8_t x = 0; x < 8; x++) {
|
||||||
|
tileData[p][y][x] =
|
||||||
|
tileData[index][y ^ ((rotation & 2) ? 7 : 0)][x ^ ((rotation & 1) ? 7 : 0)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (attributes[p].nbColors) {
|
||||||
|
case 2:
|
||||||
|
for (uint8_t y = 0; y < 8; y++)
|
||||||
|
for (uint8_t x = 0; x < 8; x++)
|
||||||
|
tileData[p][y][x] = getRandomBits(1);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
for (uint8_t y = 0; y < 8; y++)
|
||||||
|
for (uint8_t x = 0; x < 8; x++)
|
||||||
|
tileData[p][y][x] = getRandomBits(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (uint8_t y = 0; y < 8; y++)
|
||||||
|
for (uint8_t x = 0; x < 8; x++) {
|
||||||
|
do {
|
||||||
|
index = getRandomBits(2);
|
||||||
|
} while (index == 3);
|
||||||
|
tileData[p][y][x] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t colors[10];
|
||||||
|
for (uint8_t p = 0; p < 10; p++) {
|
||||||
|
colors[p] = getRandomBits(15);
|
||||||
|
}
|
||||||
|
// Randomly make color #0 of all palettes transparent
|
||||||
|
if (!getRandomBits(2)) {
|
||||||
|
colors[0] |= 0x8000;
|
||||||
|
colors[5] |= 0x8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t palettes[60][4];
|
||||||
|
for (uint8_t p = 0; p < 60; p++) {
|
||||||
|
const uint16_t *subpal = colors;
|
||||||
|
if (p & 1) {
|
||||||
|
subpal += 5;
|
||||||
|
}
|
||||||
|
uint8_t total = 0;
|
||||||
|
for (uint8_t index = 0; index < 5; index++) {
|
||||||
|
if (p & (2 << index)) {
|
||||||
|
palettes[p][total++] = subpal[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_IHDR(png, pngInfo, width * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA,
|
||||||
|
getRandomBits(1) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
|
||||||
|
// While it would be nice to write the image little by little, I really don't want to handle
|
||||||
|
// interlacing myself. (We're doing interlacing to test that RGBGFX correctly handles it.)
|
||||||
|
uint8_t const SIZEOF_PIXEL = 4; // Each pixel is 4 bytes (RGBA @ 8 bits/component)
|
||||||
|
uint8_t data[height * 8 * width * 8 * SIZEOF_PIXEL];
|
||||||
|
uint8_t *rowPtrs[height * 8];
|
||||||
|
for (uint8_t y = 0; y < height * 8; ++y) {
|
||||||
|
rowPtrs[y] = &data[y * width * 8 * SIZEOF_PIXEL];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t p = 0; p < nbTiles; p++) {
|
||||||
|
uint8_t tx = 8 * (p % width), ty = 8 * (p / width);
|
||||||
|
for (uint8_t y = 0; y < 8; y++) {
|
||||||
|
uint8_t * const row = rowPtrs[ty + y];
|
||||||
|
for (uint8_t x = 0; x < 8; x++) {
|
||||||
|
uint8_t * const pixel = &row[(tx + x) * SIZEOF_PIXEL];
|
||||||
|
uint16_t color = palettes[attributes[p].palette][tileData[p][y][x]];
|
||||||
|
pixel[0] = _5to8(color & 0x1F);
|
||||||
|
pixel[1] = _5to8(color >> 5 & 0x1F);
|
||||||
|
pixel[2] = _5to8(color >> 10 & 0x1F);
|
||||||
|
pixel[3] = color & 0x8000 ? 0x00 : 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
png_set_rows(png, pngInfo, rowPtrs);
|
||||||
|
png_write_png(png, pngInfo, PNG_TRANSFORM_IDENTITY, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fputs("usage: randtilegen <basename> [<basename> [...]]\n", stderr);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t maxBasenameLen = 0;
|
||||||
|
for (int index = 1; index < argc; index++) {
|
||||||
|
size_t length = strlen(argv[index]);
|
||||||
|
if (length > maxBasenameLen) {
|
||||||
|
maxBasenameLen = length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename[maxBasenameLen + 25];
|
||||||
|
for (uint16_t i = 0;; i++) { // 65k images ought to be enough
|
||||||
|
for (int index = 1; index < argc; index++) {
|
||||||
|
sprintf(filename, "%s%" PRIu16 ".png", argv[index], i);
|
||||||
|
FILE *img = fopen(filename, "wb");
|
||||||
|
if (!img) {
|
||||||
|
perror("PNG fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
if (!png) {
|
||||||
|
perror("png_create_write_struct");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
png_infop pngInfo = png_create_info_struct(png);
|
||||||
|
if (!pngInfo) {
|
||||||
|
perror("png_create_info_struct");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (setjmp(png_jmpbuf(png))) {
|
||||||
|
fprintf(stderr, "FATAL: an error occurred while writing image \"%s\"\n", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_init_io(png, img);
|
||||||
|
generate_random_image(png, pngInfo);
|
||||||
|
png_destroy_write_struct(&png, &pngInfo);
|
||||||
|
fclose(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == UINT16_MAX) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user