Browse Source

Add RLE compression support to png2bng and bngviewer

rexim 5 years ago
parent
commit
c0ff544a1e
3 changed files with 69 additions and 16 deletions
  1. 46 0
      bng.h
  2. 18 14
      bngviewer.c
  3. 5 2
      png2bng.c

+ 46 - 0
bng.h

@@ -78,6 +78,22 @@ struct Bng_Pixel_Format pixel_format_by_name(const char *name)
     return result;
 }
 
+struct RLE_Pair
+{
+    uint32_t count;
+    uint32_t pixel;
+};
+
+struct RLE_Compressed_Bng
+{
+    uint32_t magic;
+    uint32_t width;
+    uint32_t height;
+    struct Bng_Pixel_Format pixel_format;
+    uint32_t pairs_count;
+    struct RLE_Pair pairs[];
+} PACKED;
+
 struct Bng
 {
     uint32_t magic;
@@ -87,6 +103,36 @@ struct Bng
     uint32_t pixels[];
 } PACKED;
 
+// NOTE: You have to allocate `width * height` amount of pixels
+void decompress_pixels(struct RLE_Pair *pairs, uint32_t pairs_count, uint32_t *pixels)
+{
+    size_t pixels_count = 0;
+    for (uint32_t i = 0; i < pairs_count; ++i) {
+        for (uint32_t j = 0; j < pairs[i].count; ++j) {
+            pixels[pixels_count++] = pairs[i].pixel;
+        }
+    }
+}
+
+// NOTE: You have to allocate `width * height` amount of pairs
+uint32_t compress_pixels(uint32_t width, uint32_t height, uint32_t *pixels, struct RLE_Pair *pairs)
+{
+    uint32_t pairs_count = 0;
+    const uint32_t n = width * height;
+
+    for (uint32_t i = 0; i < n; ++i) {
+        if (pairs_count > 0 && pairs[pairs_count - 1].pixel == pixels[i]) {
+            pairs[pairs_count - 1].count += 1;
+        } else {
+            pairs_count += 1;
+            pairs[pairs_count - 1].count = 1;
+            pairs[pairs_count - 1].pixel = pixels[i];
+        }
+    }
+
+    return pairs_count;
+}
+
 #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)))
 

+ 18 - 14
bngviewer.c

@@ -15,18 +15,21 @@ void* read_whole_file(const char *filename)
     return data;
 }
 
-struct Bng *bng_load(const char *filepath)
+uint32_t *bng_load(const char *filepath, uint32_t *width, uint32_t *height)
 {
-    struct Bng *bng = read_whole_file(filepath);
+    struct RLE_Compressed_Bng *compressed_bng = read_whole_file(filepath);
 
-    for (size_t i = 0; i < bng->width * bng->height; ++i) {
-        bng->pixels[i] = convert_pixel(
-            bng->pixels[i],
-            bng->pixel_format,
-            RGBA);
+    uint32_t *pixels = malloc(compressed_bng->width * compressed_bng->height * sizeof(uint32_t));
+    decompress_pixels(compressed_bng->pairs, compressed_bng->pairs_count, pixels);
+
+    for (size_t i = 0; i < compressed_bng->width * compressed_bng->height; ++i) {
+        pixels[i] = convert_pixel(pixels[i], compressed_bng->pixel_format, RGBA);
     }
 
-    return bng;
+    *width = compressed_bng->width;
+    *height = compressed_bng->height;
+
+    return pixels;
 }
 
 int main(int argc, char *argv[])
@@ -52,14 +55,15 @@ int main(int argc, char *argv[])
                 window, -1,
                 SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
 
-    struct Bng *bng = bng_load(input_filepath);
+    uint32_t width, height;
+    uint32_t *pixels = bng_load(input_filepath, &width, &height);
 
     SDL_Surface* image_surface =
-        SDL_CreateRGBSurfaceFrom(bng->pixels,
-                                 bng->width,
-                                 bng->height,
+        SDL_CreateRGBSurfaceFrom(pixels,
+                                 width,
+                                 height,
                                  32,
-                                 bng->width * 4,
+                                 width * 4,
                                  0x000000FF,
                                  0x0000FF00,
                                  0x00FF0000,
@@ -80,7 +84,7 @@ int main(int argc, char *argv[])
         SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
         SDL_RenderClear(renderer);
 
-        SDL_Rect rect = {0, 0, bng->width, bng->height};
+        SDL_Rect rect = {0, 0, width, height};
 
         SDL_RenderCopy(renderer, texture, &rect, &rect);
         SDL_RenderPresent(renderer);

+ 5 - 2
png2bng.c

@@ -17,7 +17,7 @@ int main(int argc, char **argv)
     uint32_t *data = (uint32_t*)stbi_load(input_filepath, &x, &y, &n, 4);
     assert(data != NULL);
 
-    struct Bng bng = {
+    struct RLE_Compressed_Bng bng = {
         .magic        = BNG_MAGIC,
         .width        = x,
         .height       = y,
@@ -28,9 +28,12 @@ int main(int argc, char **argv)
         data[i] = convert_pixel(data[i], RGBA, desired_format);
     }
 
+    struct RLE_Pair *pairs = malloc(sizeof(struct RLE_Pair) * bng.width * bng.height);
+    bng.pairs_count = compress_pixels(bng.width, bng.height, data, pairs);
+
     FILE *out = fopen(output_filepath, "wb");
     fwrite(&bng, 1, sizeof(bng), out);
-    fwrite(data, 1, x * y * 4, out);
+    fwrite(pairs, 1, bng.pairs_count * sizeof(struct RLE_Pair), out);
     fclose(out);
 
     return 0;