summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2024-02-21 20:37:26 -0600
committerLywx <kiritodev01@gmail.com>2024-02-21 20:38:05 -0600
commit12532b1a7657f7194f9f325f3b9428e173754550 (patch)
tree77688adbca3d06c080ddc4872504e53f186cc24c /lib
parent055e2a0b65ecb373f82b2112ee9ec3b98cf2a62e (diff)
[WIP] Added palette and tlut export
Diffstat (limited to 'lib')
-rw-r--r--lib/n64graphics/n64graphics.c199
-rw-r--r--lib/n64graphics/n64graphics.h26
-rw-r--r--lib/n64graphics/stb_image_write.h116
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)
{