Browse Source

Add support for custom pixel formats to png2bng

rexim 5 years ago
parent
commit
34361f60a2
4 changed files with 123 additions and 14 deletions
  1. 1 0
      .gitignore
  2. 8 0
      Makefile
  3. 21 4
      bng.h
  4. 93 10
      png2bng.c

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 a.out
 a.out
 prime
 prime
 png2bng
 png2bng
+png2bng_test
 bngviewer
 bngviewer
 *.wasm
 *.wasm
 *.bng
 *.bng

+ 8 - 0
Makefile

@@ -14,3 +14,11 @@ bngviewer: bngviewer.c bng.h
 
 
 bng.wasm: bng.wat
 bng.wasm: bng.wat
 	wat2wasm bng.wat
 	wat2wasm bng.wat
+
+.PHONY: test
+
+test: png2bng_test
+	./png2bng_test
+
+png2bng_test: png2bng.c bng.h stb_image.h
+	$(CC) -O3 -DTEST -o png2bng_test png2bng.c -lm

+ 21 - 4
bng.h

@@ -9,15 +9,32 @@
 #  warning "Packed attributes for struct is not implemented for this compiler. Feel free to fix that and submit a Pull Request to https://github.com/tsoding/bng"
 #  warning "Packed attributes for struct is not implemented for this compiler. Feel free to fix that and submit a Pull Request to https://github.com/tsoding/bng"
 #endif
 #endif
 
 
-struct Bng
+struct Bng_Pixel_Format
 {
 {
-    uint32_t magic;
-    uint32_t width;
-    uint32_t height;
     uint8_t red_byte;           // 0
     uint8_t red_byte;           // 0
     uint8_t green_byte;         // 1
     uint8_t green_byte;         // 1
     uint8_t blue_byte;          // 2
     uint8_t blue_byte;          // 2
     uint8_t alpha_byte;         // 3
     uint8_t alpha_byte;         // 3
+} PACKED;
+
+int bng_pixel_format_equals(struct Bng_Pixel_Format a, struct Bng_Pixel_Format b)
+{
+    return a.red_byte == b.red_byte &&
+        a.green_byte == b.green_byte &&
+        a.blue_byte == b.blue_byte &&
+        a.alpha_byte == b.alpha_byte;
+}
+
+const struct Bng_Pixel_Format RGBA = {0, 1, 2, 3};
+const struct Bng_Pixel_Format ABGR = {3, 2, 1, 0};
+const struct Bng_Pixel_Format GRAB = {1, 0, 3, 2};
+
+struct Bng
+{
+    uint32_t magic;
+    uint32_t width;
+    uint32_t height;
+    struct Bng_Pixel_Format pixel_format;
     uint8_t data[];
     uint8_t data[];
 } PACKED;
 } PACKED;
 
 

+ 93 - 10
png2bng.c

@@ -2,30 +2,112 @@
 #include "./stb_image.h"
 #include "./stb_image.h"
 #include "./bng.h"
 #include "./bng.h"
 
 
+#define GET_BYTE(bytes, index) ((bytes & (0xFF << ((4 - index - 1) * 8))) >> ((4 - index - 1) * 8))
+#define SET_BYTE(bytes, index, byte) bytes = (bytes | (byte << ((4 - index - 1) * 8)))
+
+uint32_t convert_pixel(uint32_t pixel,
+                       struct Bng_Pixel_Format source,
+                       struct Bng_Pixel_Format desired)
+{
+    uint32_t result = 0;
+    SET_BYTE(result, desired.red_byte,   GET_BYTE(pixel, source.red_byte));
+    SET_BYTE(result, desired.green_byte, GET_BYTE(pixel, source.green_byte));
+    SET_BYTE(result, desired.blue_byte,  GET_BYTE(pixel, source.blue_byte));
+    SET_BYTE(result, desired.alpha_byte, GET_BYTE(pixel, source.alpha_byte));
+    return result;
+}
+
+struct Bng_Pixel_Format pixel_format_by_name(const char *name)
+{
+    struct Bng_Pixel_Format result = {0};
+    size_t n = strlen(name);
+    if (n > 4) n = 4;
+
+    for (size_t i = 0; i < n; ++i) {
+        switch (name[i]) {
+        case 'R':
+            result.red_byte = i;
+            break;
+        case 'G':
+            result.green_byte = i;
+            break;
+        case 'B':
+            result.blue_byte = i;
+            break;
+        case 'A':
+            result.alpha_byte = i;
+            break;
+        default: {}
+        }
+    }
+
+    return result;
+}
+
+#ifdef TEST
+int test_convert_pixel()
+{
+#define TEST_CASE(input, source, desired, expected)                 \
+    do {                                                            \
+        uint32_t actual = convert_pixel(input, source, desired);    \
+        if (actual != expected) {                                   \
+            printf("%s:%d: Oopsie-doopsie!\n", __FILE__, __LINE__); \
+            printf("Actual: %08X\n", actual);                       \
+            printf("Expected: %08X\n", expected);                   \
+            exit(1);                                                \
+        }                                                           \
+        printf("%08X == %08X, OK\n", actual, expected);             \
+    } while (0)
+
+    TEST_CASE(0xAABBCCDD, RGBA, ABGR, 0xDDCCBBAA);
+    TEST_CASE(0xAABBCCDD, ABGR, GRAB, 0xCCDDAABB);
+    TEST_CASE(0xAABBCCDD, RGBA, GRAB, 0xBBAADDCC);
+#undef TEST_CASE
+}
+
+int test_pixel_format_by_name()
+{
+    assert(bng_pixel_format_equals(pixel_format_by_name("RGBA"), RGBA));
+    assert(bng_pixel_format_equals(pixel_format_by_name("ABGR"), ABGR));
+    assert(bng_pixel_format_equals(pixel_format_by_name("GRAB"), GRAB));
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    test_convert_pixel();
+    test_pixel_format_by_name();
+    return 0;
+}
+
+#else
+
 int main(int argc, char **argv)
 int main(int argc, char **argv)
 {
 {
-    if (argc < 3) {
-        fprintf(stderr, "png2bng <input.png> <output.bng>\n");
+    if (argc < 4) {
+        fprintf(stderr, "png2bng <input.png> <output.bng> <pixel-format>\n");
         exit(1);
         exit(1);
     }
     }
 
 
     const char *input_filepath = argv[1];
     const char *input_filepath = argv[1];
     const char *output_filepath = argv[2];
     const char *output_filepath = argv[2];
+    struct Bng_Pixel_Format desired_format = pixel_format_by_name(argv[3]);
 
 
     int x, y, n;
     int x, y, n;
-    unsigned char *data = stbi_load(input_filepath, &x, &y, &n, 4);
+    uint32_t *data = (uint32_t*)stbi_load(input_filepath, &x, &y, &n, 4);
     assert(data != NULL);
     assert(data != NULL);
 
 
     struct Bng bng = {
     struct Bng bng = {
-        .magic      = BNG_MAGIC,
-        .width      = x,
-        .height     = y,
-        .red_byte   = 0,
-        .green_byte = 1,
-        .blue_byte  = 2,
-        .alpha_byte = 3,
+        .magic        = BNG_MAGIC,
+        .width        = x,
+        .height       = y,
+        .pixel_format = desired_format,
     };
     };
 
 
+    for (size_t i = 0; i < bng.width * bng.height; ++i) {
+        data[i] = convert_pixel(data[i], RGBA, desired_format);
+    }
+
     FILE *out = fopen(output_filepath, "wb");
     FILE *out = fopen(output_filepath, "wb");
     fwrite(&bng, 1, sizeof(bng), out);
     fwrite(&bng, 1, sizeof(bng), out);
     fwrite(data, 1, x * y * 4, out);
     fwrite(data, 1, x * y * 4, out);
@@ -33,3 +115,4 @@ int main(int argc, char **argv)
 
 
     return 0;
     return 0;
 }
 }
+#endif