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_data.cpp

      1 #include <vector>
      2 #include <string>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <set>
      6 #include <stdio.h>
      7 #include <fstream>
      8 #include <cstdint>
      9 #include <cstdlib>
     10 
     11 #include "lodepng.h"
     12 #include "mttile.h"
     13 #include "export.h"
     14 #include "map_attributes.h"
     15 #include "palettes.h"
     16 
     17 #include "cmp_int_color.h"
     18 #include "process_arguments.h"
     19 
     20 using namespace std;
     21 
     22 #include "png_image.h"
     23 #include "png2asset.h"
     24 #include "image_utils.h"
     25 #include "maps.h"
     26 #include "metasprites.h"
     27 #include "png_image.h"
     28 
     29 int decodePNG(vector<unsigned char>& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true);
     30 void loadFile(vector<unsigned char>& buffer, const std::string& filename);
     31 
     32 int ReadImageData_KeepPaletteOrder(  PNG2AssetData* assetData, string input_filename) {
     33 
     34     //load and decode png
     35     vector<unsigned char> buffer;
     36     lodepng::load_file(buffer, input_filename);
     37     lodepng::State state;
     38 
     39     //Calling with keep_palette_order means
     40         //-The image should be png indexed (1-8 bits per pixel)
     41         //-For CGB: Each 4 colors define a gbc palette, the first color is the transparent one
     42         //-Each rectangle with dimension(tile_w, tile_h) in the image has colors from one of those palettes only
     43     state.info_raw.colortype = LCT_PALETTE;
     44     state.info_raw.bitdepth = 8;
     45 
     46     // * Do *NOT* turn color_convert ON here.
     47     // When source PNG is indexed with bit depth was less than 8 bits per pixel then
     48     // color_convert may mangle the packed indexed values. Instead manually unpack them.
     49     //
     50     // This is necessary since some PNG encoders will use the minimum possible number of bits.
     51     //   For example 2 colors in the palette -> 1bpp -> 8 pixels per byte in the decoded image.
     52     //     Also see below about requirement to use palette from source image
     53     state.decoder.color_convert = false;
     54 
     55     // Clearing is needed to ensure loading the png works
     56     // (in cases where a previous png was loaded for source or entity tilesets)
     57     assetData->image.data.clear();
     58 
     59     unsigned error = lodepng::decode(assetData->image.data, assetData->image.w, assetData->image.h, state, buffer);
     60     if(error)
     61     {
     62         printf("decoder error %s\n", lodepng_error_text(error));
     63         return EXIT_FAILURE;
     64     }
     65 
     66     // Unpack the image if needed. Also checks and errors on incompatible palette type if needed
     67     if(!image_indexed_ensure_8bpp(assetData->image.data, (int)state.info_png.color.bitdepth, (int)state.info_png.color.colortype))
     68         return EXIT_FAILURE;
     69 
     70     // Use source image palette since lodepng conversion to indexed (LCT_PALETTE) won't create a palette
     71     // So: state->info_png.color.palette/size instead of state->info_raw.palette/size
     72     unsigned int palette_count = PaletteCountApplyMaxLimit((unsigned int)assetData->args->max_palettes, (unsigned int)((state.info_png.color.palettesize + assetData->image.colors_per_pal - 1) / assetData->image.colors_per_pal));
     73     assetData->image.total_color_count = palette_count * assetData->image.colors_per_pal;
     74     // Copy the palette data since the source buffer (state...) won't exist outside the scope of this function
     75     assetData->image.palette = new unsigned char[assetData->image.total_color_count * RGBA32_SZ];
     76     memcpy(assetData->image.palette, state.info_png.color.palette, assetData->image.total_color_count * RGBA32_SZ);
     77 
     78     // Save a copy of the palette data if it's the source palette (free first if already allocated)
     79     if (assetData->args->processing_mode == MODE_SOURCE_TILESET) {
     80         if (assetData->image.source_tileset_palette) free(assetData->image.source_tileset_palette);
     81         assetData->image.source_tileset_palette = new unsigned char[assetData->image.total_color_count * RGBA32_SZ];
     82         memcpy(assetData->image.source_tileset_palette, state.info_png.color.palette, assetData->image.total_color_count * RGBA32_SZ);
     83     }
     84 
     85 
     86     if(assetData->args->repair_indexed_pal)
     87         if(!image_indexed_repair_tile_palettes(assetData->image, assetData->args->use_2x2_map_attributes))
     88             return EXIT_FAILURE;
     89 
     90     // TODO: Enable dimension check
     91     // // Validate image dimensions
     92     // if (((image.w % tile_w) != 0) || ((image.h % tile_h) != 0))
     93     // {
     94     //     printf("Error: Image size %d x %d isn't an even multiple of tile size %d x %d\n", image.w, image.h, tile_w, tile_h);
     95     //     return EXIT_FAILURE;
     96     // }
     97 
     98     // Only check this for the main image, not for source tilesets
     99     if ((assetData->args->processing_mode == MODE_MAIN_IMAGE) && (assetData->args->has_source_tilesets)) {
    100 
    101         // Make sure these two values match when keeping palette order
    102         if(assetData->image.total_color_count != assetData->args->source_total_color_count) {
    103 
    104             printf("error: The number of color palettes for your source tileset (%d) and target image (%d) do not match.",
    105                    (unsigned int)assetData->args->source_total_color_count, (unsigned int)assetData->image.total_color_count);
    106             return EXIT_FAILURE;
    107         }
    108 
    109         size_t size = max(assetData->image.total_color_count, assetData->args->source_total_color_count);
    110         // size_t size = max(assetData->image.total_color_count, assetData->source_tileset_image.total_color_count);
    111 
    112         // Make sure these two values match when keeping palette order
    113         if (memcmp(assetData->image.palette, assetData->image.source_tileset_palette, size) != 0) {
    114 
    115             printf("error: The palettes for your source tileset and target image do not match.");
    116             return EXIT_FAILURE;
    117         }
    118     }
    119     return EXIT_SUCCESS;
    120 }
    121 
    122 int ReadImageData_Default(PNG2AssetData* assetData, string  input_filename) {
    123 
    124     //load and decode png
    125     vector<unsigned char> buffer;
    126     lodepng::load_file(buffer, input_filename);
    127     lodepng::State state;
    128 
    129     PNGImage image32;
    130     image32.colors_per_pal = assetData->image.colors_per_pal;
    131     image32.tile_w = assetData->image.tile_w;
    132     image32.tile_h = assetData->image.tile_h;
    133 
    134     unsigned error = lodepng::decode(image32.data, image32.w, image32.h, state, buffer); //decode as 32 bit
    135     if(error)
    136     {
    137         printf("decoder error %s\n", lodepng_error_text(error));
    138         return EXIT_FAILURE;
    139     }
    140 
    141     // Validate image dimensions
    142     if(((image32.w % image32.tile_w) != 0) || ((image32.h % image32.tile_h) != 0))
    143     {
    144         printf("Error: Image size %d x %d isn't an even multiple of tile size %d x %d\n", image32.w, image32.h, image32.tile_w, image32.tile_h);
    145         return EXIT_FAILURE;
    146     }
    147 
    148     int* palettes_per_tile = BuildPalettesAndAttributes(image32, assetData);
    149 
    150     // Create the indexed image
    151     // Clearing is needed to ensure loading the png works
    152     // (in cases where a previous png was loaded for source or entity tilesets)
    153     assetData->image.data.clear();
    154     assetData->image.w = image32.w;
    155     assetData->image.h = image32.h;
    156 
    157     unsigned int palette_count = PaletteCountApplyMaxLimit((unsigned int)assetData->args->max_palettes, (unsigned int)assetData->palettes.size());
    158 
    159     assetData->image.total_color_count = palette_count * assetData->image.colors_per_pal;
    160     assetData->image.palette = new unsigned char[palette_count * assetData->image.colors_per_pal * RGBA32_SZ]; // total color count * 4 bytes each
    161     // Pre-fill palette to all black. Prevents garbage in palette color slots that are unused (ex: only 3 colors when colors-per-pal is 4)
    162     memset(assetData->image.palette, 0, palette_count * assetData->image.colors_per_pal * RGBA32_SZ);
    163 
    164     // If using a source tileset and have more palettes than it defines
    165     // (only check for main image data, not source tilesets)
    166     if ((assetData->args->processing_mode == MODE_MAIN_IMAGE) && (assetData->args->has_source_tilesets)) {
    167         if (assetData->image.total_color_count > assetData->args->source_total_color_count) {
    168             printf("Found %d extra palette(s) for target tilemap.\n", (unsigned int)((assetData->image.total_color_count - assetData->args->source_total_color_count) / assetData->image.colors_per_pal));
    169         }
    170     }
    171     for(size_t p = 0; p < palette_count; ++p)
    172     {
    173         int* color_ptr = (int*)&assetData->image.palette[p * assetData->image.colors_per_pal * RGBA32_SZ];
    174 
    175         // When palettes[p].size() != image.colors_per_pal the unused colors are left as black (see memset above)
    176         for(SetPal::iterator it = assetData->palettes[p].begin(); it != assetData->palettes[p].end(); ++it, color_ptr++)
    177         {
    178             unsigned char* c = (unsigned char*)&(*it);
    179             *color_ptr = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
    180         }
    181     }
    182 
    183     for(size_t y = 0; y < image32.h; ++y)
    184     {
    185         for(size_t x = 0; x < image32.w; ++x)
    186         {
    187             unsigned char* c32ptr = &image32.data[(image32.w * y + x) * RGBA32_SZ];
    188             int color32 = (c32ptr[0] << 24) | (c32ptr[1] << 16) | (c32ptr[2] << 8) | c32ptr[3];
    189             unsigned char palette = palettes_per_tile[(y / image32.tile_h) * (image32.w / image32.tile_w) + (x / image32.tile_w)];
    190             unsigned char index = (unsigned char)std::distance(assetData->palettes[palette].begin(), assetData->palettes[palette].find(color32));
    191             assetData->image.data.push_back((palette << assetData->args->bpp) + index);
    192         }
    193     }
    194 
    195     //Test: output png to see how it looks
    196     //Export(image, "temp.png");
    197     return EXIT_SUCCESS;
    198 }
    199 
    200 int ReadImageData( PNG2AssetData* assetData, string  input_filename) {
    201 
    202     assetData->image.colors_per_pal = static_cast<size_t>(1) << assetData->args->bpp;
    203     // assetData->source_tileset_image.colors_per_pal = static_cast<size_t>(1) << assetData->args->bpp;
    204 
    205     if(assetData->args->export_as_map)
    206     {
    207         assetData->image.tile_w = 8; //Force tiles_w to 8 on maps
    208         assetData->image.tile_h = 8; //Force tiles_h to 8 on maps
    209         // assetData->source_tileset_image.tile_w = 8; //Force tiles_w to 8 on maps
    210         // assetData->source_tileset_image.tile_h = 8; //Force tiles_h to 8 on maps
    211         assetData->args->sprite_mode = SPR_NONE;
    212     }
    213 
    214 
    215     int errorCode = EXIT_SUCCESS;
    216 
    217     // Will the specified PNG have a palette provided with it?
    218     if (assetData->args->keep_palette_order) {
    219 
    220         // Save the error code
    221         errorCode = ReadImageData_KeepPaletteOrder(assetData, input_filename);
    222     }
    223     else
    224     {
    225 
    226         // Save the error code
    227         errorCode = ReadImageData_Default(assetData, input_filename);
    228     }
    229 
    230     if (errorCode != EXIT_SUCCESS) return errorCode;
    231 
    232 
    233     // Only set this data when processing the main image
    234     if (assetData->args->processing_mode == MODE_MAIN_IMAGE) {
    235         if(assetData->args->spriteSize.width == 0)    assetData->args->spriteSize.width = (int)assetData->image.w;
    236         if(assetData->args->spriteSize.height == 0)   assetData->args->spriteSize.height = (int)assetData->image.h;
    237         if(assetData->args->pivot.x == 0xFFFFFF)      assetData->args->pivot.x = (unsigned int)assetData->args->spriteSize.width / 2;
    238         if(assetData->args->pivot.y == 0xFFFFFF)      assetData->args->pivot.y = (unsigned int)assetData->args->spriteSize.height / 2;
    239         if(assetData->args->pivot.width == 0xFFFFFF)  assetData->args->pivot.width = assetData->args->spriteSize.width;
    240         if(assetData->args->pivot.height == 0xFFFFFF) assetData->args->pivot.height = assetData->args->spriteSize.height;
    241     }
    242     return EXIT_SUCCESS;
    243 }
    244 

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