diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2024-02-21 20:37:26 -0600 |
|---|---|---|
| committer | Lywx <kiritodev01@gmail.com> | 2024-02-21 20:38:05 -0600 |
| commit | 12532b1a7657f7194f9f325f3b9428e173754550 (patch) | |
| tree | 77688adbca3d06c080ddc4872504e53f186cc24c /lib | |
| parent | 055e2a0b65ecb373f82b2112ee9ec3b98cf2a62e (diff) | |
[WIP] Added palette and tlut export
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/n64graphics/n64graphics.c | 199 | ||||
| -rw-r--r-- | lib/n64graphics/n64graphics.h | 26 | ||||
| -rw-r--r-- | lib/n64graphics/stb_image_write.h | 116 |
3 files changed, 339 insertions, 2 deletions
diff --git a/lib/n64graphics/n64graphics.c b/lib/n64graphics/n64graphics.c index a3794b0..cca1a61 100644 --- a/lib/n64graphics/n64graphics.c +++ b/lib/n64graphics/n64graphics.c @@ -121,6 +121,37 @@ ia* raw2ia(const uint8_t* raw, int width, int height, int depth) { return img; } +ci* raw2ci_torch(const uint8_t* raw, int width, int height, int depth) { + ci* img = NULL; + int img_size; + + img_size = width * height * sizeof(*img); + img = malloc(img_size); + if (!img) { + ERROR("Error allocating %u bytes\n", img_size); + return NULL; + } + + switch (depth) { + case 8: + for (int i = 0; i < width * height; i++) { + img[i].index = raw[i]; + } + break; + case 4: + for (int i = 0; i < width * height; i++) { + int pos = i / 2; + img[i].index = i % 2 ? raw[pos] & 0xF : raw[pos] >> 4; + } + break; + default: + ERROR("Error invalid depth %d\n", depth); + break; + } + + return img; +} + ia* raw2i(const uint8_t* raw, int width, int height, int depth) { ia* img = NULL; int img_size; @@ -303,6 +334,37 @@ int i2raw(uint8_t* raw, const ia* img, int width, int height, int depth) { return size; } +int ci2raw_torch(uint8_t* raw, const ci* img, int width, int height, int depth) { + int size = width * height * depth / 8; + INFO("Converting I%d %dx%d to raw\n", depth, width, height); + + switch (depth) { + case 8: + for (int i = 0; i < width * height; i++) { + raw[i] = img[i].index; + } + break; + case 4: + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x += 2) { + const size_t pos = (y * width + x) / 2; + + const uint8_t cR1 = img[y * width + x].index; + const uint8_t cR2 = img[y * width + x + 1].index; + + raw[pos] = cR1 << 4 | cR2; + } + } + break; + default: + ERROR("Error invalid depth %d\n", depth); + size = -1; + break; + } + + return size; +} + //--------------------------------------------------------- // internal RGBA/IA -> PNG //--------------------------------------------------------- @@ -354,6 +416,27 @@ int ia2png(unsigned char** png_output, int* size_output, const ia* img, int widt return ret; } +int ci2png(unsigned char** png_output, int* size_output, const ci* img, int width, int height) { + int ret = 0; + + // convert to format stb_image_write expects + uint8_t* data = malloc(width * height); + if (data) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + int idx = j * width + i; + data[idx] = img[idx].index; + } + } + + (*png_output) = stbi_write_plte_png_to_mem(data, 0, width, height, 1, NULL, 0, size_output); + + free(data); + } + + return ret; +} + //--------------------------------------------------------- // PNG -> internal RGBA/IA //--------------------------------------------------------- @@ -372,7 +455,7 @@ rgba* png2rgba(unsigned char* png_input, int size_input, int* width, int* height } INFO("Read %dx%d channels: %d\n", w, h, channels); - img_size = w * h * sizeof(*img); + img_size = w * h * sizeof(rgba); img = malloc(img_size); if (!img) { ERROR("Error allocating %u bytes\n", img_size); @@ -421,6 +504,68 @@ rgba* png2rgba(unsigned char* png_input, int size_input, int* width, int* height return img; } +rgb* png2rgb(unsigned char* png_input, int size_input, int* width, int* height) { + rgba* img = NULL; + int w = 0; + int h = 0; + int channels = 0; + int img_size; + + stbi_uc* data = stbi_load_from_memory(png_input, size_input, &w, &h, &channels, STBI_rgb); + if (!data || w <= 0 || h <= 0) { + ERROR("Error loading file\n"); + return NULL; + } + INFO("Read %dx%d channels: %d\n", w, h, channels); + + img_size = w * h * sizeof(*img); + img = malloc(img_size); + if (!img) { + ERROR("Error allocating %u bytes\n", img_size); + return NULL; + } + + switch (channels) { + case 3: // red, green, blue + case 4: // red, green, blue + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int idx = j * w + i; + img[idx].red = data[channels * idx]; + img[idx].green = data[channels * idx + 1]; + img[idx].blue = data[channels * idx + 2]; + if (channels == 4) { + img[idx].alpha = data[channels * idx + 3]; + } else { + img[idx].alpha = 0xFF; + } + } + } + break; + case 2: // grey, alpha + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int idx = j * w + i; + img[idx].red = data[2 * idx]; + img[idx].green = data[2 * idx]; + img[idx].blue = data[2 * idx]; + } + } + break; + default: + ERROR("Don't know how to read channels: %d\n", channels); + free(img); + img = NULL; + } + + // cleanup + stbi_image_free(data); + + *width = w; + *height = h; + return img; +} + ia* png2ia(unsigned char* png_input, int size_input, int* width, int* height) { ia* img = NULL; int w = 0, h = 0; @@ -481,6 +626,58 @@ ia* png2ia(unsigned char* png_input, int size_input, int* width, int* height) { return img; } +ci* png2ci(unsigned char* png_input, int size_input, int* width, int* height) { + ci* img = NULL; + int w = 0, h = 0; + int channels = 0; + int img_size; + + stbi_uc* data = stbi_load_from_memory(png_input, size_input, &w, &h, &channels, STBI_default); + if (!data || w <= 0 || h <= 0) { + ERROR("Error loading file\n"); + return NULL; + } + INFO("Read %dx%d channels: %d\n", w, h, channels); + + img_size = w * h * sizeof(*img); + img = malloc(img_size); + if (!img) { + ERROR("Error allocating %d bytes\n", img_size); + return NULL; + } + + switch (channels) { + case 3: // red, green, blue + case 4: // red, green, blue, alpha + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int idx = j * w + i; + img[idx].index = data[channels * idx]; + } + } + break; + case 2: // grey, alpha + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int idx = j * w + i; + img[idx].index = data[2 * idx]; + } + } + break; + default: + ERROR("Don't know how to read channels: %d\n", channels); + free(img); + img = NULL; + } + + // cleanup + stbi_image_free(data); + + *width = w; + *height = h; + return img; +} + // find index of palette color // return -1 if not found static int pal_find_color(const palette_t* pal, uint16_t val) { diff --git a/lib/n64graphics/n64graphics.h b/lib/n64graphics/n64graphics.h index 0d17e86..225169e 100644 --- a/lib/n64graphics/n64graphics.h +++ b/lib/n64graphics/n64graphics.h @@ -12,12 +12,23 @@ typedef struct _rgba uint8_t alpha; } rgba; +typedef struct _rgb +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} rgb; + typedef struct _ia { uint8_t intensity; uint8_t alpha; } ia; +typedef struct _ci { + uint8_t index; +} ci; + // CI palette typedef struct { @@ -39,6 +50,9 @@ ia *raw2ia(const uint8_t *raw, int width, int height, int depth); // N64 raw I4/I8 -> intermediate IA ia *raw2i(const uint8_t *raw, int width, int height, int depth); +// N64 raw CI4/CI8 -> intermediate CI +ci* raw2ci_torch(const uint8_t* raw, int width, int height, int depth); + //--------------------------------------------------------- // intermediate RGBA/IA -> N64 RGBA/IA/I/CI // returns length written to 'raw' used or -1 on error @@ -53,6 +67,9 @@ int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth); // intermediate IA -> N64 raw I4/I8 int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth); +// intermediate CI -> N64 raw CI4/CI8 +int ci2raw_torch(uint8_t* raw, const ci* img, int width, int height, int depth); + //--------------------------------------------------------- // N64 CI <-> N64 RGBA16/IA16 @@ -64,7 +81,6 @@ uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int hei // convert from raw (RGBA16 or IA16) format to CI + palette int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth); - //--------------------------------------------------------- // intermediate RGBA/IA -> PNG //--------------------------------------------------------- @@ -75,6 +91,8 @@ int rgba2png(unsigned char** png_output, int* size_output, const rgba* img, int // intermediate IA write to grayscale PNG file int ia2png(unsigned char** png_output, int* size_output, const ia* img, int width, int height); +int ci2png(unsigned char** png_output, int* size_output, const ci* img, int width, int height); + //--------------------------------------------------------- // PNG -> intermediate RGBA/IA @@ -83,9 +101,15 @@ int ia2png(unsigned char** png_output, int* size_output, const ia* img, int widt // PNG file -> intermediate RGBA rgba *png2rgba(unsigned char* png_input, int size_input, int *width, int *height); +// PNG file -> intermediate RGB +rgb* png2rgb(unsigned char* png_input, int size_input, int* width, int* height); + // PNG file -> intermediate IA ia *png2ia(unsigned char* png_input, int size_input, int *width, int *height); +// PNG file -> intermediate CI +ci* png2ci(unsigned char* png_input, int size_input, int* width, int* height); + //--------------------------------------------------------- // version diff --git a/lib/n64graphics/stb_image_write.h b/lib/n64graphics/stb_image_write.h index 023d71e..465ea3b 100644 --- a/lib/n64graphics/stb_image_write.h +++ b/lib/n64graphics/stb_image_write.h @@ -1211,6 +1211,122 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s return out; } +typedef struct _pixel +{ + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; +} pixel; + +STBIWDEF unsigned char *stbi_write_plte_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, pixel* plte, int plte_size, int *out_len) { + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 3, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + // out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12 + (plte_size * 3 + 12)) + (plte_size + 12); + // if (!out) return 0; + // *out_len = 8 + 12+13 + 12+zlen + 12 + (plte_size * 3 + 12) + (plte_size + 12); + + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12 + (32 * 3 + 12)); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12 + (32 * 3 + 12); + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(3); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + // WRITE PLTE Chunk + stbiw__wp32(o, 32 * 3); + stbiw__wptag(o, "PLTE"); + // STBIW_MEMMOVE(0, sig, 3*16); o+=3*16; + for (j=0; j < 32; j++) { + *o++ = STBIW_UCHAR(j); + *o++ = STBIW_UCHAR(j); + *o++ = STBIW_UCHAR(j); + } + stbiw__wpcrc(&o, 32 * 3); + + // Write tRNS Chunk + // stbiw__wp32(o, plte_size); + // stbiw__wptag(o, "tRNS"); + // for (j=0; j < plte_size; j++) { + // *o++ = STBIW_UCHAR(plte[j].alpha); + // } + // stbiw__wpcrc(&o, plte_size); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { |
