git.y1.nz

gbdk-2020

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

commit 390f40d36ad6a01b8592a2a6d5428fb73dcf8507
parent 61b99faf2a756d5a97aaf9a7c3d931e3ae466823
Author: bbbbbr <bbbbbr@users.noreply.github.com>
Date:   Thu, 26 Oct 2023 13:08:42 -0700

Merge pull request #576 from LaroldsJubilantJunkyard/png2asset-refractor

Png2asset refactor by @LaroldsJubilantJunkyard 
Diffstat:
Mgbdk-support/png2asset/Makefile2+-
Agbdk-support/png2asset/cmp_int_color.cpp42++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/cmp_int_color.h42++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/export.cpp483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/export.h12++++++++++++
Agbdk-support/png2asset/image_data.cpp220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/image_data.h11+++++++++++
Mgbdk-support/png2asset/image_utils.cpp2+-
Mgbdk-support/png2asset/lodepng.cpp6+++---
Agbdk-support/png2asset/main.cpp75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/map_attributes.cpp131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/map_attributes.h11+++++++++++
Agbdk-support/png2asset/maps.cpp96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/maps.h10++++++++++
Agbdk-support/png2asset/metasprites.cpp86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/metasprites.h8++++++++
Agbdk-support/png2asset/metasprites_functions.h9+++++++++
Agbdk-support/png2asset/mttile.h12++++++++++++
Agbdk-support/png2asset/palettes.cpp118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/palettes.h16++++++++++++++++
Mgbdk-support/png2asset/png2asset.cpp1570++++---------------------------------------------------------------------------
Mgbdk-support/png2asset/png2asset.h193+++++++++++--------------------------------------------------------------------
Agbdk-support/png2asset/png_image.h91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/process_arguments.cpp281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/process_arguments.h69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/rgb_to_nes_lut.h3+++
Agbdk-support/png2asset/tiles.cpp29+++++++++++++++++++++++++++++
Agbdk-support/png2asset/tiles.h97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/png2asset/vector2.h19+++++++++++++++++++
29 files changed, 2072 insertions(+), 1672 deletions(-)

diff --git a/gbdk-support/png2asset/Makefile b/gbdk-support/png2asset/Makefile @@ -41,7 +41,7 @@ endif TARGET = png2asset -SRCS := lodepng.cpp png2asset.cpp rgb_to_nes_lut.cpp image_utils.cpp +SRCS := cmp_int_color.cpp image_data.cpp lodepng.cpp map_attributes.cpp metasprites.cpp png2asset.cpp rgb_to_nes_lut.cpp export.cpp image_utils.cpp main.cpp maps.cpp palettes.cpp process_arguments.cpp tiles.cpp OBJS := $(SRCS:%.cpp=%.o) $(TARGET): $(OBJS) diff --git a/gbdk-support/png2asset/cmp_int_color.cpp b/gbdk-support/png2asset/cmp_int_color.cpp @@ -0,0 +1,41 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" + +#include "cmp_int_color.h" + +using namespace std; + +#include "png2asset.h" +#include "png_image.h" +#include "image_utils.h" + +SetPal GetPaletteColors(const PNGImage& image, int x, int y, int w, int h) +{ + SetPal ret; + for(int j = y; j < (y + h); ++j) + { + for(int i = x; i < (x + w); ++i) + { + const unsigned char* color = &image.data[(j * image.w + i) * RGBA32_SZ]; + int color_int = (color[0] << 24) | (color[1] << 16) | (color[2] << 8) | color[3]; + ret.insert(color_int); + } + } + + for(SetPal::iterator it = ret.begin(); it != ret.end(); ++it) + { + if(it != ret.begin() && ((0xFF & *it) != 0xFF)) //ret.begin() should be the only one transparent + printf("Warning: found more than one transparent color in tile at x:%d, y:%d of size w:%d, h:%d\n", x, y, w, h); + } + + return ret; +} + diff --git a/gbdk-support/png2asset/cmp_int_color.h b/gbdk-support/png2asset/cmp_int_color.h @@ -0,0 +1,41 @@ +#pragma once + +#include <set> +#include "png_image.h" + +using namespace std; + +//Functor to compare entries in SetPal +struct CmpIntColor { + bool operator() (unsigned int const& c1, unsigned int const& c2) const + { + unsigned char* c1_ptr = (unsigned char*)&c1; + unsigned char* c2_ptr = (unsigned char*)&c2; + + //Compare alpha first, transparent color is considered smaller + if(c1_ptr[0] != c2_ptr[0]) + { + return c1_ptr[0] < c2_ptr[0]; + } + else + { + // Do a compare with luminance in upper bits, and original rgb24 in lower bits. + // This prefers luminance, but considers RGB values for equal-luminance cases to + // make sure the compare functor satisifed the strictly weak ordering requirement + uint64_t lum_1 = (unsigned int)(c1_ptr[3] * 299 + c1_ptr[2] * 587 + c1_ptr[1] * 114); + uint64_t lum_2 = (unsigned int)(c2_ptr[3] * 299 + c2_ptr[2] * 587 + c2_ptr[1] * 114); + uint64_t rgb_1 = (c1_ptr[3] << 16) | (c1_ptr[2] << 8) | c1_ptr[1]; + uint64_t rgb_2 = (c2_ptr[3] << 16) | (c2_ptr[2] << 8) | c2_ptr[1]; + uint64_t all_1 = (lum_1 << 24) | rgb_1; + uint64_t all_2 = (lum_2 << 24) | rgb_2; + return all_1 > all_2; + } + } +}; + +//This set will keep colors in the palette ordered based on their grayscale values to ensure they look good on DMG +//This assumes the palette used in DMG will be 00 01 10 11 +typedef set< unsigned int, CmpIntColor > SetPal; + + +SetPal GetPaletteColors(const PNGImage& image, int x, int y, int w, int h); + diff --git a/gbdk-support/png2asset/export.cpp b/gbdk-support/png2asset/export.cpp @@ -0,0 +1,483 @@ +// This prevents windows build errors regarding the 'fopen' function +// Example: 'fopen': This function or variable may be unsafe.Consider using fopen_s instead.To disable deprecation, use _CRT_SECURE_NO_WARNINGS.See online help for details. +// More info: https://stackoverflow.com/questions/14386/fopen-deprecated-warning +// From bbbbbr: `Looks like some of the benefits are concurrency protection, but that probably isn't an issue for png2asset` +#ifdef _WIN32 +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" + +#include "cmp_int_color.h" +#include "metasprites.h" + +#include "png2asset.h" +#include "png_image.h" +#include "export.h" +#include "image_utils.h" +#include "process_arguments.h" +#include "rgb_to_nes_lut.h" + +using namespace std; + +bool export_h_file( PNG2AssetData* assetData) { + + FILE* file; + + file = fopen(assetData->args->output_filename_h.c_str(), "w"); + if(!file) { + printf("Error writing file: %s", assetData->args->output_filename_h.c_str()); + return false; + } + + fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n"); + fprintf(file, "#ifndef METASPRITE_%s_H\n", assetData->args->data_name.c_str()); + fprintf(file, "#define METASPRITE_%s_H\n", assetData->args->data_name.c_str()); + fprintf(file, "\n"); + fprintf(file, "#include <stdint.h>\n"); + fprintf(file, "#include <gbdk/platform.h>\n"); + fprintf(file, "#include <gbdk/metasprites.h>\n"); + fprintf(file, "\n"); + if(assetData->args->use_structs) + { + if(assetData->args->export_as_map) + { + fprintf(file, "#include \"TilesInfo.h\"\n"); + fprintf(file, "#include \"MapInfo.h\"\n"); + fprintf(file, "\n"); + fprintf(file, "extern const struct TilesInfo %s_tiles_info;\n", assetData->args->data_name.c_str()); + fprintf(file, "extern const struct MapInfo %s;\n", assetData->args->data_name.c_str()); + } + else + { + fprintf(file, "#include \"MetaSpriteInfo.h\"\n"); + fprintf(file, "\n"); + fprintf(file, "extern const struct MetaSpriteInfo %s;\n", assetData->args->data_name.c_str()); + } + } + else + { + fprintf(file, "#define %s_TILE_ORIGIN %d\n", assetData->args->data_name.c_str(), assetData->args->tile_origin); + fprintf(file, "#define %s_TILE_W %d\n", assetData->args->data_name.c_str(), assetData->image.tile_w); + fprintf(file, "#define %s_TILE_H %d\n", assetData->args->data_name.c_str(), assetData->image.tile_h); + fprintf(file, "#define %s_WIDTH %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->args->spriteSize.width); + fprintf(file, "#define %s_HEIGHT %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->args->spriteSize.height); + // The TILE_COUNT calc here is referring to number of 8x8 tiles, + // so the >> 3 for each sizes axis is to get a multiplier for larger hardware sprites such as 8x16 and 16x16 + fprintf(file, "#define %s_TILE_COUNT %d\n", assetData->args->data_name.c_str(), ((unsigned int)assetData->tiles.size() - assetData->args->source_tileset_size) * (assetData->image.tile_h >> 3) * (assetData->image.tile_w >> 3)); + if(assetData->args->include_palettes) { + fprintf(file, "#define %s_PALETTE_COUNT %d\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->image.total_color_count / assetData->image.colors_per_pal)); + fprintf(file, "#define %s_COLORS_PER_PALETTE %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->image.colors_per_pal); + fprintf(file, "#define %s_TOTAL_COLORS %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->image.total_color_count); + } + + if(assetData->args->includedMapOrMetaspriteData) { + + if(assetData->args->export_as_map) + { + fprintf(file, "#define %s_MAP_ATTRIBUTES ", assetData->args->data_name.c_str()); + if(assetData->args->use_map_attributes && assetData->map_attributes.size()) + fprintf(file, "%s_map_attributes\n", assetData->args->data_name.c_str()); + else + fprintf(file, "0\n"); + + if(assetData->args->use_map_attributes) + { + int scale = assetData->args->use_2x2_map_attributes ? 2 : 1; + fprintf(file, "#define %s_MAP_ATTRIBUTES_WIDTH %d\n", assetData->args->data_name.c_str(), (int)(scale * assetData->args->map_attributes_size.width)); + fprintf(file, "#define %s_MAP_ATTRIBUTES_HEIGHT %d\n", assetData->args->data_name.c_str(), (int)(scale * assetData->args->map_attributes_size.height)); + fprintf(file, "#define %s_MAP_ATTRIBUTES_PACKED_WIDTH %d\n", assetData->args->data_name.c_str(), (int)assetData->args->map_attributes_packed_size.width); + fprintf(file, "#define %s_MAP_ATTRIBUTES_PACKED_HEIGHT %d\n", assetData->args->data_name.c_str(), (int)assetData->args->map_attributes_packed_size.height); + } + + if(assetData->args->use_structs) + { + fprintf(file, "#define %s_TILE_PALS ", assetData->args->data_name.c_str()); + if(assetData->args->use_map_attributes) + fprintf(file, "0\n"); + else + fprintf(file, "%s_tile_pals\n", assetData->args->data_name.c_str()); + } + } + else + { + fprintf(file, "#define %s_PIVOT_X %d\n", assetData->args->data_name.c_str(), assetData->args->pivot.x); + fprintf(file, "#define %s_PIVOT_Y %d\n", assetData->args->data_name.c_str(), assetData->args->pivot.y); + fprintf(file, "#define %s_PIVOT_W %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->args->pivot.width); + fprintf(file, "#define %s_PIVOT_H %d\n", assetData->args->data_name.c_str(), (unsigned int)assetData->args->pivot.height); + } + } + fprintf(file, "\n"); + fprintf(file, "BANKREF_EXTERN(%s)\n", assetData->args->data_name.c_str()); + fprintf(file, "\n"); + + // If we are not using a source tileset, or if we have extra palettes defined + if(assetData->args->include_palettes && (assetData->image.total_color_count - assetData->args->source_total_color_count > 0 || assetData->args->source_tilesets.size()==0)) { + fprintf(file, "extern const palette_color_t %s_palettes[%d];\n", assetData->args->data_name.c_str(), (unsigned int)assetData->image.total_color_count - (unsigned int)assetData->args->source_total_color_count); + } + if(assetData->args->includeTileData) { + fprintf(file, "extern const uint8_t %s_tiles[%d];\n", assetData->args->data_name.c_str(), (unsigned int)((assetData->tiles.size() - assetData->args->source_tileset_size) * (assetData->image.tile_w * assetData->image.tile_h * assetData->args->bpp / 8))); + } + + fprintf(file, "\n"); + if(assetData->args->includedMapOrMetaspriteData) { + if(assetData->args->export_as_map) + { + fprintf(file, "extern const unsigned char %s_map[%d];\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map).size()); + + if(assetData->args->use_map_attributes && assetData->map_attributes.size()) { + fprintf(file, "extern const unsigned char %s_map_attributes[%d];\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map_attributes).size()); + } + else + { + // Some platforms (like SMS/GG) encode attributes as part of map + // For compatibility, add a define that makes _map_attributes equal _map, + // so that set_bkg_attributes can work the same on these platforms + fprintf(file, "#define %s_map_attributes %s_map\n", assetData->args->data_name.c_str(), assetData->args->data_name.c_str()); + } + if(!assetData->args->use_map_attributes && (assetData->args->includeTileData) && (assetData->args->use_structs)) { + fprintf(file, "extern const unsigned char* %s_tile_pals[%d];\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->tiles).size()); + } + } + else + { + fprintf(file, "extern const metasprite_t* const %s_metasprites[%d];\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->sprites.size())); + } + } + } + fprintf(file, "\n"); + fprintf(file, "#endif\n"); + + + fclose(file); + // END: Output .h FILE + + return true; // success +} + + + +bool export_c_file( PNG2AssetData* assetData) { + + FILE* file; + + file = fopen(assetData->args->output_filename.c_str(), "w"); + if(!file) { + printf("Error writing file: %s", assetData->args->output_filename.c_str()); + return false; + } + + if(assetData->args->bank >= 0) fprintf(file, "#pragma bank %d\n\n", assetData->args->bank); + + fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n\n"); + + fprintf(file, "#include <stdint.h>\n"); + fprintf(file, "#include <gbdk/platform.h>\n"); + fprintf(file, "#include <gbdk/metasprites.h>\n"); + fprintf(file, "\n"); + + fprintf(file, "BANKREF(%s)\n\n", assetData->args->data_name.c_str()); + + // Are we not using a source tileset, or do we have extra colors + if(assetData->args->include_palettes && (assetData->image.total_color_count - assetData->args->source_total_color_count > 0 || !assetData->args->source_tilesets.size() == 0)) { + + // Subtract however many palettes we had in the source tileset + fprintf(file, "const palette_color_t %s_palettes[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)assetData->image.total_color_count - (unsigned int)assetData->args->source_total_color_count); + + // Offset by however many palettes we had in the source tileset + for(size_t i = assetData->args->source_total_color_count / assetData->image.colors_per_pal; i < assetData->image.total_color_count / assetData->image.colors_per_pal; ++i) + { + if(i != 0) + fprintf(file, ",\n"); + fprintf(file, "\t"); + + unsigned char* pal_ptr = &assetData->image.palette[i * (assetData->image.colors_per_pal * RGBA32_SZ)]; + for(int c = 0; c < (int)assetData->image.colors_per_pal; ++c, pal_ptr += RGBA32_SZ) + { + size_t rgb222 = (((pal_ptr[2] >> 6) & 0x3) << 4) | + (((pal_ptr[1] >> 6) & 0x3) << 2) | + (((pal_ptr[0] >> 6) & 0x3) << 0); + if(assetData->args->convert_rgb_to_nes) { + fprintf(file, "0x%0X", rgb_to_nes[rgb222]); + } + else + fprintf(file, "RGB8(%3d,%3d,%3d)", pal_ptr[0], pal_ptr[1], pal_ptr[2]); + if(c != (int)assetData->image.colors_per_pal - 1) + fprintf(file, ", "); + // Line break every 4 color entries, to keep line width down + if(((c + 1) % 4) == 0) + fprintf(file, "\n\t"); + } + } + fprintf(file, "\n};\n"); + } + + if(assetData->args->includeTileData) { + fprintf(file, "\n"); + fprintf(file, "const uint8_t %s_tiles[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)((assetData->tiles.size() - assetData->args->source_tileset_size) * assetData->image.tile_w * assetData->image.tile_h * assetData->args->bpp / 8)); + fprintf(file, "\t"); + for(vector< Tile >::iterator it = assetData->tiles.begin() + assetData->args->source_tileset_size; it != assetData->tiles.end(); ++it) + { + + int line_break = 1; // Start with 1 to prevent line break on first iteration + vector< unsigned char > packed_data = (*it).GetPackedData(assetData->args->pack_mode, assetData->image.tile_w, assetData->image.tile_h, assetData->args->bpp); + for(vector< unsigned char >::iterator it2 = packed_data.begin(); it2 != packed_data.end(); ++it2) + { + fprintf(file, "0x%02x", (*it2)); + if((it + 1) != assetData->tiles.end() || (it2 + 1) != packed_data.end()) + fprintf(file, ","); + // Add a line break after each 8x8 tile + if(((line_break++) % (8 / assetData->args->bpp)) == 0) + fprintf(file, "\n\t"); + } + + if((!assetData->args->export_as_map) && (it != assetData->tiles.end())) + fprintf(file, "\n"); + } + fprintf(file, "};\n\n"); + } + + if(assetData->args->includedMapOrMetaspriteData) { + + if(!assetData->args->export_as_map) + { + for(vector< MetaSprite >::iterator it = assetData->sprites.begin(); it != assetData->sprites.end(); ++it) + { + fprintf(file, "const metasprite_t %s_metasprite%d[] = {\n", assetData->args->data_name.c_str(), (int)(it - assetData->sprites.begin())); + for(MetaSprite::iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) + { + int pal_idx = (*it2).props & 0xF; + int flip_x = ((*it2).props >> 5) & 1; + int flip_y = ((*it2).props >> 6) & 1; + fprintf(file, + "\tMETASPR_ITEM(%d, %d, %d, S_PAL(%d)%s%s),\n", + (*it2).offset_y, + (*it2).offset_x, + (*it2).offset_idx, + pal_idx, + flip_x ? " | S_FLIPX" : "", + flip_y ? " | S_FLIPY" : ""); + } + fprintf(file, "\tMETASPR_TERM\n"); + fprintf(file, "};\n\n"); + } + + fprintf(file, "const metasprite_t* const %s_metasprites[%d] = {\n\t", assetData->args->data_name.c_str(), (unsigned int)(assetData->sprites.size())); + for(vector< MetaSprite >::iterator it = assetData->sprites.begin(); it != assetData->sprites.end(); ++it) + { + fprintf(file, "%s_metasprite%d", assetData->args->data_name.c_str(), (int)(it - assetData->sprites.begin())); + if(it + 1 != assetData->sprites.end()) + fprintf(file, ", "); + } + fprintf(file, "\n};\n"); + + if(assetData->args->use_structs) + { + fprintf(file, "\n"); + fprintf(file, "#include \"MetaSpriteInfo.h\"\n"); + fprintf(file, "const struct MetaSpriteInfo %s = {\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //width\n", (unsigned int)assetData->args->pivot.width); + fprintf(file, "\t%d, //height\n", (unsigned int)assetData->args->pivot.height); + fprintf(file, "\t%d, //num tiles\n", (unsigned int)assetData->tiles.size() * (assetData->image.tile_h >> 3)); + fprintf(file, "\t%s_tiles, //tiles\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //num palettes\n", (unsigned int)(assetData->image.total_color_count / assetData->image.colors_per_pal)); + fprintf(file, "\t%s_palettes, //CGB palette\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //num sprites\n", (unsigned int)assetData->sprites.size()); + fprintf(file, "\t%s_metasprites, //metasprites\n", assetData->args->data_name.c_str()); + fprintf(file, "};\n"); + } + } + else + { + if(assetData->args->includeTileData) { + if(assetData->args->use_structs) + { + //Export tiles pals (if any) + if(!assetData->args->use_map_attributes) + { + fprintf(file, "\n"); + fprintf(file, "const uint8_t %s_tile_pals[%d] = {\n\t", assetData->args->data_name.c_str(), (unsigned int)(assetData->tiles.size()) - assetData->args->source_tileset_size); + for(vector< Tile >::iterator it = assetData->tiles.begin() + assetData->args->source_tileset_size; it != assetData->tiles.end(); ++it) + { + if(it != assetData->tiles.begin()) + fprintf(file, ", "); + fprintf(file, "%d", it->pal); + } + fprintf(file, "\n};\n"); + } + //Export Tiles Info + fprintf(file, "\n"); + fprintf(file, "#include \"TilesInfo.h\"\n"); + fprintf(file, "BANKREF(%s_tiles_info)\n", assetData->args->data_name.c_str()); + fprintf(file, "const struct TilesInfo %s_tiles_info = {\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //num tiles\n", (unsigned int)assetData->tiles.size() * (assetData->image.tile_h >> 3)); + fprintf(file, "\t%s_tiles, //tiles\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //num palettes\n", (unsigned int)(assetData->image.total_color_count / assetData->image.colors_per_pal)); + fprintf(file, "\t%s_palettes, //palettes\n", assetData->args->data_name.c_str()); + if(!assetData->args->use_map_attributes) + fprintf(file, "\t%s_tile_pals, //tile palettes\n", assetData->args->data_name.c_str()); + else + fprintf(file, "\t0 //tile palettes\n"); + fprintf(file, "};\n"); + } + } + + //Export map + fprintf(file, "\n"); + fprintf(file, "const unsigned char %s_map[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map.size())); + size_t line_size = assetData->map.size() / (assetData->image.h / 8); + if(assetData->args->output_transposed) { + + for(size_t i = 0; i < line_size; ++i) + { + fprintf(file, "\t"); + for(size_t j = 0; j < assetData->image.h / 8; ++j) + { + fprintf(file, "0x%02x,", assetData->map[j * line_size + i]); + } + fprintf(file, "\n"); + } + } + else { + + for(size_t j = 0; j < assetData->image.h / 8; ++j) + { + fprintf(file, "\t"); + for(size_t i = 0; i < line_size; ++i) + { + fprintf(file, "0x%02x,", assetData->map[j * line_size + i]); + } + fprintf(file, "\n"); + } + } + fprintf(file, "};\n"); + + + //Export map attributes (if any) + if(assetData->args->use_map_attributes && assetData->map_attributes.size()) + { + fprintf(file, "\n"); + fprintf(file, "const unsigned char %s_map_attributes[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map_attributes.size())); + if(assetData->args->output_transposed) { + for(size_t i = 0; i < assetData->args->map_attributes_packed_size.width; ++i) + { + fprintf(file, "\t"); + for(size_t j = 0; j < assetData->args->map_attributes_packed_size.height; ++j) + { + fprintf(file, "0x%02x,", assetData->map_attributes[j * assetData->args->map_attributes_packed_size.width + i]); + } + fprintf(file, "\n"); + } + } + else { + for(size_t j = 0; j < assetData->args->map_attributes_packed_size.height; ++j) + { + fprintf(file, "\t"); + for(size_t i = 0; i < assetData->args->map_attributes_packed_size.width; ++i) + { + fprintf(file, "0x%02x,", assetData->map_attributes[j * assetData->args->map_attributes_packed_size.width + i]); + } + fprintf(file, "\n"); + } + } + + fprintf(file, "};\n"); + } + + if(assetData->args->use_structs) + { + //Export Map Info + fprintf(file, "\n"); + fprintf(file, "#include \"MapInfo.h\"\n"); + fprintf(file, "BANKREF_EXTERN(%s_tiles_info)\n", assetData->args->data_name.c_str()); + fprintf(file, "const struct MapInfo %s = {\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%s_map, //map\n", assetData->args->data_name.c_str()); + fprintf(file, "\t%d, //with\n", assetData->image.w >> 3); + fprintf(file, "\t%d, //height\n", assetData->image.h >> 3); + if(assetData->args->use_map_attributes && assetData->map_attributes.size()) + fprintf(file, "\t%s_map_attributes, //map attributes\n", assetData->args->data_name.c_str()); + else + fprintf(file, "\t%s, //map attributes\n", "0"); + fprintf(file, "\tBANK(%s_tiles_info), //tiles bank\n", assetData->args->data_name.c_str()); + fprintf(file, "\t&%s_tiles_info, //tiles info\n", assetData->args->data_name.c_str()); + fprintf(file, "};\n"); + } + } + } + + fclose(file); + + return true; // success +} + + +bool export_map_binary( PNG2AssetData* assetData) { + + std::ofstream mapBinaryFile, mapAttributesBinaryfile, tilesBinaryFile; + mapBinaryFile.open(assetData->args->output_filename_bin, std::ios_base::binary); + tilesBinaryFile.open(assetData->args->output_filename_tiles_bin, std::ios_base::binary); + + for(vector< Tile >::iterator it = assetData->tiles.begin() + assetData->args->source_tileset_size; it != assetData->tiles.end(); ++it) + { + + vector< unsigned char > packed_data = (*it).GetPackedData(assetData->args->pack_mode, assetData->image.tile_w, assetData->image.tile_h, assetData->args->bpp); + for(vector< unsigned char >::iterator it2 = packed_data.begin(); it2 != packed_data.end(); ++it2) + { + + const char chars[] = { (const char)(*it2) }; + tilesBinaryFile.write(chars, 1); + } + + } + + + // Open our file for writing attributes if specified + if(assetData->args->use_map_attributes)mapAttributesBinaryfile.open(assetData->args->output_filename_attributes_bin, std::ios_base::binary); + + int columns = assetData->image.w >> 3; + int rows = assetData->image.h >> 3; + + // If we want the values to be column-by-column + if(assetData->args->output_transposed) { + + // Swap the column/row for loops + for(int column = 0; column < columns; column++) { + for(int row = 0; row < rows; ++row) { + + int tile = column + row * columns; + + const char mapChars[] = { (const char)assetData->map[tile] }; + + // Write map items column-by-column + mapBinaryFile.write(mapChars, 1); + if(assetData->args->use_map_attributes) { + const char mapAttributeChars[] = { (const char)assetData->map_attributes[tile] }; + mapAttributesBinaryfile.write(mapAttributeChars, 1); + } + } + } + } + else { + + // Write the arrays as-is, row-by-row + mapBinaryFile.write((const char*)(&assetData->map[0]), rows * columns); + if(assetData->args->use_map_attributes)mapAttributesBinaryfile.write((const char*)(&assetData->map_attributes[0]), rows * columns); + } + + // Finalize the files + mapBinaryFile.close(); + tilesBinaryFile.close(); + if(assetData->args->use_map_attributes)mapAttributesBinaryfile.close(); + + return true; // success +} diff --git a/gbdk-support/png2asset/export.h b/gbdk-support/png2asset/export.h @@ -0,0 +1,12 @@ +#pragma once + + + +#include "process_arguments.h" +#include "png2asset.h" + +using namespace std; + +bool export_h_file( PNG2AssetData* assetData); +bool export_c_file( PNG2AssetData* assetData); +bool export_map_binary( PNG2AssetData* assetData); diff --git a/gbdk-support/png2asset/image_data.cpp b/gbdk-support/png2asset/image_data.cpp @@ -0,0 +1,220 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" +#include "export.h" +#include "map_attributes.h" +#include "palettes.h" + +#include "cmp_int_color.h" +#include "process_arguments.h" + +using namespace std; + +#include "png_image.h" +#include "png2asset.h" +#include "image_utils.h" +#include "maps.h" +#include "metasprites.h" +#include "png_image.h" + +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); +void loadFile(vector<unsigned char>& buffer, const std::string& filename); + +int ReadImageData_KeepPaletteOrder( PNG2AssetData* assetData, string input_filename) { + + //load and decode png + vector<unsigned char> buffer; + lodepng::load_file(buffer, input_filename); + lodepng::State state; + + + //Calling with keep_palette_order means + //-The image should be png indexed (1-8 bits per pixel) + //-For CGB: Each 4 colors define a gbc palette, the first color is the transparent one + //-Each rectangle with dimension(tile_w, tile_h) in the image has colors from one of those palettes only + state.info_raw.colortype = LCT_PALETTE; + state.info_raw.bitdepth = 8; + + // * Do *NOT* turn color_convert ON here. + // When source PNG is indexed with bit depth was less than 8 bits per pixel then + // color_convert may mangle the packed indexed values. Instead manually unpack them. + // + // This is necessary since some PNG encoders will use the minimum possible number of bits. + // For example 2 colors in the palette -> 1bpp -> 8 pixels per byte in the decoded image. + // Also see below about requirement to use palette from source image + state.decoder.color_convert = false; + + unsigned error = lodepng::decode(assetData->image.data, assetData->image.w, assetData->image.h, state, buffer); + // Unpack the image if needed. Also checks and errors on incompatible palette type if needed + if(!image_indexed_ensure_8bpp(assetData->image.data, assetData->image.w, assetData->image.h, (int)state.info_png.color.bitdepth, (int)state.info_png.color.colortype)) + return 1; + else if(error) { + printf("decoder error %s\n", lodepng_error_text(error)); + return 1; + } + + // Use source image palette since lodepng conversion to indexed (LCT_PALETTE) won't create a palette + // So: state->info_png.color.palette/size instead of state->info_raw.palette/size + unsigned int palette_count = PaletteCountApplyMaxLimit((unsigned int)assetData->args->max_palettes, (unsigned int)(state.info_png.color.palettesize / assetData->image.colors_per_pal)); + assetData->image.total_color_count = palette_count * assetData->image.colors_per_pal; + // Copy the palette data since the source buffer (state...) won't exist outside the scope of this function + assetData->image.palette = new unsigned char[assetData->image.total_color_count * RGBA32_SZ]; + memcpy(assetData->image.palette, state.info_png.color.palette, assetData->image.total_color_count * RGBA32_SZ); + + + + if(assetData->args->repair_indexed_pal) + if(!image_indexed_repair_tile_palettes(assetData->image, assetData->args->use_2x2_map_attributes)) + return 1; + + // TODO: Enable dimension check + // // Validate image dimensions + // if (((image.w % tile_w) != 0) || ((image.h % tile_h) != 0)) + // { + // 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); + // return 1; + // } + + if(assetData->args->source_tilesets.size()>0) { + + // Make sure these two values match when keeping palette order + if(assetData->image.total_color_count != assetData->source_tileset_image.total_color_count) { + + printf("error: The number of color palette's for your source tileset (%d) and target image (%d) do not match.", (unsigned int)assetData->source_tileset_image.total_color_count, (unsigned int)assetData->image.total_color_count); + return 1; + } + + size_t size = max(assetData->image.total_color_count, assetData->source_tileset_image.total_color_count); + + // Make sure these two values match when keeping palette order + if(memcmp(assetData->image.palette, assetData->source_tileset_image.palette, size) != 0) { + + printf("error: The palette's for your source tileset and target image do not match."); + return 1; + } + } + return 0; +} + +int ReadImageData_Default(PNG2AssetData* assetData, string input_filename) { + + + + //load and decode png + vector<unsigned char> buffer; + lodepng::load_file(buffer, assetData->args->input_filename); + lodepng::State state; + + PNGImage image32; + image32.colors_per_pal = assetData->image.colors_per_pal; + image32.tile_w = assetData->image.tile_w; + image32.tile_h = assetData->image.tile_h; + + unsigned error = lodepng::decode(image32.data, image32.w, image32.h, state, buffer); //decode as 32 bit + if(error) + { + printf("decoder error %s\n", lodepng_error_text(error)); + return 1; + } + + // Validate image dimensions + if(((image32.w % image32.tile_w) != 0) || ((image32.h % image32.tile_h) != 0)) + { + 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); + return 1; + } + + int* palettes_per_tile = BuildPalettesAndAttributes(image32, assetData->palettes, assetData->args->use_2x2_map_attributes); + + //Create the indexed image + assetData->image.data.clear(); + assetData->image.w = image32.w; + assetData->image.h = image32.h; + + unsigned int palette_count = PaletteCountApplyMaxLimit((unsigned int)assetData->args->max_palettes, (unsigned int)assetData->palettes.size()); + + assetData->image.total_color_count = palette_count * assetData->image.colors_per_pal; + assetData->image.palette = new unsigned char[palette_count * assetData->image.colors_per_pal * RGBA32_SZ]; // total color count * 4 bytes each + + // If we are using a sourcetileset and have more palettes than it defines + if(assetData->args->source_tilesets.size() > 0 && (assetData->image.total_color_count > assetData->args->source_total_color_count)) { + 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)); + } + for(size_t p = 0; p < palette_count; ++p) + { + int* color_ptr = (int*)&assetData->image.palette[p * assetData->image.colors_per_pal * RGBA32_SZ]; + + //TODO: if palettes[p].size() != image.colors_per_pal we should probably try to fill the gaps based on grayscale values + + for(SetPal::iterator it = assetData->palettes[p].begin(); it != assetData->palettes[p].end(); ++it, color_ptr++) + { + unsigned char* c = (unsigned char*)&(*it); + *color_ptr = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; + } + } + + for(size_t y = 0; y < image32.h; ++y) + { + for(size_t x = 0; x < image32.w; ++x) + { + unsigned char* c32ptr = &image32.data[(image32.w * y + x) * RGBA32_SZ]; + int color32 = (c32ptr[0] << 24) | (c32ptr[1] << 16) | (c32ptr[2] << 8) | c32ptr[3]; + unsigned char palette = palettes_per_tile[(y / image32.tile_h) * (image32.w / image32.tile_w) + (x / image32.tile_w)]; + unsigned char index = (unsigned char)std::distance(assetData->palettes[palette].begin(), assetData->palettes[palette].find(color32)); + assetData->image.data.push_back((palette << assetData->args->bpp) + index); + } + } + + //Test: output png to see how it looks + //Export(image, "temp.png"); + return 0; +} + +int ReadImageData( PNG2AssetData* assetData, string input_filename) { + + assetData->image.colors_per_pal = static_cast<size_t>(1) << assetData->args->bpp; + + if(assetData->args->export_as_map) + { + assetData->image.tile_w = 8; //Force tiles_w to 8 on maps + assetData->image.tile_h = 8; //Force tiles_h to 8 on maps + assetData->args->sprite_mode = SPR_NONE; + } + + + int errorCode = 0; + + // Will the specified PNG have a palette provided with it? + if(assetData->args->keep_palette_order) { + + // Save the error code + errorCode= ReadImageData_KeepPaletteOrder(assetData, input_filename); + } + else + { + + // Save the error code + errorCode= ReadImageData_Default(assetData, input_filename); + } + + if(errorCode != 0)return errorCode; + + + if(assetData->args->spriteSize.width == 0) assetData->args->spriteSize.width = (int)assetData->image.w; + if(assetData->args->spriteSize.height == 0) assetData->args->spriteSize.height = (int)assetData->image.h; + if(assetData->args->pivot.x == 0xFFFFFF) assetData->args->pivot.x = (unsigned int)assetData->args->spriteSize.width / 2; + if(assetData->args->pivot.y == 0xFFFFFF) assetData->args->pivot.y = (unsigned int)assetData->args->spriteSize.height / 2; + if(assetData->args->pivot.width == 0xFFFFFF) assetData->args->pivot.width = assetData->args->spriteSize.width; + if(assetData->args->pivot.height == 0xFFFFFF) assetData->args->pivot.height = assetData->args->spriteSize.height; + + return 0; +} + diff --git a/gbdk-support/png2asset/image_data.h b/gbdk-support/png2asset/image_data.h @@ -0,0 +1,11 @@ +#pragma once +#include <string> +#include "cmp_int_color.h" +#include "process_arguments.h" + + +using namespace std; + + +int ReadImageData( PNG2AssetData* assetData, string input_filename); + diff --git a/gbdk-support/png2asset/image_utils.cpp b/gbdk-support/png2asset/image_utils.cpp @@ -72,7 +72,7 @@ bool image_indexed_ensure_8bpp(vector<uint8_t> & src_image_data, int width, int // Returns the sub-palette number for a pixel in a indexed 8bpp image static int pixel_get_palette_num(const PNGImage& image, int x, int y) { - return image.data[(y * image.w) + x] / image.colors_per_pal; + return image.data[(y * image.w) + x] / (unsigned int)image.colors_per_pal; } diff --git a/gbdk-support/png2asset/lodepng.cpp b/gbdk-support/png2asset/lodepng.cpp @@ -709,7 +709,7 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { size = headsize; for(i = 0; i < headsize; ++i) { unsigned l = maxlens[i]; - if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); + if(l > FIRSTBITS) size += (static_cast<size_t>(1) << (l - FIRSTBITS)); } tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len)); tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value)); @@ -728,7 +728,7 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { if(l <= FIRSTBITS) continue; tree->table_len[i] = l; tree->table_value[i] = pointer; - pointer += (1u << (l - FIRSTBITS)); + pointer += (static_cast<size_t>(1) << (l - FIRSTBITS)); } lodepng_free(maxlens); @@ -5471,7 +5471,7 @@ static size_t ilog2i(size_t i) { l = ilog2(i); /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u) linearly approximates the missing fractional part multiplied by i */ - return i * l + ((i - (1u << l)) << 1u); + return i * l + ((i - (static_cast<size_t>(1) << l)) << static_cast<size_t>(1)); } static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, diff --git a/gbdk-support/png2asset/main.cpp b/gbdk-support/png2asset/main.cpp @@ -0,0 +1,74 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" +#include "export.h" +#include "map_attributes.h" +#include "palettes.h" + +#include "cmp_int_color.h" + +#include "png2asset.h" +#include "image_utils.h" +#include "image_data.h" +#include "maps.h" +#include "metasprites.h" +#include "metasprites_functions.h" +#include "process_arguments.h" +#include "png_image.h" +#include "tiles.h" + +using namespace std; + +int main(int argc, char* argv[]) +{ + int errorCode = 0; + + // Read all arguments + PNG2AssetArguments arguments; + + // Make sure we had no errors + if((errorCode = processPNG2AssetArguments(argc, argv, &arguments)) != 0) { + return errorCode; + } + + PNG2AssetData png2AssetInstance; + + // If we have a source tilest + if(arguments.source_tilesets.size() > 0) { + + vector<string>::iterator sourceTilesetsIterator = arguments.source_tilesets.begin(); + + // Iterate through each source tileset and execute + for(sourceTilesetsIterator; sourceTilesetsIterator < arguments.source_tilesets.end(); sourceTilesetsIterator++) { + + // Run with our source tileset filename + errorCode = png2AssetInstance.Execute(&arguments, *sourceTilesetsIterator); + + // Return the error code if the function returns non-zero + if(errorCode != 0) { + return errorCode; + } + + } + + // Save these values for later usage on the main execution + arguments.source_tileset_size = (unsigned int)png2AssetInstance.tiles.size(); + arguments.source_total_color_count = png2AssetInstance.image.total_color_count; + } + + // Run the primary input file + // Return the error code if the function returns non-zero + if((errorCode = png2AssetInstance.Execute(&arguments, arguments.input_filename)) != 0) { + return errorCode; + } + + return png2AssetInstance.Export(); +} + diff --git a/gbdk-support/png2asset/map_attributes.cpp b/gbdk-support/png2asset/map_attributes.cpp @@ -0,0 +1,130 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" + +#include "cmp_int_color.h" + +using namespace std; + +#include "png2asset.h" +#include "image_utils.h" +#include "process_arguments.h" + +unsigned char GetMapAttribute(size_t x, size_t y,PNG2AssetData* assetData) +{ + if(x < assetData->args->map_attributes_size.width && y < assetData->args->map_attributes_size.height) + return assetData->map_attributes[y * assetData->args->map_attributes_size.width + x]; + else + return 0; +} + +void ReduceMapAttributes2x2(const vector< SetPal >& palettes,PNG2AssetData* assetData) +{ + size_t w = (assetData->args->map_attributes_size.width + 1) / 2; + size_t h = (assetData->args->map_attributes_size.height + 1) / 2; + vector< unsigned char > map_attributes_2x2; + map_attributes_2x2.resize(w * h); + for(size_t y = 0; y < h; y++) + { + for(size_t x = 0; x < w; x++) + { + // Use only Top-left attribute, ignoring the other three as they should now be identical + map_attributes_2x2[y * w + x] = GetMapAttribute(2 * x, 2 * y,assetData); + } + } + // Overwrite old attributes + assetData->args->map_attributes_size.width = w; + assetData->args->map_attributes_size.height = h; + assetData->map_attributes = map_attributes_2x2; +} + +// +// Aligns map attribute data to be aligned properly for NES and set_bkg_submap_attributes +// Namely: +// * Width aligned to multiples of 2 to reflect the NES's packed attribute table +// * Every 16th row is blank to reflect the unused row in the NES's packed attribute table +// +void AlignMapAttributes(PNG2AssetData* assetData) +{ + const size_t ATTRIBUTE_HEIGHT = 15; + const size_t ATTRIBUTE_ALIGNED_HEIGHT = 16; + vector< unsigned char > map_attributes_aligned; + size_t map_attributes_aligned_width = 2 * ((assetData->args->map_attributes_size.width + 1) / 2); + size_t num_vertical_nametables = (assetData->args->map_attributes_size.height + ATTRIBUTE_HEIGHT - 1) / ATTRIBUTE_HEIGHT; + map_attributes_aligned.resize(map_attributes_aligned_width * (num_vertical_nametables * ATTRIBUTE_ALIGNED_HEIGHT)); + for(size_t i = 0; i < num_vertical_nametables; i++) + { + bool last_nametable = (i == num_vertical_nametables - 1); + size_t height = last_nametable ? (assetData->args->map_attributes_size.height - i * ATTRIBUTE_HEIGHT) : ATTRIBUTE_HEIGHT; + for(size_t y = 0; y < height; y++) + { + for(size_t x = 0; x < assetData->args->map_attributes_size.width; x++) + { + map_attributes_aligned[(i * ATTRIBUTE_ALIGNED_HEIGHT + y) * map_attributes_aligned_width + x] = + assetData->map_attributes[(i * ATTRIBUTE_HEIGHT + y) * assetData->args->map_attributes_size.width + x]; + } + } + } + // Overwrite old attributes + assetData->args->map_attributes_size.width = map_attributes_aligned_width; + assetData->args->map_attributes_size.height = num_vertical_nametables * ATTRIBUTE_ALIGNED_HEIGHT; + assetData->map_attributes = map_attributes_aligned; +} + +// +// Pack map attributes +// (NES packs multiple 2-bit entries into one byte) +// +void PackMapAttributes(PNG2AssetData *assetData) +{ + vector< unsigned char > map_attributes_packed; + assetData->args->map_attributes_packed_size.width = (assetData->args->map_attributes_size.width + 1) / 2; + assetData->args->map_attributes_packed_size.height = (assetData->args->map_attributes_size.height + 1) / 2; + map_attributes_packed.resize(assetData->args->map_attributes_packed_size.width * assetData->args->map_attributes_packed_size.height); + for(size_t y = 0; y < assetData->args->map_attributes_packed_size.height; y++) + { + for(size_t x = 0; x < assetData->args->map_attributes_packed_size.width; x++) + { + unsigned char a_tl = GetMapAttribute(2 * x + 0, 2 * y + 0, assetData); + unsigned char a_tr = GetMapAttribute(2 * x + 1, 2 * y + 0, assetData); + unsigned char a_bl = GetMapAttribute(2 * x + 0, 2 * y + 1, assetData); + unsigned char a_br = GetMapAttribute(2 * x + 1, 2 * y + 1, assetData); + unsigned char packed_bits = (a_br << 6) | (a_bl << 4) | (a_tr << 2) | (a_tl << 0); + map_attributes_packed[assetData->args->map_attributes_packed_size.width * y + x] = packed_bits; + } + } + // Overwrite old attributes + assetData->map_attributes = map_attributes_packed; +} + +void HandleMapAttributes(PNG2AssetData* assetData) { + assetData->args->map_attributes_size.width = assetData->image.w / 8; + assetData->args->map_attributes_size.height = assetData->image.h / 8; + + // Optionally perform 2x2 reduction on attributes (NES attribute table has this format) + if(assetData->args->use_2x2_map_attributes) + { + // NES attribute map dimensions are half-resolution + ReduceMapAttributes2x2(assetData->palettes, assetData); + } + // Optionally align and pack map attributes into NES PPU format + if(assetData->args->pack_map_attributes) + { + AlignMapAttributes(assetData); + PackMapAttributes(assetData); + } + else + { + // Use original attribute dimensions for packed + assetData->args->map_attributes_packed_size.width = assetData->args->map_attributes_size.width; + assetData->args->map_attributes_packed_size.height = assetData->args->map_attributes_size.height; + } +} + diff --git a/gbdk-support/png2asset/map_attributes.h b/gbdk-support/png2asset/map_attributes.h @@ -0,0 +1,10 @@ +#pragma once +#include <vector> +#include "process_arguments.h" +#include "cmp_int_color.h" +using namespace std; +unsigned char GetMapAttribute(size_t x, size_t y); +void ReduceMapAttributes2x2(const vector< SetPal >& palettes); +void AlignMapAttributes(PNG2AssetData* assetData); +void PackMapAttributes(PNG2AssetData* assetData); +void HandleMapAttributes( PNG2AssetData* assetData); + diff --git a/gbdk-support/png2asset/maps.cpp b/gbdk-support/png2asset/maps.cpp @@ -0,0 +1,96 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" +#include "export.h" + +#include "cmp_int_color.h" +#include "tiles.h" + +using namespace std; + +#include "png2asset.h" +#include "image_utils.h" +#include "map_attributes.h" +#include "process_arguments.h" + + +void GetMap( PNG2AssetData* assetData) +{ + for(int y = 0; y < (int)assetData->image.h; y += assetData->image.tile_h) + { + for(int x = 0; x < (int)assetData->image.w; x += assetData->image.tile_w) + { + Tile tile(assetData->image.tile_w * assetData->image.tile_h); + assetData->image.ExtractTile(x, y, tile, assetData->args->sprite_mode, assetData->args->export_as_map, assetData->args->use_map_attributes); + + size_t idx; + unsigned char props; + + if(assetData->args->keep_duplicate_tiles) + { + assetData->tiles.push_back(tile); + idx = assetData->tiles.size() - 1; + props = assetData->args->props_default; + } + else + { + if(!FindTile(tile, idx, props,assetData)) + { + if(assetData->args->source_tilesets.size() > 0) { + printf("found a tile not in the source tileset at %d,%d. The target tileset has %d extra tiles.\n", x, y, (unsigned int)assetData->args->extra_tile_count + 1); + assetData->args->extra_tile_count++; + assetData->args->includeTileData = true; + } + assetData->tiles.push_back(tile); + idx = assetData->tiles.size() - 1; + props = assetData->args->props_default; + + if(assetData->tiles.size() > 256 && assetData->args->pack_mode != Tile::SMS) + printf("Warning: found more than 256 tiles on x:%d,y:%d\n", x, y); + + if(((assetData->tiles.size() + assetData->args->tile_origin) > 256) && (assetData->args->pack_mode != Tile::SMS)) + printf("Warning: tile count (%d) + tile origin (%d) exceeds 256 at x:%d,y:%d\n", (unsigned int)assetData->tiles.size(), assetData->args->tile_origin, x, y); + } + } + + assetData->map.push_back((unsigned char)idx + assetData->args->tile_origin); + + if(assetData->args->use_map_attributes) + { + unsigned char pal_idx = assetData->image.data[y * assetData->image.w + x] >> assetData->args->bpp; //We can pick the palette from the first pixel of this tile + if(assetData->args->pack_mode == Tile::SGB) + { + props = props << 1; //Mirror flags in SGB are on bit 7 + props |= (pal_idx + 4) << 2; //Pals are in bits 2,3,4 and need to go from 4 to 7 + assetData->map.push_back(props); //Also they are stored within the map tiles + } + else if(assetData->args->pack_mode == Tile::SMS) + { + props = props >> 4; + if(idx > 255) + props |= 1; + assetData->map.push_back(props); + } + else + { + props |= pal_idx; + assetData->map_attributes.push_back(props); + } + } + + } + } + + + + HandleMapAttributes( assetData); +} + diff --git a/gbdk-support/png2asset/maps.h b/gbdk-support/png2asset/maps.h @@ -0,0 +1,9 @@ +#pragma once + +#include <vector> +#include "cmp_int_color.h" +#include "process_arguments.h" +using namespace std; + + +void GetMap( PNG2AssetData* assetData); + diff --git a/gbdk-support/png2asset/metasprites.cpp b/gbdk-support/png2asset/metasprites.cpp @@ -0,0 +1,86 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <fstream> +#include <cstdint> + +#include "mttile.h" + +#include "cmp_int_color.h" +#include "metasprites.h" +#include "tiles.h" + +#include "png2asset.h" +#include "image_utils.h" +#include "process_arguments.h" + +using namespace std; + +void GetMetaSprite(int _x, int _y, int _w, int _h, int pivot_x, int pivot_y, PNG2AssetData* assetData) +{ + int last_x = _x + pivot_x; + int last_y = _y + pivot_y; + + assetData->sprites.push_back(MetaSprite()); + MetaSprite& mt_sprite = assetData->sprites.back(); + for(int y = _y; y < _y + _h && y < (int)assetData->image.h; y += assetData->image.tile_h) + { + for(int x = _x; x < _x + _w && x < (int)assetData->image.w; x += assetData->image.tile_w) + { + Tile tile(assetData->image.tile_h * assetData->image.tile_w); + if(assetData->image.ExtractTile(x, y, tile, assetData->args->sprite_mode, assetData->args->export_as_map, assetData->args->use_map_attributes)) + { + size_t idx; + unsigned char props; + unsigned char pal_idx = assetData->image.data[y * assetData->image.w + x] >> 2; //We can pick the palette from the first pixel of this tile + + if(assetData->args->keep_duplicate_tiles) + { + assetData->tiles.push_back(tile); + idx = assetData->tiles.size() - 1; + props = assetData->args->props_default; + } + else + { + if(!FindTile(tile, idx, props, assetData)) + { + if(assetData->args->source_tilesets.size() > 0) { + printf("found a tile not in the source tileset at %d,%d. The target tileset has %d extra tiles.\n", x, y, (unsigned int)assetData->args->extra_tile_count + 1); + assetData->args->extra_tile_count++; + assetData->args->includeTileData = true; + } + assetData->tiles.push_back(tile); + idx = assetData->tiles.size() - 1; + props = assetData->args->props_default; + } + } + + props |= pal_idx; + + // Scale up index based on 8x8 tiles-per-hardware sprite + if(assetData->args->sprite_mode == SPR_8x16) + idx *= 2; + else if(assetData->args->sprite_mode == SPR_16x16_MSX) + idx *= 4; + + mt_sprite.push_back(MTTile(x - last_x, y - last_y, (unsigned char)idx, props)); + last_x = x; + last_y = y; + } + } + } +} + + +void GetAllMetasprites(PNG2AssetData* assetData) { + //Extract metasprites + for(int y = 0; y < (int)assetData->image.h; y += (unsigned int)assetData->args->spriteSize.height) + { + for(int x = 0; x < (int)assetData->image.w; x += (unsigned int)assetData->args->spriteSize.width) + { + GetMetaSprite(x, y, (unsigned int)assetData->args->spriteSize.width, (unsigned int)assetData->args->spriteSize.height, assetData->args->pivot.x, assetData->args->pivot.y, assetData); + } + } +} diff --git a/gbdk-support/png2asset/metasprites.h b/gbdk-support/png2asset/metasprites.h @@ -0,0 +1,8 @@ +#pragma once +#include <vector> +#include "mttile.h" + + +using namespace std; + +typedef vector< MTTile > MetaSprite; diff --git a/gbdk-support/png2asset/metasprites_functions.h b/gbdk-support/png2asset/metasprites_functions.h @@ -0,0 +1,8 @@ +#pragma once +#include <vector> +#include "metasprites.h" +#include "process_arguments.h" + + +void GetAllMetasprites(PNG2AssetData* assetData); +void GetMetaSprite(int _x, int _y, int _w, int _h, int pivot_x, int pivot_y, PNG2AssetData* assetData); + diff --git a/gbdk-support/png2asset/mttile.h b/gbdk-support/png2asset/mttile.h @@ -0,0 +1,12 @@ +#pragma once +struct MTTile +{ + char offset_x; + char offset_y; + unsigned char offset_idx; + unsigned char props; + + MTTile(char offset_x, char offset_y, unsigned char offset_idx, unsigned char props) : offset_x(offset_x), offset_y(offset_y), offset_idx(offset_idx), props(props) {} + MTTile() : offset_x(0), offset_y(0), offset_idx(0), props(0) {} +}; + diff --git a/gbdk-support/png2asset/palettes.cpp b/gbdk-support/png2asset/palettes.cpp @@ -0,0 +1,117 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" +#include "export.h" +#include "map_attributes.h" +#include "palettes.h" + +#include "cmp_int_color.h" + +using namespace std; + +#include "png2asset.h" +#include "image_utils.h" +#include "maps.h" +#include "metasprites.h" + + +unsigned int PaletteCountApplyMaxLimit(unsigned int max_palettes, unsigned int cur_palette_size) +{ + if(cur_palette_size > max_palettes) + { + printf("Warning: %d palettes found, truncating to %d (-max_palettes)\n", (unsigned int)cur_palette_size, (unsigned int)max_palettes); + return max_palettes; + } + else + return cur_palette_size; +} + +int FindOrCreateSubPalette(const SetPal& pal, vector< SetPal >& palettes, size_t colors_per_pal) +{ + // Return -1 if colors can't even fit in sub-palette hardware limit + if(pal.size() > colors_per_pal) + { + return -1; + } + //Check if it matches any palettes or create a new one + int i; + for(i = 0; i < (int)palettes.size(); ++i) + { + //Try to merge this palette with any of the palettes (checking if they are equal is not enough since the palettes can have less than 4 colors) + SetPal merged(palettes[i]); + merged.insert(pal.begin(), pal.end()); + if(merged.size() <= colors_per_pal) + { + if(palettes[i].size() <= colors_per_pal) + palettes[i] = merged; //Increase colors with this palette (it has less than 4 colors) + return i; //Found palette + } + } + + if(i == (int)palettes.size()) + { + //Palette not found, add a new one + palettes.push_back(pal); + } + return i; +} + + + +// +// Builds palettes and palette-per-tile (attributes) for an image +// +// Inputs: +// image32 : Input image in RGBA32 format +// palettes : Palettes for image. Will be added-to / merged with new palettes. +// half_resolution : If true, attributes will follow a constraint of every 2x2 cell having the same attribute. +// +// Returns: array of attributes. This always has *per-tile* dimensions, even when half_resolution is true. +// +int* BuildPalettesAndAttributes(const PNGImage& image32, vector< SetPal >& palettes, bool half_resolution) +{ + int* palettes_per_tile = new int[(image32.w / image32.tile_w) * (image32.h / image32.tile_h)]; + int sx = half_resolution ? 2 : 1; + int sy = half_resolution ? 2 : 1; + for(unsigned int y = 0; y < image32.h; y += image32.tile_h * sy) + { + for(unsigned int x = 0; x < image32.w; x += image32.tile_w * sx) + { + //Get palette colors on (x, y, image32.tile_w, image32.tile_h) + SetPal pal = GetPaletteColors(image32, (x / sx) * sx, (y / sy) * sy, sx * image32.tile_w, sy * image32.tile_h); + + int subPalIndex = FindOrCreateSubPalette(pal, palettes, image32.colors_per_pal); + if(subPalIndex < 0) + { + printf("Error: more than %d colors found in tile at x:%d, y:%d of size w:%d, h:%d\n", + (unsigned int)image32.colors_per_pal, + (x / sx) * sx, + (y / sy) * sy, + sx * image32.tile_w, + sy * image32.tile_h); + subPalIndex = 0; // Force to sub-palette 0, to allow getting a partially-incorrect output image + } + // Assign single or multiple entries in palettes_per_tile, to keep it independent of + // of half_resolution parameter + int dx = ((x / image32.tile_w) / sx) * sx; + int dy = ((y / image32.tile_h) / sy) * sy; + int w = (image32.w / image32.tile_w); + for(int yy = 0; yy < sy; yy++) + { + for(int xx = 0; xx < sx; xx++) + { + palettes_per_tile[(dy + yy) * w + dx + xx] = subPalIndex; + } + } + } + } + return palettes_per_tile; +} + diff --git a/gbdk-support/png2asset/palettes.h b/gbdk-support/png2asset/palettes.h @@ -0,0 +1,15 @@ +#pragma once +#include <vector> + +#include "cmp_int_color.h" +#include "png2asset.h" + +using namespace std; + + +unsigned int PaletteCountApplyMaxLimit(unsigned int max_palettes, unsigned int cur_palette_size); + +int FindOrCreateSubPalette(const SetPal& pal, vector< SetPal >& palettes, size_t colors_per_pal); + + +int* BuildPalettesAndAttributes(const PNGImage& image32, vector< SetPal >& palettes, bool half_resolution); + diff --git a/gbdk-support/png2asset/png2asset.cpp b/gbdk-support/png2asset/png2asset.cpp @@ -8,1548 +8,117 @@ #include <cstdint> #include "lodepng.h" +#include "mttile.h" +#include "export.h" +#include "map_attributes.h" +#include "palettes.h" -using namespace std; +#include "cmp_int_color.h" #include "png2asset.h" #include "image_utils.h" +#include "image_data.h" +#include "maps.h" +#include "metasprites.h" +#include "metasprites_functions.h" +#include "process_arguments.h" +#include "png_image.h" +#include "tiles.h" -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); -void loadFile(vector<unsigned char>& buffer, const std::string& filename); - -bool export_map_binary(); -bool export_h_file(void); -bool export_c_file(void); - -// TODO: Moved these vars to global scope (from giant main()) as start of breaking main into functions -// They should get encapsulated - string output_filename_h; - string output_filename_bin; - string output_filename_attributes_bin; - string output_filename_tiles_bin; - string data_name; - //default values for some params - int sprite_w = 0; - int sprite_h = 0; - int pivot_x = 0xFFFFFF; - int pivot_y = 0xFFFFFF; - int pivot_w = 0xFFFFFF; - int pivot_h = 0xFFFFFF; - string output_filename; - int bank = -1; - bool keep_palette_order = false; - bool repair_indexed_pal = false; - bool output_binary = false; - bool output_transposed = false; - size_t max_palettes = 8; - - bool export_as_map = false; - bool use_map_attributes = false; - bool use_2x2_map_attributes = false; - bool pack_map_attributes = false; - bool convert_rgb_to_nes = false; - size_t map_attributes_width = 0; - size_t map_attributes_height = 0; - size_t map_attributes_packed_width = 0; - size_t map_attributes_packed_height = 0; - int sprite_mode = SPR_8x16; - - int bpp = 2; - unsigned int tile_origin = 0; // Default to no tile index offset - -extern unsigned char rgb_to_nes[64]; - - -struct MTTile -{ - char offset_x; - char offset_y; - unsigned char offset_idx; - unsigned char props; - - MTTile(char offset_x, char offset_y, unsigned char offset_idx, unsigned char props) : offset_x(offset_x), offset_y(offset_y), offset_idx(offset_idx), props(props) {} - MTTile() : offset_x(0), offset_y(0), offset_idx(0), props(0) {} -}; -string source_tileset; -unsigned int extra_tile_count = 0; -unsigned int source_total_color_count = 0; // Total number of colors (palette_count x colors_per_palette) -unsigned int source_tileset_size = 0; -bool includeTileData = true; -bool includedMapOrMetaspriteData = true; -PNGImage source_tileset_image; -bool use_source_tileset = false; -bool keep_duplicate_tiles = false; -bool include_palettes = true; - -typedef vector< MTTile > MetaSprite; -vector< Tile > tiles; -vector< MetaSprite > sprites; -vector< unsigned char > map; -vector< unsigned char > map_attributes; -PNGImage image; -int props_default = 0x00; // Default Sprite props has no attributes enabled -bool use_structs = false; -bool flip_tiles = true; -Tile::PackMode pack_mode = Tile::GB; - - -// TODO: Move into tiles.cpp -Tile FlipH(const Tile& tile) -{ - Tile ret; - for(int j = (int)tile.data.size() - 8; j >= 0; j -= 8) - { - for(int i = 0; i < 8; ++ i) - { - ret.data.push_back(tile.data[j + i]); - } - } - ret.pal = tile.pal; - return ret; -} - -Tile FlipV(const Tile& tile) -{ - Tile ret; - for(int j = 0; j < (int)tile.data.size(); j += 8) - { - for(int i = 7; i >= 0; -- i) - { - ret.data.push_back(tile.data[j + i]); - } - } - ret.pal = tile.pal; - return ret; -} - -bool FindTile(const Tile& t, size_t& idx, unsigned char& props) -{ - vector< Tile >::iterator it; - it = find(tiles.begin(), tiles.end(), t); - if(it != tiles.end()) - { - idx = (size_t)(it - tiles.begin()); - props = props_default; - return true; - } - - if(flip_tiles) - { - Tile tile = FlipV(t); - it = find(tiles.begin(), tiles.end(), tile); - if(it != tiles.end()) - { - idx = (size_t)(it - tiles.begin()); - props = props_default | (1 << 5); - return true; - } - - tile = FlipH(tile); - it = find(tiles.begin(), tiles.end(), tile); - if(it != tiles.end()) - { - idx = (size_t)(it - tiles.begin()); - props = props_default | (1 << 5) | (1 << 6); - return true; - } - - tile = FlipV(tile); - it = find(tiles.begin(), tiles.end(), tile); - if(it != tiles.end()) - { - idx = (size_t)(it - tiles.begin()); - props = props_default | (1 << 6); - return true; - } - } - - return false; -} - -void GetMetaSprite(int _x, int _y, int _w, int _h, int pivot_x, int pivot_y) -{ - int last_x = _x + pivot_x; - int last_y = _y + pivot_y; - - sprites.push_back(MetaSprite()); - MetaSprite& mt_sprite = sprites.back(); - for(int y = _y; y < _y + _h && y < (int)image.h; y += image.tile_h) - { - for(int x = _x; x < _x + _w && x < (int)image.w; x += image.tile_w) - { - Tile tile(image.tile_h * image.tile_w); - if (image.ExtractTile(x, y, tile, sprite_mode, export_as_map, use_map_attributes)) - { - size_t idx; - unsigned char props; - unsigned char pal_idx = image.data[y * image.w + x] >> 2; //We can pick the palette from the first pixel of this tile - - if((!use_source_tileset) && keep_duplicate_tiles) - { - tiles.push_back(tile); - idx = tiles.size() - 1; - props = props_default; - } - else - { - if(!FindTile(tile, idx, props)) - { - if (use_source_tileset) { - printf("found a tile not in the source tileset at %d,%d. The target tileset has %d extra tiles.\n", x, y,extra_tile_count+1); - extra_tile_count++; - includeTileData = true; - } - tiles.push_back(tile); - idx = tiles.size() - 1; - props = props_default; - } - } - - props |= pal_idx; - - // Scale up index based on 8x8 tiles-per-hardware sprite - if (sprite_mode == SPR_8x16) - idx *= 2; - else if (sprite_mode == SPR_16x16_MSX) - idx *= 4; - - mt_sprite.push_back(MTTile(x - last_x, y - last_y, (unsigned char)idx, props)); - last_x = x; - last_y = y; - } - } - } -} - -void GetMap() -{ - for(int y = 0; y < (int)image.h; y += image.tile_h) - { - for(int x = 0; x < (int)image.w; x += image.tile_w) - { - Tile tile(image.tile_w * image.tile_h); - image.ExtractTile(x, y, tile, sprite_mode, export_as_map, use_map_attributes); - - size_t idx; - unsigned char props; - - if((!use_source_tileset) && keep_duplicate_tiles) - { - tiles.push_back(tile); - idx = tiles.size() - 1; - props = props_default; - } - else - { - if(!FindTile(tile, idx, props)) - { - if (use_source_tileset) { - printf("found a tile not in the source tileset at %d,%d. The target tileset has %d extra tiles.\n", x, y, extra_tile_count + 1); - extra_tile_count++; - includeTileData = true; - } - tiles.push_back(tile); - idx = tiles.size() - 1; - props = props_default; - - if(tiles.size() > 256 && pack_mode != Tile::SMS) - printf("Warning: found more than 256 tiles on x:%d,y:%d\n", x, y); - - if(((tiles.size() + tile_origin) > 256) && (pack_mode != Tile::SMS)) - printf("Warning: tile count (%d) + tile origin (%d) exceeds 256 at x:%d,y:%d\n", (unsigned int)tiles.size(), tile_origin, x, y); - } - } - - map.push_back((unsigned char)idx + tile_origin); - - if(use_map_attributes) - { - unsigned char pal_idx = image.data[y * image.w + x] >> bpp; //We can pick the palette from the first pixel of this tile - if(pack_mode == Tile::SGB) - { - props = props << 1; //Mirror flags in SGB are on bit 7 - props |= (pal_idx + 4) << 2; //Pals are in bits 2,3,4 and need to go from 4 to 7 - map.push_back(props); //Also they are stored within the map tiles - } - else if(pack_mode == Tile::SMS) - { - props = props >> 4; - if(idx > 255) - props |= 1; - map.push_back(props); - } - else - { - props |= pal_idx; - map_attributes.push_back(props); - } - } - - } - } -} -//Functor to compare entries in SetPal -struct CmpIntColor { - bool operator() (unsigned int const& c1, unsigned int const& c2) const - { - unsigned char* c1_ptr = (unsigned char*)&c1; - unsigned char* c2_ptr = (unsigned char*)&c2; - - //Compare alpha first, transparent color is considered smaller - if(c1_ptr[0] != c2_ptr[0]) - { - return c1_ptr[0] < c2_ptr[0]; - } - else - { - // Do a compare with luminance in upper bits, and original rgb24 in lower bits. - // This prefers luminance, but considers RGB values for equal-luminance cases to - // make sure the compare functor satisifed the strictly weak ordering requirement - uint64_t lum_1 = (unsigned int)(c1_ptr[3] * 299 + c1_ptr[2] * 587 + c1_ptr[1] * 114); - uint64_t lum_2 = (unsigned int)(c2_ptr[3] * 299 + c2_ptr[2] * 587 + c2_ptr[1] * 114); - uint64_t rgb_1 = (c1_ptr[3] << 16) | (c1_ptr[2] << 8) | c1_ptr[1]; - uint64_t rgb_2 = (c2_ptr[3] << 16) | (c2_ptr[2] << 8) | c2_ptr[1]; - uint64_t all_1 = (lum_1 << 24) | rgb_1; - uint64_t all_2 = (lum_2 << 24) | rgb_2; - return all_1 > all_2; - } - } -}; - -//This set will keep colors in the palette ordered based on their grayscale values to ensure they look good on DMG -//This assumes the palette used in DMG will be 00 01 10 11 -typedef set< unsigned int, CmpIntColor > SetPal; - -SetPal GetPaletteColors(const PNGImage& image, int x, int y, int w, int h) -{ - SetPal ret; - for(int j = y; j < (y + h); ++ j) - { - for(int i = x; i < (x + w); ++ i) - { - const unsigned char* color = &image.data[(j * image.w + i) * RGBA32_SZ]; - int color_int = (color[0] << 24) | (color[1] << 16) | (color[2] << 8) | color[3]; - ret.insert(color_int); - } - } - - for(SetPal::iterator it = ret.begin(); it != ret.end(); ++it) - { - if(it != ret.begin() && ((0xFF & *it) != 0xFF)) //ret.begin() should be the only one transparent - printf("Warning: found more than one transparent color in tile at x:%d, y:%d of size w:%d, h:%d\n", x, y, w, h); - } +using namespace std; - return ret; -} +int PNG2AssetData::Execute(PNG2AssetArguments* arguments, string input_filename){ -unsigned int PaletteCountApplyMaxLimit(unsigned int max_palettes, unsigned int cur_palette_size) -{ - if (cur_palette_size > max_palettes) - { - printf("Warning: %d palettes found, truncating to %d (-max_palettes)\n", (unsigned int)cur_palette_size, (unsigned int)max_palettes); - return max_palettes; - } - else - return cur_palette_size; -} + this->args = arguments; -int FindOrCreateSubPalette(const SetPal& pal, vector< SetPal >& palettes, size_t colors_per_pal) -{ - // Return -1 if colors can't even fit in sub-palette hardware limit - if (pal.size() > colors_per_pal) + if(arguments->sprite_mode == SPR_8x8) { - return -1; + this->image.tile_w = 8; + this->image.tile_h = 8; } - //Check if it matches any palettes or create a new one - int i; - for (i = 0; i < (int)palettes.size(); ++i) + else if(arguments->sprite_mode == SPR_8x16) { - //Try to merge this palette with any of the palettes (checking if they are equal is not enough since the palettes can have less than 4 colors) - SetPal merged(palettes[i]); - merged.insert(pal.begin(), pal.end()); - if (merged.size() <= colors_per_pal) - { - if (palettes[i].size() <= colors_per_pal) - palettes[i] = merged; //Increase colors with this palette (it has less than 4 colors) - return i; //Found palette - } + this->image.tile_w = 8; + this->image.tile_h = 16; } - - if (i == (int)palettes.size()) + else if(arguments->sprite_mode == SPR_16x16_MSX) { - //Palette not found, add a new one - palettes.push_back(pal); + this->image.tile_w = 16; + this->image.tile_h = 16; } - return i; -} + int readImageDataValue = ReadImageData(this, input_filename); -// -// Builds palettes and palette-per-tile (attributes) for an image -// -// Inputs: -// image32 : Input image in RGBA32 format -// palettes : Palettes for image. Will be added-to / merged with new palettes. -// half_resolution : If true, attributes will follow a constraint of every 2x2 cell having the same attribute. -// -// Returns: array of attributes. This always has *per-tile* dimensions, even when half_resolution is true. -// -int* BuildPalettesAndAttributes(const PNGImage& image32, vector< SetPal >& palettes, bool half_resolution) -{ - int* palettes_per_tile = new int[(image32.w / image32.tile_w) * (image32.h / image32.tile_h)]; - int sx = half_resolution ? 2 : 1; - int sy = half_resolution ? 2 : 1; - for (unsigned int y = 0; y < image32.h; y += image32.tile_h * sy) - { - for (unsigned int x = 0; x < image32.w; x += image32.tile_w * sx) - { - //Get palette colors on (x, y, image32.tile_w, image32.tile_h) - SetPal pal = GetPaletteColors(image32, (x / sx) * sx, (y / sy) * sy, sx * image32.tile_w, sy * image32.tile_h); - - int subPalIndex = FindOrCreateSubPalette(pal, palettes, image32.colors_per_pal); - if (subPalIndex < 0) - { - printf("Error: more than %d colors found in tile at x:%d, y:%d of size w:%d, h:%d\n", - (unsigned int)image32.colors_per_pal, - (x / sx) * sx, - (y / sy) * sy, - sx * image32.tile_w, - sy * image32.tile_h); - subPalIndex = 0; // Force to sub-palette 0, to allow getting a partially-incorrect output image - } - // Assign single or multiple entries in palettes_per_tile, to keep it independent of - // of half_resolution parameter - int dx = ((x / image32.tile_w) / sx) * sx; - int dy = ((y / image32.tile_h) / sy) * sy; - int w = (image32.w / image32.tile_w); - for (int yy = 0; yy < sy; yy++) - { - for (int xx = 0; xx < sx; xx++) - { - palettes_per_tile[(dy + yy) * w + dx + xx] = subPalIndex; - } - } - } + // Return the error code if the function returns non-zero + if(readImageDataValue != 0) { + return readImageDataValue; } - return palettes_per_tile; -} -unsigned char GetMapAttribute(size_t x, size_t y) -{ - if (x < map_attributes_width && y < map_attributes_height) - return map_attributes[y * map_attributes_width + x]; - else - return 0; -} + // Get the data depending on the type + if(this->args->export_as_map)GetMap(this); + else GetAllMetasprites(this); -void ReduceMapAttributes2x2(const vector< SetPal >& palettes) -{ - size_t w = (map_attributes_width + 1) / 2; - size_t h = (map_attributes_height + 1) / 2; - vector< unsigned char > map_attributes_2x2; - map_attributes_2x2.resize(w * h); - for (size_t y = 0; y < h; y++) - { - for (size_t x = 0; x < w; x++) - { - // Use only Top-left attribute, ignoring the other three as they should now be identical - map_attributes_2x2[y * w + x] = GetMapAttribute(2 * x, 2 * y); - } - } - // Overwrite old attributes - map_attributes_width = w; - map_attributes_height = h; - map_attributes = map_attributes_2x2; + return 0; } -// -// Aligns map attribute data to be aligned properly for NES and set_bkg_submap_attributes -// Namely: -// * Width aligned to multiples of 2 to reflect the NES's packed attribute table -// * Every 16th row is blank to reflect the unused row in the NES's packed attribute table -// -void AlignMapAttributes() -{ - const size_t ATTRIBUTE_HEIGHT = 15; - const size_t ATTRIBUTE_ALIGNED_HEIGHT = 16; - vector< unsigned char > map_attributes_aligned; - size_t map_attributes_aligned_width = 2 * ((map_attributes_width + 1) / 2); - size_t num_vertical_nametables = (map_attributes_height + ATTRIBUTE_HEIGHT - 1) / ATTRIBUTE_HEIGHT; - map_attributes_aligned.resize(map_attributes_aligned_width * (num_vertical_nametables * ATTRIBUTE_ALIGNED_HEIGHT)); - for (size_t i = 0; i < num_vertical_nametables; i++) - { - bool last_nametable = (i == num_vertical_nametables - 1); - size_t height = last_nametable ? (map_attributes_height - i * ATTRIBUTE_HEIGHT) : ATTRIBUTE_HEIGHT; - for (size_t y = 0; y < height; y++) - { - for (size_t x = 0; x < map_attributes_width; x++) - { - map_attributes_aligned[(i * ATTRIBUTE_ALIGNED_HEIGHT + y) * map_attributes_aligned_width + x] = - map_attributes[(i * ATTRIBUTE_HEIGHT + y) * map_attributes_width + x]; - } - } - } - // Overwrite old attributes - map_attributes_width = map_attributes_aligned_width; - map_attributes_height = num_vertical_nametables * ATTRIBUTE_ALIGNED_HEIGHT; - map_attributes = map_attributes_aligned; -} +int PNG2AssetData::Export() { -// -// Pack map attributes -// (NES packs multiple 2-bit entries into one byte) -// -void PackMapAttributes() -{ - vector< unsigned char > map_attributes_packed; - map_attributes_packed_width = (map_attributes_width + 1) / 2; - map_attributes_packed_height = (map_attributes_height + 1) / 2; - map_attributes_packed.resize(map_attributes_packed_width * map_attributes_packed_height); - for (size_t y = 0; y < map_attributes_packed_height; y++) - { - for (size_t x = 0; x < map_attributes_packed_width; x++) - { - unsigned char a_tl = GetMapAttribute(2 * x + 0, 2 * y + 0); - unsigned char a_tr = GetMapAttribute(2 * x + 1, 2 * y + 0); - unsigned char a_bl = GetMapAttribute(2 * x + 0, 2 * y + 1); - unsigned char a_br = GetMapAttribute(2 * x + 1, 2 * y + 1); - unsigned char packed_bits = (a_br << 6) | (a_bl << 4) | (a_tr << 2) | (a_tl << 0); - map_attributes_packed[map_attributes_packed_width * y + x] = packed_bits; - } - } - // Overwrite old attributes - map_attributes = map_attributes_packed; -} - -bool GetSourceTileset(bool repair_indexed_pal, bool keep_palette_order, unsigned int max_palettes, vector< SetPal >& palettes) { - - lodepng::State sourceTilesetState; - vector<unsigned char> buffer2; - - lodepng::load_file(buffer2, source_tileset); - - // TODO: This code block below is mostly identical (aside from memcpy, var names) to the main indexed image load. - // It should get deduplicated into a function. - if (keep_palette_order) { - //Calling with keep_palette_order means - //-The image should be png indexed (1-8 bits per pixel) - //-For CGB: Each 4 colors define a gbc palette, the first color is the transparent one - //-Each rectangle with dimension(tile_w, tile_h) in the image has colors from one of those palettes only - sourceTilesetState.info_raw.colortype = LCT_PALETTE; - sourceTilesetState.info_raw.bitdepth = 8; - - // * Do *NOT* turn color_convert ON here. - // When source PNG is indexed with bit depth was less than 8 bits per pixel then - // color_convert may mangle the packed indexed values. Instead manually unpack them. - // - // This is necessary since some PNG encoders will use the minimum possible number of bits. - // For example 2 colors in the palette -> 1bpp -> 8 pixels per byte in the decoded image. - // Also see below about requirement to use palette from source image - sourceTilesetState.decoder.color_convert = false; - - unsigned error = lodepng::decode(source_tileset_image.data, source_tileset_image.w, source_tileset_image.h, sourceTilesetState, buffer2); - // Unpack the image if needed. Also checks and errors on incompatible palette type if needed - if (!image_indexed_ensure_8bpp(source_tileset_image.data, source_tileset_image.w, source_tileset_image.h, (int)sourceTilesetState.info_png.color.bitdepth, (int)sourceTilesetState.info_png.color.colortype)) - return false; - else if(error) { - printf("decoder error %s\n", lodepng_error_text(error)); - return false; - } - - // Use source image palette since lodepng conversion to indexed (LCT_PALETTE) won't create a palette - // So: source_tileset_image.info_png.color.palette/size instead of source_tileset_image.info_raw.palette/size - unsigned int palette_count = PaletteCountApplyMaxLimit(max_palettes, sourceTilesetState.info_png.color.palettesize / source_tileset_image.colors_per_pal); - source_tileset_image.total_color_count = palette_count * source_tileset_image.colors_per_pal; - source_tileset_image.palette = (uint8_t*)malloc(source_tileset_image.total_color_count * RGBA32_SZ); - if(source_tileset_image.palette) { - memcpy(source_tileset_image.palette, sourceTilesetState.info_png.color.palette, source_tileset_image.total_color_count * RGBA32_SZ); - } + // Header file export + if(export_h_file(this) == false) return 1; // Exit with Fail - if (repair_indexed_pal) - if (!image_indexed_repair_tile_palettes(source_tileset_image, use_2x2_map_attributes)) - return 1; + if((this->args->export_as_map) && (this->args->output_binary)) { + // Handle special case of binary map export + export_map_binary(this); } else { - - PNGImage image32; - unsigned error = lodepng::decode(image32.data, image32.w, image32.h, sourceTilesetState, buffer2); //decode as 32 bit - if (error) - { - printf("decoder error %s\n", lodepng_error_text(error)); - return false; - } - - image32.colors_per_pal = source_tileset_image.colors_per_pal; - image32.tile_w = source_tileset_image.tile_w; - image32.tile_h = source_tileset_image.tile_h; - int* palettes_per_tile = BuildPalettesAndAttributes(image32, palettes, use_2x2_map_attributes); - - //Create the indexed image - source_tileset_image.data.clear(); - source_tileset_image.w = image32.w; - source_tileset_image.h = image32.h; - - unsigned int palette_count = PaletteCountApplyMaxLimit(max_palettes, palettes.size()); - - source_tileset_image.total_color_count = palette_count * source_tileset_image.colors_per_pal; - source_tileset_image.palette = new unsigned char[palette_count * source_tileset_image.colors_per_pal * RGBA32_SZ]; //colors_per_pal colors * 4 bytes each - source_total_color_count = source_tileset_image.total_color_count; - - for (size_t p = 0; p < palette_count; ++p) - { - int* color_ptr = (int*)&source_tileset_image.palette[p * source_tileset_image.colors_per_pal * RGBA32_SZ]; - - //TODO: if palettes[p].size() != colors_per_pal we should probably try to fill the gaps based on grayscale values - - for (SetPal::iterator it = palettes[p].begin(); it != palettes[p].end(); ++it, color_ptr++) - { - unsigned char* c = (unsigned char*)&(*it); - *color_ptr = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; - } - } - - for (size_t y = 0; y < image32.h; ++y) - { - for (size_t x = 0; x < image32.w; ++x) - { - unsigned char* c32ptr = &image32.data[(image32.w * y + x) * RGBA32_SZ]; - int color32 = (c32ptr[0] << 24) | (c32ptr[1] << 16) | (c32ptr[2] << 8) | c32ptr[3]; - unsigned char palette = palettes_per_tile[(y / image32.tile_h) * (image32.w / image32.tile_w) + (x / image32.tile_w)]; - unsigned char index = std::distance(palettes[palette].begin(), palettes[palette].find(color32)); - source_tileset_image.data.push_back((palette << bpp) + index); - } - } + // Normal source file export + if(export_c_file(this) == false) return 1; // Exit with Fail } - // We'll change the image variable - // So we don't have to change any of the existing code - PNGImage temp = image; - image = source_tileset_image; - use_source_tileset = false; - GetMap(); - - // Our source tileset shouldn't build the map arrays up - // Clear anything from the previous 'GetMap' call - map.clear(); - map_attributes.clear(); - use_source_tileset = true; - - // Change the image variable back - image = temp; - - source_tileset_size = tiles.size(); - - printf("Got %d tiles from the source tileset.\n", (unsigned int)tiles.size()); - printf("Got %d palettes from the source tileset.\n", (unsigned int)(source_tileset_image.total_color_count / source_tileset_image.colors_per_pal)); - - return true; - + return 0; } -void Export(const PNGImage& image, const char* path) +bool FindTile(const Tile& t, size_t& idx, unsigned char& props, PNG2AssetData* assetData) { - lodepng::State state; - state.info_png.color.colortype = LCT_PALETTE; - state.info_png.color.bitdepth = 8; - state.info_raw.colortype = LCT_PALETTE; - state.info_raw.bitdepth = 8; - state.encoder.auto_convert = 0; //we specify ourselves exactly what output PNG color mode we want - -#define ADD_PALETTE(R, G, B, A) lodepng_palette_add(&state.info_png.color, R, G, B, A); lodepng_palette_add(&state.info_raw, R, G, B, A) - for(size_t p = 0; p < image.total_color_count; ++ p) - { - unsigned char* c = &image.palette[p * RGBA32_SZ]; - ADD_PALETTE(c[0], c[1], c[2], c[3]); - } - - std::vector<unsigned char> buffer; - lodepng::encode(buffer, image.data, image.w, image.h, state); - lodepng::save_file(buffer, path); -} - -int main(int argc, char* argv[]) -{ - if (argc < 2) - { - printf("usage: png2asset <file>.png [options]\n"); - printf("-o ouput file (default: <png file>.c)\n"); - printf("-c deprecated, same as -o\n"); - printf("-sw <width> metasprites width size (default: png width)\n"); - printf("-sh <height> metasprites height size (default: png height)\n"); - printf("-sp <props> change default for sprite OAM property bytes (in hex) (default: 0x00)\n"); - printf("-px <x coord> metasprites pivot x coordinate (default: metasprites width / 2)\n"); - printf("-py <y coord> metasprites pivot y coordinate (default: metasprites height / 2)\n"); - printf("-pw <width> metasprites collision rect width (default: metasprites width)\n"); - printf("-ph <height> metasprites collision rect height (default: metasprites height)\n"); - printf("-spr8x8 use SPRITES_8x8\n"); - printf("-spr8x16 use SPRITES_8x16 (this is the default)\n"); - printf("-spr16x16msx use SPRITES_16x16\n"); - printf("-b <bank> bank (default: fixed bank)\n"); - printf("-keep_palette_order use png palette\n"); - printf("-repair_indexed_pal try to repair indexed tile palettes (implies \"-keep_palette_order\")\n"); - printf("-noflip disable tile flip\n"); - printf("-map Export as map (tileset + bg)\n"); - printf("-use_map_attributes Use CGB BG Map attributes\n"); - printf("-use_nes_attributes Use NES BG Map attributes\n"); - printf("-use_nes_colors Convert RGB color values to NES PPU colors\n"); - printf("-use_structs Group the exported info into structs (default: false) (used by ZGB Game Engine)\n"); - printf("-bpp bits per pixel: 1, 2, 4 (default: 2)\n"); - printf("-max_palettes max number of palettes allowed (default: 8)\n"); - printf(" (note: max colors = max_palettes x num colors per palette)\n"); - printf("-pack_mode gb, nes, sgb, sms, 1bpp (default: gb)\n"); - printf("-tile_origin tile index offset for maps (default: 0)\n"); - - printf("-tiles_only export tile data only\n"); - printf("-maps_only export map tilemap only\n"); - printf("-metasprites_only export metasprite descriptors only\n"); - printf("-source_tileset use source tileset (image with common tiles)\n"); - printf("-keep_duplicate_tiles do not remove duplicate tiles (default: not enabled)\n"); - printf("-no_palettes do not export palette data\n"); - - printf("-bin export to binary format\n"); - printf("-transposed export transposed (column-by-column instead of row-by-row)\n"); - return 0; - } - - //default params - output_filename = argv[1]; - output_filename = output_filename.substr(0, output_filename.size() - 4) + ".c"; - - //Parse argv - for(int i = 2; i < argc; ++i) - { - if(!strcmp(argv[i], "-sw")) - { - sprite_w = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-sh")) - { - sprite_h = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-sp")) - { - props_default = strtol(argv[++i], NULL, 16); - } - if(!strcmp(argv[i], "-px")) - { - pivot_x = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-py")) - { - pivot_y = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-pw")) - { - pivot_w = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-ph")) - { - pivot_h = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-spr8x8")) - { - image.tile_w = 8; - image.tile_h = 8; - sprite_mode = SPR_8x8; - } - else if(!strcmp(argv[i],"-spr8x16")) - { - image.tile_w = 8; - image.tile_h = 16; - sprite_mode = SPR_8x16; - } - else if(!strcmp(argv[i],"-spr16x16msx")) - { - image.tile_w = 16; - image.tile_h = 16; - sprite_mode = SPR_16x16_MSX; - } - else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "-o")) - { - output_filename = argv[++ i]; - } - else if(!strcmp(argv[i], "-b")) - { - bank = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-keep_palette_order")) - { - keep_palette_order = true; - } - else if(!strcmp(argv[i], "-repair_indexed_pal")) - { - repair_indexed_pal = true; - keep_palette_order = true; // -repair_indexed_pal requires -keep_palette_order, so force it on - } - else if(!strcmp(argv[i], "-noflip")) - { - flip_tiles = false; - } - else if(!strcmp(argv[i], "-map")) - { - export_as_map = true; - } - else if(!strcmp(argv[i], "-use_map_attributes")) - { - use_map_attributes = true; - } - else if (!strcmp(argv[i], "-use_nes_attributes")) - { - use_map_attributes = true; - use_2x2_map_attributes = true; - pack_map_attributes = true; - } - else if (!strcmp(argv[i], "-use_nes_colors")) - { - convert_rgb_to_nes = true; - } - else if(!strcmp(argv[i], "-use_structs")) - { - use_structs = true; - } - else if(!strcmp(argv[i], "-bpp")) - { - bpp = atoi(argv[++ i]); - } - else if(!strcmp(argv[i], "-max_palettes")) - { - max_palettes = atoi(argv[++ i]); - if (max_palettes == 0) - { - printf("-max_palettes must be larger than zero\n"); - return 1; - } - } - else if(!strcmp(argv[i], "-pack_mode")) - { - std::string pack_mode_str = argv[++ i]; - if (pack_mode_str == "gb") pack_mode = Tile::GB; - else if (pack_mode_str == "nes") pack_mode = Tile::NES; - else if(pack_mode_str == "sgb") pack_mode = Tile::SGB; - else if(pack_mode_str == "sms") pack_mode = Tile::SMS; - else if(pack_mode_str == "1bpp") pack_mode = Tile::BPP1; - else - { - printf("-pack_mode must be one of gb, nes, sgb, sms, 1bpp\n"); - return 1; - } - } - else if(!strcmp(argv[i], "-tile_origin")) - { - tile_origin = atoi(argv[++ i]); - } - else if (!strcmp(argv[i], "-maps_only") || !strcmp(argv[i], "-metasprites_only")) - { - includeTileData = false; - } - else if (!strcmp(argv[i], "-tiles_only")) - { - includedMapOrMetaspriteData = false; - } - else if (!strcmp(argv[i], "-keep_duplicate_tiles")) - { - keep_duplicate_tiles = true; - } - else if (!strcmp(argv[i], "-no_palettes")) - { - include_palettes = false; - } - else if (!strcmp(argv[i], "-source_tileset")) - { - use_source_tileset = true; - includeTileData = false; - source_tileset = argv[++i]; - } - else if (!strcmp(argv[i], "-bin")) - { - output_binary = true; - } - else if (!strcmp(argv[i], "-transposed")) - { - output_transposed = true; - } - } - - image.colors_per_pal = 1 << bpp; - - if(export_as_map) - { - image.tile_w = 8; //Force tiles_w to 8 on maps - image.tile_h = 8; //Force tiles_h to 8 on maps - sprite_mode = SPR_NONE; - } - - int slash_pos = (int)output_filename.find_last_of('/'); - if(slash_pos == -1) - slash_pos = (int)output_filename.find_last_of('\\'); - int dot_pos = (int)output_filename.find_first_of('.', slash_pos == -1 ? 0 : slash_pos); - - output_filename_h = output_filename.substr(0, dot_pos) + ".h"; - output_filename_bin = output_filename.substr(0, dot_pos) + "_map.bin"; - output_filename_attributes_bin = output_filename.substr(0, dot_pos) + "_map_attributes.bin"; - output_filename_tiles_bin = output_filename.substr(0, dot_pos) + "_tiles.bin"; - data_name = output_filename.substr(slash_pos + 1, dot_pos - 1 - slash_pos); - replace(data_name.begin(), data_name.end(), '-', '_'); - - // This was moved from outside the upcoming else statement when not using keep_palette_order - // So the 'GetSourceTileset' function can pre-populate it from the source tileset - vector< SetPal > palettes; - - // Copy some settings into optional source tileset image - source_tileset_image.colors_per_pal = image.colors_per_pal; - source_tileset_image.tile_w = image.tile_w; - source_tileset_image.tile_h = image.tile_h; - - if (use_source_tileset) { - - if (!GetSourceTileset(repair_indexed_pal, keep_palette_order, max_palettes, palettes)) { - return 1; - } - } - - - //load and decode png - vector<unsigned char> buffer; - lodepng::load_file(buffer, argv[1]); - lodepng::State state; - - if (keep_palette_order) { - //Calling with keep_palette_order means - //-The image should be png indexed (1-8 bits per pixel) - //-For CGB: Each 4 colors define a gbc palette, the first color is the transparent one - //-Each rectangle with dimension(tile_w, tile_h) in the image has colors from one of those palettes only - state.info_raw.colortype = LCT_PALETTE; - state.info_raw.bitdepth = 8; - - // * Do *NOT* turn color_convert ON here. - // When source PNG is indexed with bit depth was less than 8 bits per pixel then - // color_convert may mangle the packed indexed values. Instead manually unpack them. - // - // This is necessary since some PNG encoders will use the minimum possible number of bits. - // For example 2 colors in the palette -> 1bpp -> 8 pixels per byte in the decoded image. - // Also see below about requirement to use palette from source image - state.decoder.color_convert = false; - - unsigned error = lodepng::decode(image.data, image.w, image.h, state, buffer); - // Unpack the image if needed. Also checks and errors on incompatible palette type if needed - if (! image_indexed_ensure_8bpp(image.data, image.w, image.h, (int)state.info_png.color.bitdepth, (int)state.info_png.color.colortype)) - return 1; - else if(error) { - printf("decoder error %s\n", lodepng_error_text(error)); - return 1; - } - - // Use source image palette since lodepng conversion to indexed (LCT_PALETTE) won't create a palette - // So: state.info_png.color.palette/size instead of state.info_raw.palette/size - unsigned int palette_count = PaletteCountApplyMaxLimit(max_palettes, state.info_png.color.palettesize / image.colors_per_pal); - image.total_color_count = palette_count * image.colors_per_pal; - image.palette = state.info_png.color.palette; - - - if (repair_indexed_pal) - if (!image_indexed_repair_tile_palettes(image, use_2x2_map_attributes)) - return 1; - - // TODO: Enable dimension check - // // Validate image dimensions - // if (((image.w % tile_w) != 0) || ((image.h % tile_h) != 0)) - // { - // 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); - // return 1; - // } - - if (use_source_tileset) { - - // Make sure these two values match when keeping palette order - if (image.total_color_count != source_tileset_image.total_color_count) { - - printf("error: The number of color palette's for your source tileset (%d) and target image (%d) do not match.", (unsigned int)source_tileset_image.total_color_count, (unsigned int)image.total_color_count); - return 1; - } - - size_t size = max(image.total_color_count, source_tileset_image.total_color_count); - - // Make sure these two values match when keeping palette order - if (memcmp(image.palette, source_tileset_image.palette, size) != 0) { - - printf("error: The palette's for your source tileset and target image do not match."); - return 1; - } - } - } - else - { - PNGImage image32; - image32.colors_per_pal = image.colors_per_pal; - image32.tile_w = image.tile_w; - image32.tile_h = image.tile_h; - - unsigned error = lodepng::decode(image32.data, image32.w, image32.h, state, buffer); //decode as 32 bit - if(error) - { - printf("decoder error %s\n", lodepng_error_text(error)); - return 1; - } - - // Validate image dimensions - if( ((image32.w % image32.tile_w) != 0) || ((image32.h % image32.tile_h) != 0) ) - { - 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); - return 1; - } - - int* palettes_per_tile = BuildPalettesAndAttributes(image32, palettes, use_2x2_map_attributes); - - //Create the indexed image - image.data.clear(); - image.w = image32.w; - image.h = image32.h; - - unsigned int palette_count = PaletteCountApplyMaxLimit(max_palettes, palettes.size()); - - image.total_color_count = palette_count * image.colors_per_pal; - image.palette = new unsigned char[palette_count * image.colors_per_pal * RGBA32_SZ]; // total color count * 4 bytes each - - // If we are using a sourcetileset and have more palettes than it defines - if (use_source_tileset && (image.total_color_count > source_total_color_count)) { - printf("Found %d extra palette(s) for target tilemap.\n", (unsigned int)((image.total_color_count - source_total_color_count) / image.colors_per_pal)); - } - for(size_t p = 0; p < palette_count; ++p) - { - int *color_ptr = (int*)&image.palette[p * image.colors_per_pal * RGBA32_SZ]; - - //TODO: if palettes[p].size() != image.colors_per_pal we should probably try to fill the gaps based on grayscale values - - for(SetPal::iterator it = palettes[p].begin(); it != palettes[p].end(); ++ it, color_ptr ++) - { - unsigned char* c = (unsigned char*)&(*it); - *color_ptr = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; - } - } - - for(size_t y = 0; y < image32.h; ++ y) - { - for(size_t x = 0; x < image32.w; ++x) - { - unsigned char* c32ptr = &image32.data[(image32.w * y + x) * RGBA32_SZ]; - int color32 = (c32ptr[0] << 24) | (c32ptr[1] << 16) | (c32ptr[2] << 8) | c32ptr[3]; - unsigned char palette = palettes_per_tile[(y / image32.tile_h) * (image32.w / image32.tile_w) + (x / image32.tile_w)]; - unsigned char index = std::distance(palettes[palette].begin(), palettes[palette].find(color32)); - image.data.push_back((palette << bpp) + index); - } - } - - //Test: output png to see how it looks - //Export(image, "temp.png"); - } - - if(sprite_w == 0) sprite_w = (int)image.w; - if(sprite_h == 0) sprite_h = (int)image.h; - if(pivot_x == 0xFFFFFF) pivot_x = sprite_w / 2; - if(pivot_y == 0xFFFFFF) pivot_y = sprite_h / 2; - if(pivot_w == 0xFFFFFF) pivot_w = sprite_w; - if(pivot_h == 0xFFFFFF) pivot_h = sprite_h; - - if(export_as_map) - { - //Extract map - GetMap(); - } - else - { - //Extract metasprites - for(int y = 0; y < (int)image.h; y += sprite_h) - { - for(int x = 0; x < (int)image.w; x += sprite_w) - { - GetMetaSprite(x, y, sprite_w, sprite_h, pivot_x, pivot_y); - } - } - } - map_attributes_width = image.w / 8; - map_attributes_height = image.h / 8; - // Optionally perform 2x2 reduction on attributes (NES attribute table has this format) - if(use_2x2_map_attributes) - { - // NES attribute map dimensions are half-resolution - ReduceMapAttributes2x2(palettes); - } - // Optionally align and pack map attributes into NES PPU format - if (pack_map_attributes) - { - AlignMapAttributes(); - PackMapAttributes(); - } - else + vector< Tile >::iterator it; + it = find(assetData->tiles.begin(), assetData->tiles.end(), t); + if(it != assetData->tiles.end()) { - // Use original attribute dimensions for packed - map_attributes_packed_width = map_attributes_width; - map_attributes_packed_height = map_attributes_height; - } - - // === EXPORT === - - // Header file export - if (export_h_file() == false) return 1; // Exit with Fail - - if ((export_as_map) && (output_binary)) { - // Handle special case of binary map export - export_map_binary(); - } else { - // Normal source file export - if (export_c_file() == false) return 1; // Exit with Fail - } -} - - -bool export_h_file(void) { - - FILE* file; - - file=fopen(output_filename_h.c_str(), "w"); - if (!file) { - printf("Error writing file: %s", output_filename_h.c_str() ); - return false; + idx = (size_t)(it - assetData->tiles.begin()); + props = assetData->args->props_default; + return true; } - fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n"); - fprintf(file, "#ifndef METASPRITE_%s_H\n", data_name.c_str()); - fprintf(file, "#define METASPRITE_%s_H\n", data_name.c_str()); - fprintf(file, "\n"); - fprintf(file, "#include <stdint.h>\n"); - fprintf(file, "#include <gbdk/platform.h>\n"); - fprintf(file, "#include <gbdk/metasprites.h>\n"); - fprintf(file, "\n"); - if(use_structs) + if(assetData->args->flip_tiles) { - if(export_as_map) - { - fprintf(file, "#include \"TilesInfo.h\"\n"); - fprintf(file, "#include \"MapInfo.h\"\n"); - fprintf(file, "\n"); - fprintf(file, "extern const struct TilesInfo %s_tiles_info;\n", data_name.c_str()); - fprintf(file, "extern const struct MapInfo %s;\n", data_name.c_str()); - } - else + Tile tile = FlipV(t); + it = find(assetData->tiles.begin(), assetData->tiles.end(), tile); + if(it != assetData->tiles.end()) { - fprintf(file, "#include \"MetaSpriteInfo.h\"\n"); - fprintf(file, "\n"); - fprintf(file, "extern const struct MetaSpriteInfo %s;\n", data_name.c_str()); - } - } - else - { - fprintf(file, "#define %s_TILE_ORIGIN %d\n", data_name.c_str(), tile_origin); - fprintf(file, "#define %s_TILE_W %d\n", data_name.c_str(), image.tile_w); - fprintf(file, "#define %s_TILE_H %d\n", data_name.c_str(), image.tile_h); - fprintf(file, "#define %s_WIDTH %d\n", data_name.c_str(), sprite_w); - fprintf(file, "#define %s_HEIGHT %d\n", data_name.c_str(), sprite_h); - // The TILE_COUNT calc here is referring to number of 8x8 tiles, - // so the >> 3 for each sizes axis is to get a multiplier for larger hardware sprites such as 8x16 and 16x16 - fprintf(file, "#define %s_TILE_COUNT %d\n", data_name.c_str(), ((unsigned int)tiles.size() - source_tileset_size) * (image.tile_h >> 3) * (image.tile_w >> 3)); - if (include_palettes) { - fprintf(file, "#define %s_PALETTE_COUNT %d\n", data_name.c_str(), (unsigned int)(image.total_color_count / image.colors_per_pal)); - fprintf(file, "#define %s_COLORS_PER_PALETTE %d\n", data_name.c_str(), (unsigned int)image.colors_per_pal); - fprintf(file, "#define %s_TOTAL_COLORS %d\n", data_name.c_str(), (unsigned int)image.total_color_count); - } - - if (includedMapOrMetaspriteData) { - - if(export_as_map) - { - fprintf(file, "#define %s_MAP_ATTRIBUTES ", data_name.c_str()); - if(use_map_attributes && map_attributes.size()) - fprintf(file, "%s_map_attributes\n", data_name.c_str()); - else - fprintf(file, "0\n"); - - if (use_map_attributes) - { - int scale = use_2x2_map_attributes ? 2 : 1; - fprintf(file, "#define %s_MAP_ATTRIBUTES_WIDTH %d\n", data_name.c_str(), (int)(scale * map_attributes_width)); - fprintf(file, "#define %s_MAP_ATTRIBUTES_HEIGHT %d\n", data_name.c_str(), (int)(scale * map_attributes_height)); - fprintf(file, "#define %s_MAP_ATTRIBUTES_PACKED_WIDTH %d\n", data_name.c_str(), (int)map_attributes_packed_width); - fprintf(file, "#define %s_MAP_ATTRIBUTES_PACKED_HEIGHT %d\n", data_name.c_str(), (int)map_attributes_packed_height); - } - - if(use_structs) - { - fprintf(file, "#define %s_TILE_PALS ", data_name.c_str()); - if(use_map_attributes) - fprintf(file, "0\n"); - else - fprintf(file, "%s_tile_pals\n", data_name.c_str()); - } - } - else - { - fprintf(file, "#define %s_PIVOT_X %d\n", data_name.c_str(), pivot_x); - fprintf(file, "#define %s_PIVOT_Y %d\n", data_name.c_str(), pivot_y); - fprintf(file, "#define %s_PIVOT_W %d\n", data_name.c_str(), pivot_w); - fprintf(file, "#define %s_PIVOT_H %d\n", data_name.c_str(), pivot_h); - } - } - fprintf(file, "\n"); - fprintf(file, "BANKREF_EXTERN(%s)\n", data_name.c_str()); - fprintf(file, "\n"); - - // If we are not using a source tileset, or if we have extra palettes defined - if (include_palettes && (image.total_color_count - source_total_color_count > 0 || !use_source_tileset)) { - fprintf(file, "extern const palette_color_t %s_palettes[%d];\n", data_name.c_str(), (unsigned int)image.total_color_count - source_total_color_count); - } - if (includeTileData) { - fprintf(file, "extern const uint8_t %s_tiles[%d];\n", data_name.c_str(), (unsigned int)((tiles.size() - source_tileset_size) * (image.tile_w * image.tile_h * bpp / 8))); - } - - fprintf(file, "\n"); - if (includedMapOrMetaspriteData) { - if(export_as_map) - { - fprintf(file, "extern const unsigned char %s_map[%d];\n", data_name.c_str(), (unsigned int)map.size()); - - if(use_map_attributes && map_attributes.size()) { - fprintf(file, "extern const unsigned char %s_map_attributes[%d];\n", data_name.c_str(), (unsigned int)map_attributes.size()); - } - else - { - // Some platforms (like SMS/GG) encode attributes as part of map - // For compatibility, add a define that makes _map_attributes equal _map, - // so that set_bkg_attributes can work the same on these platforms - fprintf(file, "#define %s_map_attributes %s_map\n", data_name.c_str(), data_name.c_str()); - } - if (!use_map_attributes && (includeTileData) && (use_structs)) { - fprintf(file, "extern const unsigned char* %s_tile_pals[%d];\n", data_name.c_str(), (unsigned int)tiles.size()); - } - } - else - { - fprintf(file, "extern const metasprite_t* const %s_metasprites[%d];\n", data_name.c_str(), (unsigned int)sprites.size()); - } + idx = (size_t)(it - assetData->tiles.begin()); + props = assetData->args->props_default | (1 << 5); + return true; } - } - fprintf(file, "\n"); - fprintf(file, "#endif\n"); - - - fclose(file); - // END: Output .h FILE - - return true; // success -} - - - -bool export_c_file(void) { - - FILE* file; - - file = fopen(output_filename.c_str(), "w"); - if(!file) { - printf("Error writing file: %s", output_filename.c_str() ); - return false; - } - - if (bank >= 0) fprintf(file, "#pragma bank %d\n\n", bank); - - fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n\n"); - - fprintf(file, "#include <stdint.h>\n"); - fprintf(file, "#include <gbdk/platform.h>\n"); - fprintf(file, "#include <gbdk/metasprites.h>\n"); - fprintf(file, "\n"); - - fprintf(file, "BANKREF(%s)\n\n", data_name.c_str()); - // Are we not using a source tileset, or do we have extra colors - if (include_palettes && (image.total_color_count - source_total_color_count > 0||!use_source_tileset)) { - - // Subtract however many palettes we had in the source tileset - fprintf(file, "const palette_color_t %s_palettes[%d] = {\n", data_name.c_str(), (unsigned int)image.total_color_count - source_total_color_count); - - // Offset by however many palettes we had in the source tileset - for (size_t i = source_total_color_count / image.colors_per_pal; i < image.total_color_count / image.colors_per_pal; ++i) - { - if(i != 0) - fprintf(file, ",\n"); - fprintf(file, "\t"); - - unsigned char* pal_ptr = &image.palette[i * (image.colors_per_pal * RGBA32_SZ)]; - for(int c = 0; c < (int)image.colors_per_pal; ++ c, pal_ptr += RGBA32_SZ) - { - size_t rgb222 = (((pal_ptr[2] >> 6) & 0x3) << 4) | - (((pal_ptr[1] >> 6) & 0x3) << 2) | - (((pal_ptr[0] >> 6) & 0x3) << 0); - if (convert_rgb_to_nes) - fprintf(file, "0x%0X", rgb_to_nes[rgb222]); - else - fprintf(file, "RGB8(%3d,%3d,%3d)", pal_ptr[0], pal_ptr[1], pal_ptr[2]); - if(c != (int)image.colors_per_pal - 1) - fprintf(file, ", "); - // Line break every 4 color entries, to keep line width down - if (((c + 1) % 4) == 0) - fprintf(file, "\n\t"); - } - } - fprintf(file, "\n};\n"); - } - - if (includeTileData) { - fprintf(file, "\n"); - fprintf(file, "const uint8_t %s_tiles[%d] = {\n", data_name.c_str(), (unsigned int)((tiles.size()-source_tileset_size) * image.tile_w * image.tile_h * bpp / 8)); - fprintf(file, "\t"); - for (vector< Tile >::iterator it = tiles.begin()+ source_tileset_size; it != tiles.end(); ++it) + tile = FlipH(tile); + it = find(assetData->tiles.begin(), assetData->tiles.end(), tile); + if(it != assetData->tiles.end()) { - - int line_break = 1; // Start with 1 to prevent line break on first iteration - vector< unsigned char > packed_data = (*it).GetPackedData(pack_mode, image.tile_w, image.tile_h, bpp); - for(vector< unsigned char >::iterator it2 = packed_data.begin(); it2 != packed_data.end(); ++it2) - { - fprintf(file, "0x%02x", (*it2)); - if((it + 1) != tiles.end() || (it2 + 1) != packed_data.end()) - fprintf(file, ","); - // Add a line break after each 8x8 tile - if (((line_break++) % (8 / bpp)) == 0) - fprintf(file, "\n\t"); - } - - if ((!export_as_map) && (it != tiles.end())) - fprintf(file, "\n"); + idx = (size_t)(it - assetData->tiles.begin()); + props = assetData->args->props_default | (1 << 5) | (1 << 6); + return true; } - fprintf(file, "};\n\n"); - } - - if(includedMapOrMetaspriteData) { - - if(!export_as_map) - { - for(vector< MetaSprite >::iterator it = sprites.begin(); it != sprites.end(); ++ it) - { - fprintf(file, "const metasprite_t %s_metasprite%d[] = {\n", data_name.c_str(), (int)(it - sprites.begin())); - for(MetaSprite::iterator it2 = (*it).begin(); it2 != (*it).end(); ++ it2) - { - int pal_idx = (*it2).props & 0xF; - int flip_x = ((*it2).props >> 5) & 1; - int flip_y = ((*it2).props >> 6) & 1; - fprintf(file, - "\tMETASPR_ITEM(%d, %d, %d, S_PAL(%d)%s%s),\n", - (*it2).offset_y, - (*it2).offset_x, - (*it2).offset_idx, - pal_idx, - flip_x ? " | S_FLIPX" : "", - flip_y ? " | S_FLIPY" : ""); - } - fprintf(file, "\tMETASPR_TERM\n"); - fprintf(file, "};\n\n"); - } - fprintf(file, "const metasprite_t* const %s_metasprites[%d] = {\n\t", data_name.c_str(), (unsigned int)sprites.size()); - for(vector< MetaSprite >::iterator it = sprites.begin(); it != sprites.end(); ++ it) - { - fprintf(file, "%s_metasprite%d", data_name.c_str(), (int)(it - sprites.begin())); - if(it + 1 != sprites.end()) - fprintf(file, ", "); - } - fprintf(file, "\n};\n"); - - if(use_structs) - { - fprintf(file, "\n"); - fprintf(file, "#include \"MetaSpriteInfo.h\"\n"); - fprintf(file, "const struct MetaSpriteInfo %s = {\n", data_name.c_str()); - fprintf(file, "\t%d, //width\n", pivot_w); - fprintf(file, "\t%d, //height\n", pivot_h); - fprintf(file, "\t%d, //num tiles\n", (unsigned int)tiles.size() * (image.tile_h >> 3)); - fprintf(file, "\t%s_tiles, //tiles\n", data_name.c_str()); - fprintf(file, "\t%d, //num palettes\n", (unsigned int)(image.total_color_count / image.colors_per_pal)); - fprintf(file, "\t%s_palettes, //CGB palette\n", data_name.c_str()); - fprintf(file, "\t%d, //num sprites\n", (unsigned int)sprites.size()); - fprintf(file, "\t%s_metasprites, //metasprites\n", data_name.c_str()); - fprintf(file, "};\n"); - } - } - else + tile = FlipV(tile); + it = find(assetData->tiles.begin(), assetData->tiles.end(), tile); + if(it != assetData->tiles.end()) { - if (includeTileData) { - if(use_structs) - { - //Export tiles pals (if any) - if(!use_map_attributes) - { - fprintf(file, "\n"); - fprintf(file, "const uint8_t %s_tile_pals[%d] = {\n\t", data_name.c_str(), (unsigned int)tiles.size()- source_tileset_size); - for(vector< Tile >::iterator it = tiles.begin()+ source_tileset_size; it != tiles.end(); ++ it) - { - if(it != tiles.begin()) - fprintf(file, ", "); - fprintf(file, "%d", it->pal); - } - fprintf(file, "\n};\n"); - } - //Export Tiles Info - fprintf(file, "\n"); - fprintf(file, "#include \"TilesInfo.h\"\n"); - fprintf(file, "BANKREF(%s_tiles_info)\n", data_name.c_str()); - fprintf(file, "const struct TilesInfo %s_tiles_info = {\n", data_name.c_str()); - fprintf(file, "\t%d, //num tiles\n", (unsigned int)tiles.size() * (image.tile_h >> 3)); - fprintf(file, "\t%s_tiles, //tiles\n", data_name.c_str()); - fprintf(file, "\t%d, //num palettes\n", (unsigned int)(image.total_color_count / image.colors_per_pal)); - fprintf(file, "\t%s_palettes, //palettes\n", data_name.c_str()); - if(!use_map_attributes) - fprintf(file, "\t%s_tile_pals, //tile palettes\n", data_name.c_str()); - else - fprintf(file, "\t0 //tile palettes\n"); - fprintf(file, "};\n"); - } - } - - //Export map - fprintf(file, "\n"); - fprintf(file, "const unsigned char %s_map[%d] = {\n", data_name.c_str(), (unsigned int)map.size()); - size_t line_size = map.size() / (image.h / 8); - if (output_transposed) { - - for(size_t i = 0; i < line_size; ++i) - { - fprintf(file, "\t"); - for (size_t j = 0; j < image.h / 8; ++j) - { - fprintf(file, "0x%02x,", map[j * line_size + i]); - } - fprintf(file, "\n"); - } - } - else { - - for (size_t j = 0; j < image.h / 8; ++j) - { - fprintf(file, "\t"); - for (size_t i = 0; i < line_size; ++i) - { - fprintf(file, "0x%02x,", map[j * line_size + i]); - } - fprintf(file, "\n"); - } - } - fprintf(file, "};\n"); - - - //Export map attributes (if any) - if(use_map_attributes && map_attributes.size()) - { - fprintf(file, "\n"); - fprintf(file, "const unsigned char %s_map_attributes[%d] = {\n", data_name.c_str(), (unsigned int)map_attributes.size()); - if (output_transposed) { - for (size_t i = 0; i < map_attributes_packed_width; ++i) - { - fprintf(file, "\t"); - for (size_t j = 0; j < map_attributes_packed_height; ++j) - { - fprintf(file, "0x%02x,", map_attributes[j * map_attributes_packed_width + i]); - } - fprintf(file, "\n"); - } - } - else { - for (size_t j = 0; j < map_attributes_packed_height; ++j) - { - fprintf(file, "\t"); - for (size_t i = 0; i < map_attributes_packed_width; ++i) - { - fprintf(file, "0x%02x,", map_attributes[j * map_attributes_packed_width + i]); - } - fprintf(file, "\n"); - } - } - - fprintf(file, "};\n"); - } - - if(use_structs) - { - //Export Map Info - fprintf(file, "\n"); - fprintf(file, "#include \"MapInfo.h\"\n"); - fprintf(file, "BANKREF_EXTERN(%s_tiles_info)\n", data_name.c_str()); - fprintf(file, "const struct MapInfo %s = {\n", data_name.c_str()); - fprintf(file, "\t%s_map, //map\n", data_name.c_str()); - fprintf(file, "\t%d, //with\n", image.w >> 3); - fprintf(file, "\t%d, //height\n", image.h >> 3); - if (use_map_attributes && map_attributes.size()) - fprintf(file, "\t%s_map_attributes, //map attributes\n", data_name.c_str()); - else - fprintf(file, "\t%s, //map attributes\n", "0"); - fprintf(file, "\tBANK(%s_tiles_info), //tiles bank\n", data_name.c_str()); - fprintf(file, "\t&%s_tiles_info, //tiles info\n", data_name.c_str()); - fprintf(file, "};\n"); - } + idx = (size_t)(it - assetData->tiles.begin()); + props = assetData->args->props_default | (1 << 6); + return true; } } - fclose(file); - - return true; // success -} - - -bool export_map_binary() { - - std::ofstream mapBinaryFile, mapAttributesBinaryfile,tilesBinaryFile; - mapBinaryFile.open(output_filename_bin, std::ios_base::binary); - tilesBinaryFile.open(output_filename_tiles_bin, std::ios_base::binary); - - for (vector< Tile >::iterator it = tiles.begin() + source_tileset_size; it != tiles.end(); ++it) - { - - vector< unsigned char > packed_data = (*it).GetPackedData(pack_mode, image.tile_w, image.tile_h, bpp); - for (vector< unsigned char >::iterator it2 = packed_data.begin(); it2 != packed_data.end(); ++it2) - { - - const char chars[] = { (const char)(*it2) }; - tilesBinaryFile.write(chars, 1); - } - - } - - - // Open our file for writing attributes if specified - if (use_map_attributes)mapAttributesBinaryfile.open(output_filename_attributes_bin, std::ios_base::binary); - - int columns = image.w >> 3; - int rows = image.h >> 3; - - // If we want the values to be column-by-column - if (output_transposed) { - - // Swap the column/row for loops - for (int column = 0; column < columns; column++) { - for (int row = 0; row < rows; ++row) { - - int tile = column + row * columns; - - const char mapChars[] = { (const char)map[tile] }; - - // Write map items column-by-column - mapBinaryFile.write(mapChars, 1); - if(use_map_attributes) { - const char mapAttributeChars[] = { (const char)map_attributes[tile] }; - mapAttributesBinaryfile.write(mapAttributeChars, 1); - } - } - } - } - else { - - // Write the arrays as-is, row-by-row - mapBinaryFile.write((const char*)(&map[0]), rows * columns); - if (use_map_attributes)mapAttributesBinaryfile.write((const char*)(&map_attributes[0]), rows * columns); - } - - // Finalize the files - mapBinaryFile.close(); - tilesBinaryFile.close(); - if (use_map_attributes)mapAttributesBinaryfile.close(); - - return true; // success -} + return false; +} + diff --git a/gbdk-support/png2asset/png2asset.h b/gbdk-support/png2asset/png2asset.h @@ -1,179 +1,37 @@ -#ifndef _PNG2ASSET_H -#define _PNG2ASSET_H +#pragma once -#define RGBA32_SZ 4 // RGBA 8:8:8:8 is 4 bytes per pixel -enum { - SPR_NONE, - SPR_8x8, - SPR_8x16, - SPR_16x16_MSX -}; - -#define BIT(VALUE, INDEX) (1 & ((VALUE) >> (INDEX))) - -struct Tile -{ - vector< unsigned char > data; - unsigned char pal; - - Tile(size_t size = 0) : data(size), pal(0) {} - bool operator==(const Tile& t) const - { - return data == t.data && pal == t.pal; - } - - const Tile& operator=(const Tile& t) - { - data = t.data; - pal = t.pal; - return *this; - } - - enum PackMode { - GB, - NES, - SGB, - SMS, - BPP1 - }; - - vector< unsigned char > GetPackedData(PackMode pack_mode, int tile_w, int tile_h, int bpp) { - vector< unsigned char > ret((tile_w / 8) * tile_h * bpp, 0); - if(pack_mode == GB) { - for(int j = 0; j < tile_h; ++j) { - for(int i = 0; i < 8; ++ i) { - unsigned char col = data[8 * j + i]; - ret[j * 2 ] |= BIT(col, 0) << (7 - i); - ret[j * 2 + 1] |= BIT(col, 1) << (7 - i); - } - } - } - if (pack_mode == NES) { - for (int j = 0; j < tile_h; ++j) { - for (int i = 0; i < 8; ++i) { - unsigned char col = data[8 * j + i]; - ret[j] |= BIT(col, 0) << (7 - i); - ret[j+8] |= BIT(col, 1) << (7 - i); - } - } - } - else if(pack_mode == SGB) - { - for(int j = 0; j < tile_h; ++j) { - for(int i = 0; i < 8; ++ i) { - unsigned char col = data[8 * j + i]; - ret[j * 2 ] |= BIT(col, 0) << (7 - i); - ret[j * 2 + 1] |= BIT(col, 1) << (7 - i); - ret[(tile_h + j) * 2 ] |= BIT(col, 2) << (7 - i); - ret[(tile_h + j) * 2 + 1] |= BIT(col, 3) << (7 - i); - } - } - } - else if(pack_mode == SMS) - { - for(int j = 0; j < tile_h; ++j) { - for(int i = 0; i < 8; ++ i) { - unsigned char col = data[8 * j + i]; - ret[j * 4 ] |= BIT(col, 0) << (7 - i); - ret[j * 4 + 1] |= BIT(col, 1) << (7 - i); - ret[j * 4 + 2] |= BIT(col, 2) << (7 - i); - ret[j * 4 + 3] |= BIT(col, 3) << (7 - i); - } - } - } - else if(pack_mode == BPP1) - { - // Packs 8 pixel wide rows in order set by ExtractTile**() - // Process all rows of pixels in the tile - for(int j = 0; j < ((tile_w / 8) * tile_h); j++) { - // Pack each row of 8 pixels into one byte - for(int i = 0; i < 8; i++) { - unsigned char col = data[8 * j + i]; - ret[j] |= BIT(col, 0) << (7 - i); - } - } - } - return ret; - } -}; +#include <vector> +#include <string> +#include "vector2.h" +#include "mttile.h" +#include "metasprites.h" +#include "cmp_int_color.h" +#include "tiles.h" +#include "png_image.h" +#include "process_arguments.h" +using namespace std; -struct PNGImage -{ - vector< unsigned char > data; //data in indexed format - unsigned int w; - unsigned int h; - - // Default tile size - int tile_w = 8; - int tile_h = 16; - - // TODO: embed these instead of deriving them many places in the code - // int attribute_w_factor = 1; - // int attribute_h_factor = 1; - // int get_attribute_tile_w() { tile_w * attribute_w_factor; } - // int get_attribute_tile_h() { tile_h * attribute_h_factor; } - - size_t colors_per_pal; // Number of colors per palette (ex: CGB has 4 colors per palette x 8 palettes total) - size_t total_color_count; // Total number of colors across all palettes (palette_count x colors_per_pal) - unsigned char* palette; //palette colors in RGBA (1 color == 4 bytes) - -private: - bool zero_palette = false; +class PNG2AssetData { public: - unsigned char GetGBColor(int x, int y) - { - return data[w * y + x] % colors_per_pal; - } + int Execute(PNG2AssetArguments* arguments, string input_filename); + int Export(); - // This needs separate tile_w and tile_h params since - // MSX tile extraction uses it to pull out the 4 sub-tiles - bool ExtractGBTile(int x, int y, int extract_tile_w, int extract_tile_h, Tile& tile, int buffer_offset) - { - // Set the palette to 0 when pals are not stored in tiles to allow tiles to be equal even when their palettes are different - tile.pal = zero_palette ? 0 : data[w * y + x] >> 2; + int errorCode; - bool all_zero = true; - for(int j = 0; j < extract_tile_h; ++ j) - { - for(int i = 0; i < extract_tile_w; ++i) - { - unsigned char color_idx = GetGBColor(x + i, y + j); - tile.data[(j * extract_tile_w) + i + buffer_offset] = color_idx; - all_zero = all_zero && (color_idx == 0); - } - } - return !all_zero; - } + PNG2AssetArguments* args; - bool ExtractTile_MSX16x16(int x, int y, Tile& tile) - { - // MSX 16x16 sprite tiles are composed of four 8x8 tiles in this order UL, LL, UR, LR - bool UL_notempty, LL_notempty, UR_notempty, LR_notempty; + vector< SetPal > palettes; + vector< Tile > tiles; + vector< MetaSprite > sprites; + vector< unsigned char > map; + vector< unsigned char > map_attributes; + PNGImage source_tileset_image; + PNGImage image; - // Call these separately since otherwise some get optimized out during - // runtime if any single one before it returns false - UL_notempty = ExtractGBTile(x, y, 8, 8, tile, 0); - LL_notempty = ExtractGBTile(x, y + 8, 8, 8, tile, ((8 *8) * 1)); - UR_notempty = ExtractGBTile(x + 8, y, 8, 8, tile, ((8 *8) * 2)); - LR_notempty = ExtractGBTile(x + 8, y + 8, 8, 8, tile, ((8 *8) * 3)); - return (UL_notempty || LL_notempty || UR_notempty || LR_notempty); - } - - bool ExtractTile(int x, int y, Tile& tile, int sprite_mode, bool export_as_map, bool use_map_attributes) - { - // Set the palette to 0 when pals are not stored in tiles to allow tiles to be equal even when their palettes are different - zero_palette = !(export_as_map && !use_map_attributes); - - if (sprite_mode == SPR_16x16_MSX) - return ExtractTile_MSX16x16(x, y, tile); - else - return ExtractGBTile(x, y, tile_w, tile_h, tile, 0); // No buffer offset for normal tile extraction - } -// private: -// bool zero_palette = false; }; -#endif + +bool FindTile(const Tile& t, size_t& idx, unsigned char& props, PNG2AssetData* assetData); + diff --git a/gbdk-support/png2asset/png_image.h b/gbdk-support/png2asset/png_image.h @@ -0,0 +1,91 @@ +#pragma once +#include <vector> +#include "tiles.h" +using namespace std; + +#define RGBA32_SZ 4 // RGBA 8:8:8:8 is 4 bytes per pixel + +enum { + SPR_NONE, + SPR_8x8, + SPR_8x16, + SPR_16x16_MSX +}; + +struct PNGImage +{ + vector< unsigned char > data; //data in indexed format + unsigned int w; + unsigned int h; + + // Default tile size + int tile_w = 8; + int tile_h = 16; + + // TODO: embed these instead of deriving them many places in the code + // int attribute_w_factor = 1; + // int attribute_h_factor = 1; + // int get_attribute_tile_w() { tile_w * attribute_w_factor; } + // int get_attribute_tile_h() { tile_h * attribute_h_factor; } + + size_t colors_per_pal; // Number of colors per palette (ex: CGB has 4 colors per palette x 8 palettes total) + size_t total_color_count; // Total number of colors across all palettes (palette_count x colors_per_pal) + unsigned char* palette; //palette colors in RGBA (1 color == 4 bytes) + +private: + bool zero_palette = false; + +public: + unsigned char GetGBColor(int x, int y) + { + return data[w * y + x] % colors_per_pal; + } + + + // This needs separate tile_w and tile_h params since + // MSX tile extraction uses it to pull out the 4 sub-tiles + bool ExtractGBTile(int x, int y, int extract_tile_w, int extract_tile_h, Tile& tile, int buffer_offset) + { + // Set the palette to 0 when pals are not stored in tiles to allow tiles to be equal even when their palettes are different + tile.pal = zero_palette ? 0 : data[w * y + x] >> 2; + + bool all_zero = true; + for(int j = 0; j < extract_tile_h; ++j) + { + for(int i = 0; i < extract_tile_w; ++i) + { + unsigned char color_idx = GetGBColor(x + i, y + j); + tile.data[(j * extract_tile_w) + i + buffer_offset] = color_idx; + all_zero = all_zero && (color_idx == 0); + } + } + return !all_zero; + } + + bool ExtractTile_MSX16x16(int x, int y, Tile& tile) + { + // MSX 16x16 sprite tiles are composed of four 8x8 tiles in this order UL, LL, UR, LR + bool UL_notempty, LL_notempty, UR_notempty, LR_notempty; + + // Call these separately since otherwise some get optimized out during + // runtime if any single one before it returns false + UL_notempty = ExtractGBTile(x, y, 8, 8, tile, 0); + LL_notempty = ExtractGBTile(x, y + 8, 8, 8, tile, ((8 * 8) * 1)); + UR_notempty = ExtractGBTile(x + 8, y, 8, 8, tile, ((8 * 8) * 2)); + LR_notempty = ExtractGBTile(x + 8, y + 8, 8, 8, tile, ((8 * 8) * 3)); + return (UL_notempty || LL_notempty || UR_notempty || LR_notempty); + } + + bool ExtractTile(int x, int y, Tile& tile, int sprite_mode, bool export_as_map, bool use_map_attributes) + { + // Set the palette to 0 when pals are not stored in tiles to allow tiles to be equal even when their palettes are different + zero_palette = !(export_as_map && !use_map_attributes); + + if(sprite_mode == SPR_16x16_MSX) + return ExtractTile_MSX16x16(x, y, tile); + else + return ExtractGBTile(x, y, tile_w, tile_h, tile, 0); // No buffer offset for normal tile extraction + } + // private: + // bool zero_palette = false; +}; diff --git a/gbdk-support/png2asset/process_arguments.cpp b/gbdk-support/png2asset/process_arguments.cpp @@ -0,0 +1,281 @@ +#include <vector> +#include <string> +#include <algorithm> +#include <cstring> +#include <set> +#include <stdio.h> +#include <fstream> +#include <cstdint> + +#include "lodepng.h" +#include "mttile.h" +#include "export.h" +#include "map_attributes.h" +#include "palettes.h" + +#include "cmp_int_color.h" + +#include "image_utils.h" +#include "image_data.h" +#include "maps.h" +#include "metasprites.h" +#include "metasprites_functions.h" +#include "process_arguments.h" +#include "png_image.h" +#include "tiles.h" + + +using namespace std; + +int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) { + + //default values for some params + args->spriteSize.width = 0; + args->spriteSize.height = 0; + args->pivot.x = 0xFFFFFF; + args->pivot.y = 0xFFFFFF; + args->pivot.width = 0xFFFFFF; + args->pivot.height = 0xFFFFFF; + args->bank = -1; + args->keep_palette_order = false; + args->repair_indexed_pal = false; + args->output_binary = false; + args->output_transposed = false; + args->max_palettes = 8; + + args->pack_mode = Tile::GB; + args->flip_tiles = true; + args->keep_duplicate_tiles = false; + args->include_palettes = true; + args->includedMapOrMetaspriteData = true; + args->includeTileData = true; + args->use_structs = false; + args->export_as_map = false; + args->use_map_attributes = false; + args->use_2x2_map_attributes = false; + args->pack_map_attributes = false; + args->convert_rgb_to_nes = false; + args->map_attributes_size.width = 0; + args->map_attributes_size.height = 0; + args->map_attributes_packed_size.width = 0; + args->map_attributes_packed_size.height = 0; + args->sprite_mode = SPR_8x16; + args->source_tileset_size = 0; + args->source_total_color_count = 0; + + args->bpp = 2; + args->tile_origin = 0; // Default to no tile index offset + + + if(argc < 2) + { + printf("usage: png2asset <file>.png [options]\n"); + printf("-o ouput file (default: <png file>.c)\n"); + printf("-c deprecated, same as -o\n"); + printf("-sw <width> metasprites width size (default: png width)\n"); + printf("-sh <height> metasprites height size (default: png height)\n"); + printf("-sp <props> change default for sprite OAM property bytes (in hex) (default: 0x00)\n"); + printf("-px <x coord> metasprites pivot x coordinate (default: metasprites width / 2)\n"); + printf("-py <y coord> metasprites pivot y coordinate (default: metasprites height / 2)\n"); + printf("-pw <width> metasprites collision rect width (default: metasprites width)\n"); + printf("-ph <height> metasprites collision rect height (default: metasprites height)\n"); + printf("-spr8x8 use SPRITES_8x8\n"); + printf("-spr8x16 use SPRITES_8x16 (this is the default)\n"); + printf("-spr16x16msx use SPRITES_16x16\n"); + printf("-b <bank> bank (default: fixed bank)\n"); + printf("-keep_palette_order use png palette\n"); + printf("-repair_indexed_pal try to repair indexed tile palettes (implies \"-keep_palette_order\")\n"); + printf("-noflip disable tile flip\n"); + printf("-map Export as map (tileset + bg)\n"); + printf("-use_map_attributes Use CGB BG Map attributes\n"); + printf("-use_nes_attributes Use NES BG Map attributes\n"); + printf("-use_nes_colors Convert RGB color values to NES PPU colors\n"); + printf("-use_structs Group the exported info into structs (default: false) (used by ZGB Game Engine)\n"); + printf("-bpp bits per pixel: 1, 2, 4 (default: 2)\n"); + printf("-max_palettes max number of palettes allowed (default: 8)\n"); + printf(" (note: max colors = max_palettes x num colors per palette)\n"); + printf("-pack_mode gb, nes, sgb, sms, 1bpp (default: gb)\n"); + printf("-tile_origin tile index offset for maps (default: 0)\n"); + + printf("-tiles_only export tile data only\n"); + printf("-maps_only export map tilemap only\n"); + printf("-metasprites_only export metasprite descriptors only\n"); + printf("-source_tileset use source tileset (image with common tiles)\n"); + printf("-keep_duplicate_tiles do not remove duplicate tiles (default: not enabled)\n"); + printf("-no_palettes do not export palette data\n"); + + printf("-bin export to binary format\n"); + printf("-transposed export transposed (column-by-column instead of row-by-row)\n"); + + return 0; + } + + //default params + args->input_filename = argv[1]; + args->output_filename = argv[1]; + args->output_filename = args->output_filename.substr(0, args->output_filename.size() - 4) + ".c"; + + //Parse argv + for(int i = 2; i < argc; ++i) + { + if(!strcmp(argv[i], "-sw")) + { + args->spriteSize.width = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-sh")) + { + args->spriteSize.height = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-sp")) + { + args->props_default = strtol(argv[++i], NULL, 16); + } + if(!strcmp(argv[i], "-px")) + { + args->pivot.x = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-py")) + { + args->pivot.y = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-pw")) + { + args->pivot.width = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-ph")) + { + args->pivot.height = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-spr8x8")) + { + args->sprite_mode = SPR_8x8; + } + else if(!strcmp(argv[i], "-spr8x16")) + { + args->sprite_mode = SPR_8x16; + } + else if(!strcmp(argv[i], "-spr16x16msx")) + { + args->sprite_mode = SPR_16x16_MSX; + } + else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "-o")) + { + args->output_filename = argv[++i]; + } + else if(!strcmp(argv[i], "-b")) + { + args->bank = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-keep_palette_order")) + { + args->keep_palette_order = true; + } + else if(!strcmp(argv[i], "-repair_indexed_pal")) + { + args->repair_indexed_pal = true; + args->keep_palette_order = true; // -repair_indexed_pal requires -keep_palette_order, so force it on + } + else if(!strcmp(argv[i], "-noflip")) + { + args->flip_tiles = false; + } + else if(!strcmp(argv[i], "-map")) + { + args->export_as_map = true; + } + else if(!strcmp(argv[i], "-use_map_attributes")) + { + args->use_map_attributes = true; + } + else if(!strcmp(argv[i], "-use_nes_attributes")) + { + args->use_map_attributes = true; + args->use_2x2_map_attributes = true; + args->pack_map_attributes = true; + } + else if(!strcmp(argv[i], "-use_nes_colors")) + { + args->convert_rgb_to_nes = true; + } + else if(!strcmp(argv[i], "-use_structs")) + { + args->use_structs = true; + } + else if(!strcmp(argv[i], "-bpp")) + { + args->bpp = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-max_palettes")) + { + args->max_palettes = atoi(argv[++i]); + if(args->max_palettes == 0) + { + printf("-max_palettes must be larger than zero\n"); + return 1; + } + } + else if(!strcmp(argv[i], "-pack_mode")) + { + std::string pack_mode_str = argv[++i]; + if(pack_mode_str == "gb") args->pack_mode = Tile::GB; + else if(pack_mode_str == "nes") args->pack_mode = Tile::NES; + else if(pack_mode_str == "sgb") args->pack_mode = Tile::SGB; + else if(pack_mode_str == "sms") args->pack_mode = Tile::SMS; + else if(pack_mode_str == "1bpp") args->pack_mode = Tile::BPP1; + else + { + printf("-pack_mode must be one of gb, nes, sgb, sms, 1bpp\n"); + return 1; + } + } + else if(!strcmp(argv[i], "-tile_origin")) + { + args->tile_origin = atoi(argv[++i]); + } + else if(!strcmp(argv[i], "-maps_only") || !strcmp(argv[i], "-metasprites_only")) + { + args->includeTileData = false; + } + else if(!strcmp(argv[i], "-tiles_only")) + { + args->includedMapOrMetaspriteData = false; + } + else if(!strcmp(argv[i], "-keep_duplicate_tiles")) + { + args->keep_duplicate_tiles = true; + } + else if(!strcmp(argv[i], "-no_palettes")) + { + args->include_palettes = false; + } + else if(!strcmp(argv[i], "-source_tileset")) + { + args->includeTileData = false; + args->source_tilesets.push_back(argv[++i]); + } + else if(!strcmp(argv[i], "-bin")) + { + args->output_binary = true; + } + else if(!strcmp(argv[i], "-transposed")) + { + args->output_transposed = true; + } + } + + + int slash_pos = (int)args->output_filename.find_last_of('/'); + if(slash_pos == -1) + slash_pos = (int)args->output_filename.find_last_of('\\'); + int dot_pos = (int)args->output_filename.find_first_of('.', slash_pos == -1 ? 0 : slash_pos); + + args->output_filename_h = args->output_filename.substr(0, dot_pos) + ".h"; + args->output_filename_bin = args->output_filename.substr(0, dot_pos) + "_map.bin"; + args->output_filename_attributes_bin = args->output_filename.substr(0, dot_pos) + "_map_attributes.bin"; + args->output_filename_tiles_bin = args->output_filename.substr(0, dot_pos) + "_tiles.bin"; + args->data_name = args->output_filename.substr(slash_pos + 1, dot_pos - 1 - slash_pos); + replace(args->data_name.begin(), args->data_name.end(), '-', '_'); + + return 0; +} + diff --git a/gbdk-support/png2asset/process_arguments.h b/gbdk-support/png2asset/process_arguments.h @@ -0,0 +1,68 @@ +#pragma once + + +#include <vector> +#include <string> +#include "vector2.h" +#include "mttile.h" +#include "tiles.h" +#include "metasprites.h" +#include "cmp_int_color.h" + +using namespace std; + + +struct PNG2AssetArguments { + + Vector2Size spriteSize; + Vector2Size map_attributes_size; + Vector2Size map_attributes_packed_size; + + Rectangle pivot; + + //default values for some params + + string output_filename_h; + string output_filename_bin; + string output_filename_attributes_bin; + string output_filename_tiles_bin; + string data_name; + string output_filename; + string input_filename; + vector<string> source_tilesets; + + size_t max_palettes; + + bool keep_palette_order; + bool repair_indexed_pal; + bool output_binary; + bool output_transposed; + bool export_as_map; + bool use_map_attributes; + bool use_2x2_map_attributes; + bool pack_map_attributes; + bool convert_rgb_to_nes; + bool includeTileData; + bool includedMapOrMetaspriteData; + bool keep_duplicate_tiles; + bool include_palettes; + bool use_structs; + bool flip_tiles; + + int errorCode; + int bank; + int sprite_mode; + int bpp; + int props_default; // Default Sprite props has no attributes enabled + + unsigned int tile_origin; // Default to no tile index offset + size_t extra_tile_count; + size_t source_total_color_count; // Total number of colors (palette_count x colors_per_palette) + unsigned int source_tileset_size; + + Tile::PackMode pack_mode; + +}; + + +int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args); + diff --git a/gbdk-support/png2asset/rgb_to_nes_lut.h b/gbdk-support/png2asset/rgb_to_nes_lut.h @@ -0,0 +1,2 @@ +#pragma once +extern unsigned char rgb_to_nes[64]; + diff --git a/gbdk-support/png2asset/tiles.cpp b/gbdk-support/png2asset/tiles.cpp @@ -0,0 +1,29 @@ +#include "tiles.h" + +Tile FlipH(const Tile& tile) +{ + Tile ret; + for(int j = (int)tile.data.size() - 8; j >= 0; j -= 8) + { + for(int i = 0; i < 8; ++i) + { + ret.data.push_back(tile.data[j + i]); + } + } + ret.pal = tile.pal; + return ret; +} + +Tile FlipV(const Tile& tile) +{ + Tile ret; + for(int j = 0; j < (int)tile.data.size(); j += 8) + { + for(int i = 7; i >= 0; --i) + { + ret.data.push_back(tile.data[j + i]); + } + } + ret.pal = tile.pal; + return ret; +} diff --git a/gbdk-support/png2asset/tiles.h b/gbdk-support/png2asset/tiles.h @@ -0,0 +1,96 @@ +#pragma once +#include <vector> + +using namespace std; + +#define BIT(VALUE, INDEX) (1 & ((VALUE) >> (INDEX))) + +struct Tile +{ + vector< unsigned char > data; + unsigned char pal; + + Tile(size_t size = 0) : data(size), pal(0) {} + bool operator==(const Tile& t) const + { + return data == t.data && pal == t.pal; + } + + const Tile& operator=(const Tile& t) + { + data = t.data; + pal = t.pal; + return *this; + } + + enum PackMode { + GB, + NES, + SGB, + SMS, + BPP1 + }; + + vector< unsigned char > GetPackedData(PackMode pack_mode, int tile_w, int tile_h, int bpp) { + vector< unsigned char > ret((tile_w / 8) * tile_h * bpp, 0); + if(pack_mode == GB) { + for(int j = 0; j < tile_h; ++j) { + for(int i = 0; i < 8; ++i) { + unsigned char col = data[8 * j + i]; + ret[j * 2] |= BIT(col, 0) << (7 - i); + ret[j * 2 + 1] |= BIT(col, 1) << (7 - i); + } + } + } + else if(pack_mode == NES) { + for(int j = 0; j < tile_h; ++j) { + for(int i = 0; i < 8; ++i) { + unsigned char col = data[8 * j + i]; + ret[j] |= BIT(col, 0) << (7 - i); + ret[j + 8] |= BIT(col, 1) << (7 - i); + } + } + } + else if(pack_mode == SGB) + { + for(int j = 0; j < tile_h; ++j) { + for(int i = 0; i < 8; ++i) { + unsigned char col = data[8 * j + i]; + ret[j * 2] |= BIT(col, 0) << (7 - i); + ret[j * 2 + 1] |= BIT(col, 1) << (7 - i); + ret[(tile_h + j) * 2] |= BIT(col, 2) << (7 - i); + ret[(tile_h + j) * 2 + 1] |= BIT(col, 3) << (7 - i); + } + } + } + else if(pack_mode == SMS) + { + for(int j = 0; j < tile_h; ++j) { + for(int i = 0; i < 8; ++i) { + unsigned char col = data[8 * j + i]; + ret[j * 4] |= BIT(col, 0) << (7 - i); + ret[j * 4 + 1] |= BIT(col, 1) << (7 - i); + ret[j * 4 + 2] |= BIT(col, 2) << (7 - i); + ret[j * 4 + 3] |= BIT(col, 3) << (7 - i); + } + } + } + else if(pack_mode == BPP1) + { + // Packs 8 pixel wide rows in order set by ExtractTile**() + // Process all rows of pixels in the tile + for(int j = 0; j < ((tile_w / 8) * tile_h); j++) { + // Pack each row of 8 pixels into one byte + for(int i = 0; i < 8; i++) { + unsigned char col = data[8 * j + i]; + ret[j] |= BIT(col, 0) << (7 - i); + } + } + } + return ret; + } +}; + + +Tile FlipH(const Tile& tile); +Tile FlipV(const Tile& tile); + diff --git a/gbdk-support/png2asset/vector2.h b/gbdk-support/png2asset/vector2.h @@ -0,0 +1,19 @@ +#pragma once + +struct Vector2Position { + + int x, y; +}; + + +struct Vector2Size { + + size_t width,height; +}; + + +struct Rectangle { + + int x, y; + size_t width, height; +};

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