diff --git a/ion/src/simulator/linux/Makefile b/ion/src/simulator/linux/Makefile index 50387ed01..4b2917d05 100644 --- a/ion/src/simulator/linux/Makefile +++ b/ion/src/simulator/linux/Makefile @@ -27,20 +27,20 @@ endif LDFLAGS += -ljpeg -jpg_assets = background horizontal_arrow vertical_arrow round small_squircle large_squircle +assets = background.jpg horizontal_arrow.png vertical_arrow.png round.png small_squircle.png large_squircle.png -jpg_images = $(foreach i,$(jpg_assets),ion/src/simulator/assets/$(i).jpg) +assets_paths = $(foreach i,$(assets),ion/src/simulator/assets/$(i)) $(eval $(call rule_for, \ LINUX_ASSETS, \ ion/src/simulator/linux/assets.s, \ - $(jpg_images), \ - $$(PYTHON) ion/src/simulator/linux/assets.py --files $(jpg_assets) --implementation $$@, \ + $(assets_paths), \ + $$(PYTHON) ion/src/simulator/linux/assets.py --files $(assets) --implementation $$@, \ global \ )) -assets_address_ranges_declaration = $(foreach i,$(jpg_assets),extern unsigned char _ion_simulator_$(i)_start;) -assets_address_ranges_declaration += $(foreach i,$(jpg_assets),extern unsigned char _ion_simulator_$(i)_end;) -assets_address_ranges_definition = $(foreach i,$(jpg_assets), {"$(i).jpg", &_ion_simulator_$(i)_start, &_ion_simulator_$(i)_end},) +assets_address_ranges_declaration = $(foreach i,$(assets),extern unsigned char _ion_simulator_$(basename $(i))_start;) +assets_address_ranges_declaration += $(foreach i,$(assets),extern unsigned char _ion_simulator_$(basename $(i))_end;) +assets_address_ranges_definition = $(foreach i,$(assets), {"$(i)", &_ion_simulator_$(basename $(i))_start, &_ion_simulator_$(basename $(i))_end},) $(call object_for,ion/src/simulator/linux/images.cpp): CXXFLAGS += -DASSETS_ADDRESS_RANGES_DECLARATION='$(assets_address_ranges_declaration)' -DASSETS_ADDRESS_RANGES_DEFINITION='$(assets_address_ranges_definition)' diff --git a/ion/src/simulator/linux/assets.py b/ion/src/simulator/linux/assets.py index 31fd20f68..dc5b72953 100644 --- a/ion/src/simulator/linux/assets.py +++ b/ion/src/simulator/linux/assets.py @@ -1,27 +1,29 @@ -# This script generates a .s file representing jpg assets in order to access +# This script generates a .s file representing assets in order to access # them from C code import sys import re import argparse import io +import os -parser = argparse.ArgumentParser(description="Process some jpg files.") -parser.add_argument('--files', nargs='+', help='a list of jpg file names') +parser = argparse.ArgumentParser(description="Process some asset files.") +parser.add_argument('--files', nargs='+', help='a list of file names') parser.add_argument('--implementation', help='the .s file to generate') args = parser.parse_args() -def print_jpg(f, jpg): - f.write(".global _ion_simulator_" + jpg + "_start\n") - f.write(".global _ion_simulator_" + jpg + "_end\n") - f.write("_ion_simulator_" + jpg + "_start:\n") - f.write(" .incbin \"ion/src/simulator/assets/" + jpg + ".jpg\"\n") - f.write("_ion_simulator_" + jpg + "_end:\n\n") +def print_asset(f, asset): + asset_basename = os.path.splitext(asset)[0] + f.write(".global _ion_simulator_" + asset_basename + "_start\n") + f.write(".global _ion_simulator_" + asset_basename + "_end\n") + f.write("_ion_simulator_" + asset_basename + "_start:\n") + f.write(" .incbin \"ion/src/simulator/assets/" + asset + "\"\n") + f.write("_ion_simulator_" + asset_basename + "_end:\n\n") def print(files, path): f = open(path, "w") - for jpg in files: - print_jpg(f, jpg) + for asset in files: + print_asset(f, asset) f.close() diff --git a/ion/src/simulator/linux/images.cpp b/ion/src/simulator/linux/images.cpp index 632a0bcbf..694767b65 100644 --- a/ion/src/simulator/linux/images.cpp +++ b/ion/src/simulator/linux/images.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #ifndef ASSETS_ADDRESS_RANGES_DECLARATION @@ -17,66 +18,152 @@ static struct { ASSETS_ADDRESS_RANGES_DEFINITION }; -SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identifier, bool withTransparency, uint8_t alpha) { +enum class AssetFormat { + JPG, + PNG +}; + +png_size_t readSize = 0; + +void ReadDataFromMemory(png_structp png, png_bytep outBytes, png_size_t byteSize) { + png_voidp io = png_get_io_ptr(png); + memcpy((char *)outBytes, (char *)io + readSize, byteSize); + readSize += byteSize; +} + +bool readPNG(unsigned char * start, size_t size, unsigned char ** bitmapData, unsigned int * width, unsigned int * height, unsigned int * bytesPerPixel, Uint32 * pixelFormat) { + readSize = 0; + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png) { return false; } // Memory failure + png_infop info = png_create_info_struct(png); + if (!info) { + // Memory failure + png_destroy_read_struct(&png, NULL, NULL); + return false; + } + static constexpr size_t k_pngSignatureLength = 8; + if(png_sig_cmp((png_const_bytep)start, 0, k_pngSignatureLength) != 0) { return false; } // Corrupted PNG + + png_set_read_fn(png, start, ReadDataFromMemory); + + png_read_info(png, info); + + int bitDepth = 0; + int colorType = -1; + png_get_IHDR(png, info, width, height, &bitDepth, &colorType, nullptr, nullptr, nullptr); + *bytesPerPixel = bitDepth*4/8; + + *pixelFormat = SDL_PIXELFORMAT_ARGB8888; + assert(colorType == PNG_COLOR_TYPE_RGB_ALPHA); + + const png_uint_32 bytesPerRow = png_get_rowbytes(png, info); + *bitmapData = new unsigned char[*height * bytesPerRow]; + unsigned char * rowData = *bitmapData; + // read single row at a time + for(unsigned int rowIndex = 0; rowIndex < *height; rowIndex++) { + png_read_row(png, (png_bytep)rowData, nullptr); + rowData += bytesPerRow; + } + + png_destroy_read_struct(&png, &info, nullptr); + return true; +} + +bool readJPG(const unsigned char * start, size_t size, unsigned char ** bitmapData, unsigned int * width, unsigned int * height, unsigned int * bytesPerPixel, Uint32 * pixelFormat) { + *pixelFormat = SDL_PIXELFORMAT_RGB24; struct jpeg_decompress_struct info; struct jpeg_error_mgr err; info.err = jpeg_std_error(&err); jpeg_create_decompress(&info); - unsigned char * jpegStart = nullptr; - unsigned long jpegSize = 0; - - for (size_t i = 0; i < sizeof(resources_addresses)/sizeof(resources_addresses[0]); i++) { - if (strcmp(identifier, resources_addresses[i].identifier) == 0) { - jpegStart = resources_addresses[i].start; - jpegSize = resources_addresses[i].end - resources_addresses[i].start; - break; - } - } - assert(jpegStart); - jpeg_mem_src(&info, jpegStart, jpegSize); + jpeg_mem_src(&info, start, size); if (jpeg_read_header(&info, TRUE) != 1) { - return nullptr; + return false; } jpeg_start_decompress(&info); - int width = info.output_width; - int height = info.output_height; - int bytesPerPixel = info.output_components; - int bitsPerPixel = bytesPerPixel*8; - - unsigned char * bitmapData = new unsigned char[height * width * bytesPerPixel]; + *width = info.output_width; + *height = info.output_height; + *bytesPerPixel = info.output_components; + *bitmapData = new unsigned char[*height * *width * *bytesPerPixel]; while (info.output_scanline < info.output_height) { unsigned char * buffer_array[1]; - buffer_array[0] = bitmapData + info.output_scanline * width * bytesPerPixel; + buffer_array[0] = *bitmapData + info.output_scanline * *width * *bytesPerPixel; jpeg_read_scanlines(&info, buffer_array, 1); } jpeg_finish_decompress(&info); jpeg_destroy_decompress(&info); + return true; +} - Uint32 surfacePixelFormat = SDL_PIXELFORMAT_RGB24; - assert(bytesPerPixel == SDL_BYTESPERPIXEL(surfacePixelFormat)); +SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identifier) { + static constexpr const char * jpgExtension = ".jpg"; + static constexpr const char * pngExtension = ".png"; - SDL_Surface * surface = SDL_CreateRGBSurfaceWithFormatFrom( - bitmapData, - width, - height, - bitsPerPixel, - width * bytesPerPixel, - surfacePixelFormat); + unsigned char * assetStart = nullptr; + unsigned long assertSize = 0; + AssetFormat format; - SDL_SetColorKey(surface, withTransparency, SDL_MapRGB(surface->format, 0xFF, 0xFF, 0xFF)); - SDL_SetSurfaceAlphaMod(surface, alpha); + // Find the asset corresponding to identifier + for (size_t i = 0; i < sizeof(resources_addresses)/sizeof(resources_addresses[0]); i++) { + if (strcmp(identifier, resources_addresses[i].identifier) == 0) { + if (strcmp(jpgExtension, identifier + strlen(identifier) - strlen(jpgExtension)) == 0) { + format = AssetFormat::JPG; + } else { + assert(strcmp(pngExtension, identifier + strlen(identifier) - strlen(pngExtension)) == 0); + format = AssetFormat::PNG; + } + assetStart = resources_addresses[i].start; + assertSize = resources_addresses[i].end - resources_addresses[i].start; + break; + } + } + assert(assetStart); - SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface); + unsigned int width; + unsigned int height; + unsigned int bytesPerPixel; + unsigned char * bitmapData; + Uint32 pixelFormat; + + switch (format) { + case AssetFormat::JPG: + if (!readJPG(assetStart, assertSize, &bitmapData, &width, &height, &bytesPerPixel, &pixelFormat)) { + return nullptr; + } + break; + default: + assert(format == AssetFormat::PNG); + if (!readPNG(assetStart, assertSize, &bitmapData, &width, &height, &bytesPerPixel, &pixelFormat)) { + return nullptr; + } + } + + assert(bytesPerPixel == SDL_BYTESPERPIXEL(pixelFormat)); + + SDL_Texture * texture = SDL_CreateTexture( + renderer, + pixelFormat, + SDL_TEXTUREACCESS_STATIC, + width, + height + ); + + SDL_UpdateTexture( + texture, + nullptr, + bitmapData, + width * bytesPerPixel + ); + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); delete[] bitmapData; - SDL_FreeSurface(surface); return texture; }