diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0a44bcff2..7bd8c6c612 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,19 @@ endif (DISABLE_NETWORK)
option(STATIC "Create a static build.")
+# Not required yet
+PKG_CHECK_MODULES(PNG libpng>=1.6)
+if (NOT PNG_FOUND)
+ PKG_CHECK_MODULES(PNG libpng16)
+endif (NOT PNG_FOUND)
+
+if (PNG_FOUND)
+ set (USE_LODEPNG FALSE)
+else (PNG_FOUND)
+ set (USE_LODEPNG TRUE)
+ message("Falling back to deprecated Lodepng for PNG support. Please upgrade your system to libpng 1.6")
+endif (PNG_FOUND)
+
# Handle creating the rct2 text and data files on OS X and Linux
# See details in src/openrct2.c:openrct2_setup_rct2_segment for how the values
# were derived.
@@ -96,10 +109,16 @@ set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
+if (USE_LODEPNG)
+ set (LODEPNG_SOURCES "lib/lodepng/*.c")
+else (USE_LODEPNG)
+ add_definitions(-DUSE_LIBPNG)
+endif (USE_LODEPNG)
+
# include lib
include_directories("lib/")
# add source files
-file(GLOB_RECURSE ORCT2_SOURCES "src/*.c" "src/*.cpp" "lib/argparse/*.c" "lib/cutest/*.c" "lib/lodepng/*.c")
+file(GLOB_RECURSE ORCT2_SOURCES "src/*.c" "src/*.cpp" "lib/argparse/*.c" "lib/cutest/*.c" ${LODEPNG_SOURCES})
if (APPLE)
file(GLOB_RECURSE ORCT2_MM_SOURCES "src/*.m")
set_source_files_properties(${ORCT2_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c -fmodules")
@@ -135,6 +154,12 @@ else (STATIC)
SET(SDL2LIBS ${SDL2_LIBRARIES})
endif (STATIC)
+if (STATIC)
+ SET(PNGLIBS ${PNG_STATIC_LIBRARIES})
+else (STATIC)
+ SET(PNGLIBS ${PNG_LIBRARIES})
+endif (STATIC)
+
if (NOT DISABLE_HTTP_TWITCH)
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.7)
@@ -165,9 +190,9 @@ if (UNIX)
set(DLLIB dl)
endif (UNIX)
-INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS})
+INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS})
-LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS})
+LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS} ${PNG_LIBRARY_DIRS})
if (WIN32)
# build as library for now, replace with add_executable
@@ -200,7 +225,7 @@ endif (UNIX AND NOT APPLE)
# libopenrct2.dll -> openrct2.dll
set_target_properties(${PROJECT} PROPERTIES PREFIX "")
-TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2LIBS} ${ORCTLIBS_LIB} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB})
+TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2LIBS} ${ORCTLIBS_LIB} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB} ${PNGLIBS})
if (APPLE OR STATIC)
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c)
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/scripts/linux/build.sh b/scripts/linux/build.sh
index 5a796a00f5..bef6f53e17 100755
--- a/scripts/linux/build.sh
+++ b/scripts/linux/build.sh
@@ -17,7 +17,7 @@ if [[ $(uname -s) == "Darwin" ]]; then
# keep in sync with version in Xcode project
sha256sum=2cec3958352477fbb876a5b6398722077084b5ff7e95a7d3cd67492abf5012fc
else
- sha256sum=69ff98c9544838fb16384bc78af9dc1c452b9d01d919e43f5fec686d02c9bdd8
+ sha256sum=31c5e19d9f794bd5f0e75f20c2b4c3c4664d736b0a4d50c8cde14a9a9007b62d
fi
libVFile="./libversion"
libdir="./lib"
@@ -62,10 +62,12 @@ pushd build
PARENT=$(readlink -f ../)
chmod a+rwx $(pwd)
chmod g+s $(pwd)
- docker run -u travis -v $PARENT:/work/openrct2 -w /work/openrct2/build -i -t openrct2/openrct2:32bit-only bash -c "cmake ../ $OPENRCT2_CMAKE_OPTS && make"
+ # CMAKE and MAKE opts from environment
+ docker run -u travis -v $PARENT:/work/openrct2 -w /work/openrct2/build -i -t openrct2/openrct2:32bit-only bash -c "cmake ../ $OPENRCT2_CMAKE_OPTS && make $OPENRCT_MAKE_OPTS"
else
cmake -DCMAKE_BUILD_TYPE=Debug $OPENRCT2_CMAKE_OPTS ..
- make
+ # NOT the same variable as above
+ make $OPENRCT2_MAKE_OPTS
fi
popd
diff --git a/scripts/linux/install.sh b/scripts/linux/install.sh
index 466a7d44e0..c0bde470cd 100755
--- a/scripts/linux/install.sh
+++ b/scripts/linux/install.sh
@@ -170,8 +170,9 @@ elif [[ $(uname) == "Linux" ]]; then
case "$TARGET" in
"linux")
sudo dpkg --add-architecture i386
+ sudo add-apt-repository -y ppa:djcj/tools
sudo apt-get update
- sudo apt-get install --no-install-recommends -y --force-yes cmake libsdl2-dev:i386 libsdl2-ttf-dev:i386 gcc-4.8 pkg-config:i386 g++-4.8-multilib gcc-4.8-multilib libjansson-dev:i386 libspeex-dev:i386 libspeexdsp-dev:i386 libcurl4-openssl-dev:i386 libcrypto++-dev:i386 clang libfontconfig1-dev:i386 libfreetype6-dev:i386 libpng-dev:i386
+ sudo apt-get install --no-install-recommends -y --force-yes cmake libsdl2-dev:i386 libsdl2-ttf-dev:i386 gcc-4.8 pkg-config:i386 g++-4.8-multilib gcc-4.8-multilib libjansson-dev:i386 libspeex-dev:i386 libspeexdsp-dev:i386 libcurl4-openssl-dev:i386 libcrypto++-dev:i386 clang libfontconfig1-dev:i386 libfreetype6-dev:i386 libpng-dev:i386 libpng16-dev:i386
download https://launchpad.net/ubuntu/+archive/primary/+files/libjansson4_2.7-1ubuntu1_i386.deb libjansson4_2.7-1ubuntu1_i386.deb
download https://launchpad.net/ubuntu/+archive/primary/+files/libjansson-dev_2.7-1ubuntu1_i386.deb libjansson-dev_2.7-1ubuntu1_i386.deb
sudo dpkg -i libjansson4_2.7-1ubuntu1_i386.deb
diff --git a/src/cmdline_sprite.c b/src/cmdline_sprite.c
index dff9acbf18..c7fc2eec5c 100644
--- a/src/cmdline_sprite.c
+++ b/src/cmdline_sprite.c
@@ -18,12 +18,12 @@
* along with this program. If not, see .
*****************************************************************************/
-#include
#include "cmdline.h"
#include "drawing/drawing.h"
+#include "image_io.h"
+#include "openrct2.h"
#include "platform/platform.h"
#include "util/util.h"
-#include "openrct2.h"
#define MODE_DEFAULT 0
#define MODE_CLOSEST 1
@@ -168,33 +168,11 @@ bool sprite_file_export(int spriteIndex, const char *outPath)
memcpy(spriteFilePalette, _standardPalette, 256 * 4);
gfx_rle_sprite_to_buffer(spriteHeader->offset, pixels, (uint8*)spriteFilePalette, &dpi, IMAGE_TYPE_NO_BACKGROUND, 0, spriteHeader->height, 0, spriteHeader->width);
- LodePNGState pngState;
- unsigned int pngError;
- unsigned char* pngData;
- size_t pngSize;
-
- lodepng_state_init(&pngState);
- pngState.info_raw.colortype = LCT_PALETTE;
- lodepng_palette_add(&pngState.info_raw, 0, 0, 0, 0);
- for (int i = 1; i < 256; i++) {
- lodepng_palette_add(
- &pngState.info_raw,
- spriteFilePalette[i].r,
- spriteFilePalette[i].g,
- spriteFilePalette[i].b,
- 255
- );
- }
-
- pngError = lodepng_encode(&pngData, &pngSize, pixels, spriteHeader->width, spriteHeader->height, &pngState);
- if (pngError != 0) {
- free(pngData);
- fprintf(stderr, "Error creating PNG data, %u: %s", pngError, lodepng_error_text(pngError));
- return false;
- } else {
- lodepng_save_file(pngData, pngSize, outPath);
- free(pngData);
+ if (image_io_png_write(&dpi, (rct_palette*)spriteFilePalette, outPath)) {
return true;
+ } else {
+ fprintf(stderr, "Error writing PNG");
+ return false;
}
}
@@ -261,16 +239,10 @@ typedef struct {
bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **outBuffer, int *outBufferLength, int mode)
{
- unsigned char *pixels;
- unsigned int width, height;
- unsigned int pngError;
-
- memcpy(spriteFilePalette, _standardPalette, 256 * 4);
-
- pngError = lodepng_decode_file(&pixels, &width, &height, path, LCT_RGBA, 8);
- if (pngError != 0) {
- free(pixels);
- fprintf(stderr, "Error creating PNG data, %u: %s", pngError, lodepng_error_text(pngError));
+ uint8 *pixels;
+ uint32 width, height;
+ if (!image_io_png_read(&pixels, &width, &height, path)) {
+ fprintf(stderr, "Error reading PNG");
return false;
}
@@ -280,6 +252,8 @@ bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **ou
return false;
}
+ memcpy(spriteFilePalette, _standardPalette, 256 * 4);
+
uint8 *buffer = malloc((height * 2) + (width * height * 16));
memset(buffer, 0, (height * 2) + (width * height * 16));
uint16 *yOffsets = (uint16*)buffer;
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..cc14cc012c
--- /dev/null
+++ b/src/image_io.c
@@ -0,0 +1,371 @@
+#ifdef USE_LIBPNG
+ #include
+#else
+ #include
+#endif
+
+#include "image_io.h"
+
+#ifdef USE_LIBPNG
+ static void my_png_read_data(png_structp png_ptr, png_bytep data, png_size_t length);
+ 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_read(uint8 **pixels, uint32 *width, uint32 *height, const utf8 *path)
+{
+#ifdef USE_LIBPNG
+ png_structp png_ptr;
+ png_infop info_ptr;
+ unsigned int sig_read = 0;
+
+ // Setup PNG structures
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return false;
+ }
+
+ // Open PNG file
+ SDL_RWops *fp = SDL_RWFromFile(path, "rb");
+ if (fp == NULL) {
+ return false;
+ }
+
+ // Set error handling
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ SDL_RWclose(fp);
+ return false;
+ }
+
+ // Setup png reading
+ png_set_read_fn(png_ptr, fp, my_png_read_data);
+ png_set_sig_bytes(png_ptr, sig_read);
+
+ // To simplify the reading process, convert 4-16 bit data to 24-32 bit data
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
+
+ // Read header
+ png_uint_32 pngWidth, pngHeight;
+ int bit_depth, color_type, interlace_type;
+ png_get_IHDR(png_ptr, info_ptr, &pngWidth, &pngHeight, &bit_depth, &color_type, &interlace_type, NULL, NULL);
+
+ // Read pixels as 32bpp RGBA data
+ png_size_t rowBytes = png_get_rowbytes(png_ptr, info_ptr);
+ png_bytepp rowPointers = png_get_rows(png_ptr, info_ptr);
+ uint8 *pngPixels = (uint8*)malloc(pngWidth * pngHeight * 4);
+ uint8 *dst = pngPixels;
+ if (color_type == PNG_COLOR_TYPE_RGB) {
+ // 24-bit PNG (no alpha)
+ const png_size_t expectedRowSize = pngWidth * 3;
+ for (png_uint_32 i = 0; i < pngHeight; i++) {
+ assert(rowBytes == expectedRowSize);
+
+ uint8 *src = rowPointers[i];
+ for (png_uint_32 x = 0; x < pngWidth; x++) {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = 255;
+ }
+ }
+ } else {
+ // 32-bit PNG (with alpha)
+ const png_size_t expectedRowSize = pngWidth * 4;
+ for (png_uint_32 i = 0; i < pngHeight; i++) {
+ assert(rowBytes == expectedRowSize);
+
+ memcpy(dst, rowPointers[i], rowBytes);
+ dst += rowBytes;
+ }
+ }
+
+ // Close the PNG
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ SDL_RWclose(fp);
+
+ // Return the output data
+ *pixels = (uint8*)pngPixels;
+ if (width != NULL) *width = pngWidth;
+ if (height != NULL) *height = pngHeight;
+ return true;
+#else
+ // Read the pixels as 32bpp RGBA
+ unsigned char *pngPixels;
+ unsigned int pngWidth, pngHeight;
+ unsigned int pngError = lodepng_decode_file(&pngPixels, &pngWidth, &pngHeight, path, LCT_RGBA, 8);
+ if (pngError != 0) {
+ free(pngPixels);
+ log_error("Error creating PNG data, %u: %s", pngError, lodepng_error_text(pngError));
+ return false;
+ }
+
+ // Return the output data
+ *pixels = (uint8*)pngPixels;
+ if (width != NULL) *width = pngWidth;
+ if (height != NULL) *height = pngHeight;
+ return true;
+#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_byte transparentIndex = 0;
+ png_set_tRNS(png_ptr, info_ptr, &transparentIndex, 1, NULL);
+ 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;
+
+ lodepng_palette_add(&state.info_raw, 0, 0, 0, 0);
+ for (int i = 1; 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) {
+ log_error("Error creating PNG data, %u: %s", error, lodepng_error_text(error));
+ 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_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ SDL_RWops *file = (SDL_RWops*)png_get_io_ptr(png_ptr);
+ SDL_RWread(file, data, length, 1);
+}
+
+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..fa8c086b05
--- /dev/null
+++ b/src/image_io.h
@@ -0,0 +1,12 @@
+#ifndef _IMAGE_IO_H_
+#define _IMAGE_IO_H_
+
+#include "common.h"
+#include "drawing/drawing.h"
+
+bool image_io_png_read(uint8 **pixels, uint32 *width, uint32 *height, const utf8 *path);
+
+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 25af755b56..81b7693e0e 100644
--- a/src/interface/screenshot.c
+++ b/src/interface/screenshot.c
@@ -17,13 +17,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*****************************************************************************/
-#pragma pack(1)
-#include
#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"
@@ -109,250 +108,44 @@ 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()
{
- rct_drawpixelinfo *dpi = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
-
- int i, index, width, height, padding;
- char path[MAX_PATH] = "";
- unsigned int error;
- unsigned char r, g, b, a = 255;
-
- unsigned char* png;
- size_t pngSize;
- LodePNGState state;
-
// Get a free screenshot path
- if ((index = screenshot_get_next_path(path, SCREENSHOT_FORMAT_PNG)) == -1)
+ int index;
+ char path[MAX_PATH] = "";
+ if ((index = screenshot_get_next_path(path, SCREENSHOT_FORMAT_PNG)) == -1) {
return -1;
-
-
- lodepng_state_init(&state);
- state.info_raw.colortype = LCT_PALETTE;
-
- // Get image size
- width = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16);
- height = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16);
-
- padding = dpi->pitch;
-
- for (i = 0; i < 256; i++) {
- 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);
}
- uint8* pixels = dpi->bits;
-
- if (padding > 0) {
- pixels = malloc(width * height);
- if (!pixels) {
- return -1;
- }
- uint8* src = dpi->bits;
- uint8* dst = pixels;
- for (int y = height; y > 0; y--) {
- for (int x = width; x > 0; x--) {
- *dst++ = *src++;
- }
- src += padding;
- }
- }
-
- error = lodepng_encode(&png, &pngSize, pixels, width, height, &state);
- if (error) {
- log_error("Unable to save screenshot, %u: %s", lodepng_error_text(error));
- index = -1;
+ rct_drawpixelinfo *dpi = RCT2_ADDRESS(RCT2_ADDRESS_SCREEN_DPI, rct_drawpixelinfo);
+ rct_palette *palette = RCT2_ADDRESS(RCT2_ADDRESS_PALETTE, rct_palette);
+ if (image_io_png_write(dpi, palette, path)) {
+ return index;
} else {
- SDL_RWops *file = SDL_RWFromFile(path, "wb");
- if (file == NULL) {
- log_error("Unable to save screenshot, %s", SDL_GetError());
- index = -1;
- } else {
- SDL_RWwrite(file, png, pngSize, 1);
- SDL_RWclose(file);
- }
+ return -1;
}
-
- free(png);
- if ((utf8*)pixels != (utf8*)dpi->bits) {
- free(pixels);
- }
- return index;
-}
-
-bool screenshot_write_png(rct_drawpixelinfo *dpi, const char *path)
-{
- 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;
}
void screenshot_giant()
@@ -436,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);
@@ -570,7 +364,8 @@ 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);
}