git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

gbdk-support/png2asset/image_utils.cpp

      1 
      2 // image_utils.cpp
      3 
      4 #include <vector>
      5 #include <stdio.h>
      6 #include <stdint.h>
      7 #include "lodepng.h"
      8 
      9 using namespace std;
     10 
     11 #include "png2asset.h"
     12 #include "image_utils.h"
     13 
     14 static void image_bitunpack(const vector<uint8_t> & src_image_data, vector<uint8_t> & unpacked_image_data, uint8_t bitdepth);
     15 
     16 static int pixel_get_palette_num(const PNGImage& image, int x, int y);
     17 static bool tile_palette_is_ok(const PNGImage& image, int x, int y, int tile_w, int tile_h);
     18 static int pixel_find_color_in_palette(const PNGImage& image, int x, int y, int pal_num, uint8_t * match_color_idx);
     19 static bool tile_palette_try_repair(PNGImage& image, int x, int y, int tile_w, int tile_h, int pal);
     20 static bool tile_palette_repair(PNGImage& image, int x, int y, int tile_w, int tile_h);
     21 
     22 
     23 // Un-bitpacks a source image into an 8-bit-per-pixel destination buffer
     24 // Acceptable range is 1-8 bits per pixel
     25 static void image_bitunpack(const vector<uint8_t> & src_image_data, vector<uint8_t> & unpacked_image_data, uint8_t bitdepth) {
     26 
     27     int pixels_per_byte = (8 / bitdepth);
     28 
     29     // Loop through all pixels in source image buffer
     30     for (const uint8_t & it_src: src_image_data) {
     31 
     32         // Unpack grouped pixel data
     33         uint8_t packed_bits = it_src;
     34         for (int b = 0; b < pixels_per_byte; b++) {
     35             unpacked_image_data.push_back(packed_bits >> (8 - bitdepth)); // Save extracted pixel bits
     36             packed_bits <<= bitdepth;                                     // Shift out the extracted bits
     37         }
     38     }
     39 }
     40 
     41 
     42 // Converts indexed image with bit depths 1- 8 bpp to indexed 8 bits per pixel
     43 // Replaces incoming image buffer with unpacked image buffer if successful
     44 bool image_indexed_ensure_8bpp(vector<uint8_t> & src_image_data, int bitdepth, int colortype) {
     45 
     46     vector<uint8_t> unpacked_image_data;
     47 
     48     if (colortype != LCT_PALETTE) {
     49         printf("png2asset: Error: keep_palette_order only works with indexed png images (pngtype: %d, bits:%d)\n",
     50                colortype, bitdepth);
     51         return false;
     52     }
     53     else if ((bitdepth < 1) || (bitdepth > 8)) {
     54         printf("png2asset: Error: Indexed mode PNG requires between 1 - 8 bits per pixel (pngtype: %d, bits:%d)\n",
     55                 colortype, bitdepth);
     56         return false;
     57     }
     58 
     59     image_bitunpack(src_image_data, unpacked_image_data, bitdepth);
     60 
     61     // Replace source image pixel data with unpacked pixels
     62     src_image_data.clear();
     63     for (const uint8_t & it_unpacked: unpacked_image_data)
     64         src_image_data.push_back(it_unpacked);
     65 
     66     return true;
     67 }
     68 
     69 
     70 
     71 
     72 // Returns the sub-palette number for a pixel in a indexed 8bpp image
     73 static int pixel_get_palette_num(const PNGImage& image, int x, int y) {
     74 
     75     return image.data[(y * image.w) + x] / (unsigned int)image.colors_per_pal;
     76 }
     77 
     78 
     79 // Verifies that a tile uses only colors within it's sub-palette in a indexed 8bpp image
     80 static bool tile_palette_is_ok(const PNGImage& image, int x, int y, int tile_w, int tile_h) {
     81 
     82     // Get palette from first pixel in tile, use that as reference
     83     int prev_palette_num = pixel_get_palette_num(image, x, y);
     84 
     85     for (int tile_y = 0; tile_y < tile_h; tile_y++) {
     86         for (int tile_x = 0; tile_x < tile_w; tile_x++) {
     87 
     88             // If any pixels have a different palette than it's a failure
     89             if (prev_palette_num != pixel_get_palette_num(image, x + tile_x, y + tile_y)) {
     90                 return false; // Return failure
     91             }
     92         }
     93     }
     94     return true; // Return success
     95 }
     96 
     97 
     98 // Finds a matching sub-palette RGB color for a pixel in a indexed 8bpp image
     99 static int pixel_find_color_in_palette(const PNGImage& image, int x, int y, int pal_num, uint8_t * match_color_idx) {
    100 
    101     // Get pixel palette index num, then get RGB value for it
    102     uint8_t pixel_pal_idx = image.data[(y * image.w) + x];
    103     uint8_t * p_pixel_color = &image.palette[pixel_pal_idx * RGBA32_SZ];
    104 
    105     // Loop through available palette colors to see if an exact match exists
    106     uint8_t * p_pal_color = &image.palette[pal_num * (image.colors_per_pal * RGBA32_SZ)];
    107     for (int c = 0; c < (int)image.colors_per_pal; c++, p_pal_color += RGBA32_SZ) {
    108 
    109         // Check palette entry RGB match against pixel RGB
    110         if ((p_pal_color[0] == p_pixel_color[0]) &&
    111             (p_pal_color[1] == p_pixel_color[1]) &&
    112             (p_pal_color[2] == p_pixel_color[2])) {
    113 
    114             *match_color_idx = (pal_num * (int)image.colors_per_pal) + c;
    115             return true; // Return success
    116         }
    117     }
    118 
    119     return false; // Return failure
    120 }
    121 
    122 
    123 // Tries to remap a tile to a new palette. Fails if any color in tile isn't in palette
    124 // Note: For any color that does match, tile color indexed gets remapped even if the rest
    125 //       of the match fails. That's ok because the new color is visually identical to previous.
    126 static bool tile_palette_try_repair(PNGImage& image, int x, int y, int tile_w, int tile_h, int pal_num) {
    127 
    128     uint8_t new_color;
    129 
    130     // For each pixel in the tile, try to find an exact color match in the palette
    131     for (int tile_y = 0; tile_y < tile_h; tile_y++) {
    132         for (int tile_x = 0; tile_x < tile_w; tile_x++) {
    133 
    134             // Update pixel if exact match was found
    135             // Otherwise quit as soon as possible to reduce processing
    136             if (pixel_find_color_in_palette(image, x + tile_x, y + tile_y, pal_num, &new_color))
    137                 image.data[((y + tile_y) * image.w) + x + tile_x] = new_color;
    138             else
    139                 return false; // Failed, tile color not present in palette
    140         }
    141     }
    142 
    143     // printf("  + png2asset: Info: repaired tile at %d x %d to palette make%d\n", x, y, pal_num);
    144     return true; // Success, found a perfect match
    145 }
    146 
    147 
    148 // Checks all palettes to see if the tile's colors can be remapped to them.
    149 static bool tile_palette_repair(PNGImage& image, int x, int y, int tile_w, int tile_h) {
    150 
    151     // Loop through all sub-palettes
    152     for (int pal_num = 0; pal_num < (int)image.total_color_count / (int)image.colors_per_pal; pal_num++)
    153         if (tile_palette_try_repair(image, x, y, tile_w, tile_h, pal_num))
    154             return true; // Success
    155 
    156     printf("png2asset: Error: tile (%dx%d) at %d,%d - %d,%d uses unique colors from different palettes, "
    157            "cannot repair palette\n", x / tile_w, y / tile_h, x, y, x + tile_w - 1, y + tile_h - 1);
    158 
    159     // If no exact palette match was found then signal failure
    160     return false; // Failure
    161 }
    162 
    163 
    164 // Tries to repair indexed color sub-palette usage per attribute tile region
    165 // Must find a palette that contains all colors in tile, fails otherwise
    166 //
    167 // Image passed in must be: indexed 8bpp
    168 bool image_indexed_repair_tile_palettes(PNGImage & image, bool use_2x2_map_attributes) {
    169 
    170     // TODO: Optional, but need to pass in vars
    171     // if ((colortype != LCT_PALETTE) || (bitdepth != 8)) {
    172     //     printf("png2asset: Error: repair_tile_palettes only works with indexed png images in 8 bits per pixel mode");
    173     //     return false;
    174     // }
    175 
    176     int tile_w = image.tile_w;
    177     int tile_h = image.tile_h;
    178 
    179     if (use_2x2_map_attributes) {
    180         tile_w *= 2;
    181         tile_h *= 2;
    182     }
    183 
    184     for (int y = 0; y < (int)image.h; y += tile_h) {
    185         for (int x = 0; x < (int)image.w; x += tile_w) {
    186             // If tile has no palette errors leave it as is
    187             // but if there are errors then try to repair it
    188             if (!tile_palette_is_ok(image, x, y, tile_w, tile_h)) {
    189                 if (!tile_palette_repair(image, x, y, tile_w, tile_h)) {
    190                     return false; // Return Failure
    191                 }
    192             }
    193         }
    194     }
    195 
    196     return true; // Return success
    197 }
    198 

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.