From 00e5c4418d038abf72c0d690da11e8343a4ac015 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Tue, 29 Dec 2015 14:32:51 +0000 Subject: [PATCH] refactor image IO to new source file --- openrct2.vcxproj | 2 + openrct2.vcxproj.filters | 6 + src/drawing/drawing.h | 11 ++ src/image_io.c | 258 +++++++++++++++++++++++++++++++++++ src/image_io.h | 10 ++ src/interface/screenshot.c | 271 ++----------------------------------- 6 files changed, 302 insertions(+), 256 deletions(-) create mode 100644 src/image_io.c create mode 100644 src/image_io.h diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 9eba05d0b3..a430d4746c 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -49,6 +49,7 @@ + @@ -218,6 +219,7 @@ + diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters index bd11a5e391..4d1a1f4363 100644 --- a/openrct2.vcxproj.filters +++ b/openrct2.vcxproj.filters @@ -558,6 +558,9 @@ Source\Windows + + Source + @@ -839,5 +842,8 @@ Source\Core + + Source + \ No newline at end of file diff --git a/src/drawing/drawing.h b/src/drawing/drawing.h index f4a6f885f7..0cd56e2cfb 100644 --- a/src/drawing/drawing.h +++ b/src/drawing/drawing.h @@ -69,6 +69,17 @@ typedef struct { void *data; } rct_gx; +typedef struct { + uint8 blue; + uint8 green; + uint8 red; + uint8 alpha; +} rct_palette_entry; + +typedef struct { + rct_palette_entry entries[256]; +} rct_palette; + #define SPRITE_ID_PALETTE_COLOUR_1(colourId) ((IMAGE_TYPE_USE_PALETTE << 28) | ((colourId) << 19)) extern const uint16 palette_to_g1_offset[]; diff --git a/src/image_io.c b/src/image_io.c new file mode 100644 index 0000000000..0dbb87ace5 --- /dev/null +++ b/src/image_io.c @@ -0,0 +1,258 @@ +#ifdef USE_LIBPNG + #include +#else + #include +#endif + +#include "image_io.h" + +#ifdef USE_LIBPNG + static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length); + static void my_png_flush(png_structp png_ptr); +#endif + +bool image_io_png_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path) +{ +#ifdef USE_LIBPNG + // Get image size + int stride = dpi->width + dpi->pitch; + + // Setup PNG + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return false; + } + + png_colorp png_palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); + for (int i = 0; i < 256; i++) { + const rct_palette_entry *entry = &palette->entries[i]; + png_palette[i].blue = entry->blue; + png_palette[i].green = entry->green; + png_palette[i].red = entry->red; + } + + png_set_PLTE(png_ptr, info_ptr, png_palette, PNG_MAX_PALETTE_LENGTH); + + // Open file for writing + SDL_RWops *file = SDL_RWFromFile(path, "wb"); + if (file == NULL) { + png_free(png_ptr, png_palette); + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return false; + } + png_set_write_fn(png_ptr, file, my_png_write_data, my_png_flush); + + // Set error handler + if (setjmp(png_jmpbuf(png_ptr))) { + png_free(png_ptr, png_palette); + png_destroy_write_struct(&png_ptr, &info_ptr); + SDL_RWclose(file); + return false; + } + + // Write header + png_set_IHDR( + png_ptr, info_ptr, dpi->width, dpi->height, 8, + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT + ); + png_write_info(png_ptr, info_ptr); + + // Write pixels + uint8 *bits = dpi->bits; + for (int y = 0; y < dpi->height; y++) { + png_write_row(png_ptr, (png_const_bytep)bits); + bits += stride; + } + + // Finish + png_write_end(png_ptr, NULL); + SDL_RWclose(file); + + png_free(png_ptr, png_palette); + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +#else + unsigned int error; + unsigned char* png; + size_t pngSize; + LodePNGState state; + + lodepng_state_init(&state); + state.info_raw.colortype = LCT_PALETTE; + + // Get image size + int stride = dpi->width + dpi->pitch; + + for (int i = 0; i < 256; i++) { + const rct_palette_entry *entry = &palette->entries[i]; + uint8 r = entry->red; + uint8 g = entry->green; + uint8 b = entry->blue; + uint8 a = 255; + lodepng_palette_add(&state.info_raw, r, g, b, a); + } + + error = lodepng_encode(&png, &pngSize, dpi->bits, stride, dpi->height, &state); + if (error != 0) { + free(png); + return false; + } else { + SDL_RWops *file = SDL_RWFromFile(path, "wb"); + if (file == NULL) { + free(png); + return false; + } + SDL_RWwrite(file, png, pngSize, 1); + SDL_RWclose(file); + } + + free(png); + return true; +#endif +} + +#ifdef USE_LIBPNG + +static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + SDL_RWops *file = (SDL_RWops*)png_get_io_ptr(png_ptr); + SDL_RWwrite(file, data, length, 1); +} + +static void my_png_flush(png_structp png_ptr) +{ + +} + +#endif + +// Bitmap header structs, for cross platform purposes +typedef struct { + uint16 bfType; + uint32 bfSize; + uint16 bfReserved1; + uint16 bfReserved2; + uint32 bfOffBits; +} BitmapFileHeader; + +typedef struct { + uint32 biSize; + sint32 biWidth; + sint32 biHeight; + uint16 biPlanes; + uint16 biBitCount; + uint32 biCompression; + uint32 biSizeImage; + sint32 biXPelsPerMeter; + sint32 biYPelsPerMeter; + uint32 biClrUsed; + uint32 biClrImportant; +} BitmapInfoHeader; + +/** + * + * rct2: 0x00683D20 + */ +bool image_io_bmp_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path) +{ + BitmapFileHeader header; + BitmapInfoHeader info; + + int i, y, width, height, stride; + uint8 *buffer, *row; + SDL_RWops *fp; + unsigned int bytesWritten; + + // Open binary file for writing + if ((fp = SDL_RWFromFile(path, "wb")) == NULL){ + return false; + } + + // Allocate buffer + buffer = malloc(0xFFFF); + if (buffer == NULL) { + SDL_RWclose(fp); + return false; + } + + // Get image size + width = dpi->width; + height = dpi->height; + stride = dpi->width + dpi->pitch; + + // File header + memset(&header, 0, sizeof(header)); + header.bfType = 0x4D42; + header.bfSize = height * stride + 1038; + header.bfOffBits = 1038; + + bytesWritten = SDL_RWwrite(fp, &header, sizeof(BitmapFileHeader), 1); + if (bytesWritten != 1) { + SDL_RWclose(fp); + SafeFree(buffer); + log_error("failed to save screenshot"); + return false; + } + + // Info header + memset(&info, 0, sizeof(info)); + info.biSize = sizeof(info); + info.biWidth = width; + info.biHeight = height; + info.biPlanes = 1; + info.biBitCount = 8; + info.biXPelsPerMeter = 2520; + info.biYPelsPerMeter = 2520; + info.biClrUsed = 246; + + bytesWritten = SDL_RWwrite(fp, &info, sizeof(BitmapInfoHeader), 1); + if (bytesWritten != 1) { + SDL_RWclose(fp); + SafeFree(buffer); + log_error("failed to save screenshot"); + return false; + } + + // Palette + memset(buffer, 0, 246 * 4); + for (i = 0; i < 246; i++) { + const rct_palette_entry *entry = &palette->entries[i]; + buffer[i * 4 + 0] = entry->blue; + buffer[i * 4 + 1] = entry->green; + buffer[i * 4 + 2] = entry->red; + } + + bytesWritten = SDL_RWwrite(fp, buffer, sizeof(char), 246 * 4); + if (bytesWritten != 246*4){ + SDL_RWclose(fp); + SafeFree(buffer); + log_error("failed to save screenshot"); + return false; + } + + // Image, save upside down + for (y = dpi->height - 1; y >= 0; y--) { + row = dpi->bits + y * (dpi->width + dpi->pitch); + + memset(buffer, 0, stride); + memcpy(buffer, row, dpi->width); + + bytesWritten = SDL_RWwrite(fp, buffer, sizeof(char), stride); + if (bytesWritten != stride){ + SDL_RWclose(fp); + SafeFree(buffer); + log_error("failed to save screenshot"); + return false; + } + } + + SDL_RWclose(fp); + free(buffer); + return true; +} diff --git a/src/image_io.h b/src/image_io.h new file mode 100644 index 0000000000..70ce4cdcc7 --- /dev/null +++ b/src/image_io.h @@ -0,0 +1,10 @@ +#ifndef _IMAGE_IO_H_ +#define _IMAGE_IO_H_ + +#include "common.h" +#include "drawing/drawing.h" + +bool image_io_png_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path); +bool image_io_bmp_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path); + +#endif diff --git a/src/interface/screenshot.c b/src/interface/screenshot.c index 7966fecaed..81b7693e0e 100644 --- a/src/interface/screenshot.c +++ b/src/interface/screenshot.c @@ -18,18 +18,11 @@ * along with this program. If not, see . *****************************************************************************/ -#pragma pack(1) - -#ifdef USE_LIBPNG - #include -#else - #include -#endif - #include "../addresses.h" #include "../config.h" #include "../drawing/drawing.h" #include "../game.h" +#include "../image_io.h" #include "../localisation/localisation.h" #include "../openrct2.h" #include "../platform/platform.h" @@ -43,13 +36,6 @@ static const char *_screenshot_format_extension[] = { ".bmp", ".png" }; static int screenshot_dump_bmp(); static int screenshot_dump_png(); -bool screenshot_write_png(rct_drawpixelinfo *dpi, const char *path); - -#ifdef USE_LIBPNG - static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length); - static void my_png_flush(png_structp png_ptr); -#endif - /** * * rct2: 0x006E3AEC @@ -122,135 +108,26 @@ int screenshot_dump() } } -// Bitmap header structs, for cross platform purposes -typedef struct { - uint16 bfType; - uint32 bfSize; - uint16 bfReserved1; - uint16 bfReserved2; - uint32 bfOffBits; -} BitmapFileHeader; - -typedef struct { - uint32 biSize; - sint32 biWidth; - sint32 biHeight; - uint16 biPlanes; - uint16 biBitCount; - uint32 biCompression; - uint32 biSizeImage; - sint32 biXPelsPerMeter; - sint32 biYPelsPerMeter; - uint32 biClrUsed; - uint32 biClrImportant; -} BitmapInfoHeader; - /** * * rct2: 0x00683D20 */ int screenshot_dump_bmp() { - BitmapFileHeader header; - BitmapInfoHeader info; - - int i, y, index, width, height, stride; - char path[MAX_PATH]; - uint8 *buffer, *row; - SDL_RWops *fp; - unsigned int bytesWritten; - // Get a free screenshot path - if ((index = screenshot_get_next_path(path, SCREENSHOT_FORMAT_BMP)) == -1) - return -1; - - // Open binary file for writing - if ((fp = SDL_RWFromFile(path, "wb")) == NULL){ + int index; + char path[MAX_PATH] = ""; + if ((index = screenshot_get_next_path(path, SCREENSHOT_FORMAT_BMP)) == -1) { return -1; } - // Allocate buffer - buffer = malloc(0xFFFF); - if (buffer == NULL) { - SDL_RWclose(fp); - return -1; - } - - // Get image size - width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); - height = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16); - stride = (width + 3) & 0xFFFFFFFC; - - // File header - memset(&header, 0, sizeof(header)); - header.bfType = 0x4D42; - header.bfSize = height * stride + 1038; - header.bfOffBits = 1038; - - bytesWritten = SDL_RWwrite(fp, &header, sizeof(BitmapFileHeader), 1); - if (bytesWritten != 1) { - SDL_RWclose(fp); - SafeFree(buffer); - log_error("failed to save screenshot"); - return -1; - } - - // Info header - memset(&info, 0, sizeof(info)); - info.biSize = sizeof(info); - info.biWidth = width; - info.biHeight = height; - info.biPlanes = 1; - info.biBitCount = 8; - info.biXPelsPerMeter = 2520; - info.biYPelsPerMeter = 2520; - info.biClrUsed = 246; - - bytesWritten = SDL_RWwrite(fp, &info, sizeof(BitmapInfoHeader), 1); - if (bytesWritten != 1) { - SDL_RWclose(fp); - SafeFree(buffer); - log_error("failed to save screenshot"); - return -1; - } - - // Palette - memset(buffer, 0, 246 * 4); - for (i = 0; i < 246; i++) { - buffer[i * 4 + 0] = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 0]; - buffer[i * 4 + 1] = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 1]; - buffer[i * 4 + 2] = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 2]; - } - - bytesWritten = SDL_RWwrite(fp, buffer, sizeof(char), 246 * 4); - if (bytesWritten != 246*4){ - SDL_RWclose(fp); - SafeFree(buffer); - log_error("failed to save screenshot"); - return -1; - } - - // Image, save upside down rct_drawpixelinfo *dpi = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo); - for (y = dpi->height - 1; y >= 0; y--) { - row = dpi->bits + y * (dpi->width + dpi->pitch); - - memset(buffer, 0, stride); - memcpy(buffer, row, dpi->width); - - bytesWritten = SDL_RWwrite(fp, buffer, sizeof(char), stride); - if (bytesWritten != stride){ - SDL_RWclose(fp); - SafeFree(buffer); - log_error("failed to save screenshot"); - return -1; - } + rct_palette *palette = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, rct_palette); + if (image_io_bmp_write(dpi, palette, path)) { + return index; + } else { + return -1; } - - SDL_RWclose(fp); - free(buffer); - - return index; } int screenshot_dump_png() @@ -263,119 +140,14 @@ int screenshot_dump_png() } rct_drawpixelinfo *dpi = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo); - if (screenshot_write_png(dpi, path)) { + rct_palette *palette = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, rct_palette); + if (image_io_png_write(dpi, palette, path)) { return index; } else { return -1; } } -bool screenshot_write_png(rct_drawpixelinfo *dpi, const char *path) -{ -#ifdef USE_LIBPNG - // Get image size - int stride = dpi->width + dpi->pitch; - - // Setup PNG - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) { - return false; - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - return false; - } - - png_colorp palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); - for (int i = 0; i < 256; i++) { - palette[i].blue = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 0]; - palette[i].green = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 1]; - palette[i].red = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 2]; - } - - png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); - - // Open file for writing - SDL_RWops *file = SDL_RWFromFile(path, "wb"); - if (file == NULL) { - png_free(png_ptr, palette); - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - return false; - } - png_set_write_fn(png_ptr, file, my_png_write_data, my_png_flush); - - // Set error handler - if (setjmp(png_jmpbuf(png_ptr))) { - png_free(png_ptr, palette); - png_destroy_write_struct(&png_ptr, &info_ptr); - SDL_RWclose(file); - return false; - } - - // Write header - png_set_IHDR( - png_ptr, info_ptr, dpi->width, dpi->height, 8, - PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT - ); - png_write_info(png_ptr, info_ptr); - - // Write pixels - uint8 *bits = dpi->bits; - for (int y = 0; y < dpi->height; y++) { - png_write_row(png_ptr, (png_const_bytep)bits); - bits += stride; - } - - // Finish - png_write_end(png_ptr, NULL); - SDL_RWclose(file); - - png_free(png_ptr, palette); - png_destroy_write_struct(&png_ptr, &info_ptr); - return true; -#else - unsigned int error; - unsigned char* png; - size_t pngSize; - LodePNGState state; - - lodepng_state_init(&state); - state.info_raw.colortype = LCT_PALETTE; - - // Get image size - int stride = (dpi->width + 3) & ~3; - - for (int i = 0; i < 256; i++) { - unsigned char r, g, b, a = 255; - - b = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 0]; - g = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 1]; - r = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, uint8)[i * 4 + 2]; - - lodepng_palette_add(&state.info_raw, r, g, b, a); - } - - error = lodepng_encode(&png, &pngSize, dpi->bits, stride, dpi->height, &state); - if (error != 0) { - free(png); - return false; - } else { - SDL_RWops *file = SDL_RWFromFile(path, "wb"); - if (file == NULL) { - free(png); - return false; - } - SDL_RWwrite(file, png, pngSize, 1); - SDL_RWclose(file); - } - - free(png); - return true; -#endif -} - void screenshot_giant() { int originalRotation = get_current_rotation(); @@ -457,7 +229,8 @@ void screenshot_giant() return; } - screenshot_write_png(&dpi, path); + rct_palette *palette = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, rct_palette); + image_io_png_write(&dpi, palette, path); free(dpi.bits); @@ -591,25 +364,11 @@ int cmdline_for_screenshot(const char **argv, int argc) viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); - screenshot_write_png(&dpi, outputPath); + rct_palette *palette = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, rct_palette); + image_io_png_write(&dpi, palette, outputPath); free(dpi.bits); } openrct2_dispose(); return 1; } - -#ifdef USE_LIBPNG - -static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) -{ - SDL_RWops *file = (SDL_RWops*)png_get_io_ptr(png_ptr); - SDL_RWwrite(file, data, length, 1); -} - -static void my_png_flush(png_structp png_ptr) -{ - -} - -#endif