Change from pnglite to libpng for PNG reading

This is desirable mainly because libpng is maintained and pnglite is
not. pnglite was last updated in 2007 (15 years ago) and probably has a
lot of security vulnerabilities.

libpng is an actively maintained library also used by browsers like
Firefox or Chromium, so it's less likely to contain security
vulnerabilities, also it's more likely to be packaged by Linux
distributions.
This commit is contained in:
Jupeyy 2022-06-11 09:09:42 +02:00 committed by heinrich5991
parent fad8afc083
commit 65ad57a448
17 changed files with 554 additions and 1366 deletions

View file

@ -58,7 +58,7 @@ jobs:
run: |
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libvulkan-dev glslang-tools spirv-tools libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev valgrind -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libvulkan-dev glslang-tools spirv-tools libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev libpng-dev valgrind -y
- name: Prepare Linux (non-fancy)
if: ${{ contains(matrix.os, 'ubuntu') && !matrix.fancy }}

View file

@ -545,7 +545,7 @@ endif()
find_package(Ogg)
find_package(Opus)
find_package(Opusfile)
find_package(Pnglite)
find_package(PNG)
find_package(PythonInterp 3)
find_package(SDL2)
find_package(SQLite3)
@ -631,7 +631,7 @@ show_dependency_status("Ogg" OGG)
show_dependency_status("OpenSSL Crypto" CRYPTO)
show_dependency_status("Opus" OPUS)
show_dependency_status("Opusfile" OPUSFILE)
show_dependency_status("Pnglite" PNGLITE)
show_dependency_status("PNG" PNG)
show_dependency_status("PythonInterp" PYTHONINTERP)
show_dependency_status("SDL2" SDL2)
show_dependency_status("SQLite3" SQLite3)
@ -680,6 +680,9 @@ if(DISCORD_DYNAMIC)
message(SEND_ERROR "You must enable the DISCORD flag if you want to link the Discord SDK")
endif()
endif()
if(NOT(PNG_FOUND))
message(SEND_ERROR "You must install libpng to compile DDNet")
endif()
if(CLIENT AND NOT(FREETYPE_FOUND))
message(SEND_ERROR "You must install Freetype to compile the ${CMAKE_PROJECT_NAME} client")
endif()
@ -1562,6 +1565,7 @@ set(COPY_FILES
${CURL_COPY_FILES}
${FREETYPE_COPY_FILES}
${OPUSFILE_COPY_FILES}
${PNG_COPY_FILES}
${SDL2_COPY_FILES}
${SQLite3_COPY_FILES}
${FFMPEG_COPY_FILES}
@ -1755,6 +1759,8 @@ set_src(ENGINE_SHARED GLOB_RECURSE src/engine/shared
http.h
huffman.cpp
huffman.h
image_loader.cpp
image_loader.h
image_manipulation.cpp
image_manipulation.h
jobs.cpp
@ -1868,6 +1874,7 @@ set(LIBS
${CURL_LIBRARIES}
${SQLite3_LIBRARIES}
${WEBSOCKETS_LIBRARIES}
${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
${PLATFORM_LIBS}
# Add pthreads (on non-Windows) at the end, so that other libraries can depend
@ -1877,6 +1884,7 @@ set(LIBS
# Targets
add_library(engine-shared EXCLUDE_FROM_ALL OBJECT ${ENGINE_INTERFACE} ${ENGINE_SHARED} ${ENGINE_UUID_SHARED} ${BASE})
target_include_directories(engine-shared PRIVATE ${PNG_INCLUDE_DIRS})
add_library(game-shared EXCLUDE_FROM_ALL OBJECT ${GAME_SHARED} ${GAME_GENERATED_SHARED})
list(APPEND TARGETS_OWN engine-shared game-shared)
@ -2102,14 +2110,14 @@ if(CLIENT)
)
set(CLIENT_SRC ${ENGINE_CLIENT} ${PLATFORM_CLIENT} ${GAME_CLIENT} ${GAME_EDITOR} ${GAME_GENERATED_CLIENT})
set(DEPS_CLIENT ${DEPS} ${GLEW_DEP} ${PNGLITE_DEP} ${WAVPACK_DEP})
set(DEPS_CLIENT ${DEPS} ${GLEW_DEP} ${WAVPACK_DEP})
# Libraries
set(LIBS_CLIENT
${LIBS}
${FREETYPE_LIBRARIES}
${GLEW_LIBRARIES}
${PNGLITE_LIBRARIES}
${PNG_LIBRARIES}
${SDL2_LIBRARIES}
${WAVPACK_LIBRARIES}
${FFMPEG_LIBRARIES}
@ -2185,7 +2193,7 @@ if(CLIENT)
${OGG_INCLUDE_DIRS}
${OPUSFILE_INCLUDE_DIRS}
${OPUS_INCLUDE_DIRS}
${PNGLITE_INCLUDE_DIRS}
${PNG_INCLUDE_DIRS}
${SDL2_INCLUDE_DIRS}
${WAVPACK_INCLUDE_DIRS}
${FFMPEG_INCLUDE_DIRS}
@ -2343,6 +2351,7 @@ if(SERVER)
# Libraries
set(LIBS_SERVER
${LIBS}
${PNG_LIBRARIES}
${MYSQL_LIBRARIES}
${TARGET_ANTIBOT}
${MINIUPNPC_LIBRARIES}
@ -2361,6 +2370,7 @@ if(SERVER)
$<TARGET_OBJECTS:game-shared>
)
target_link_libraries(${TARGET_SERVER} ${LIBS_SERVER})
target_include_directories(${TARGET_SERVER} PRIVATE ${PNG_INCLUDE_DIRS})
list(APPEND TARGETS_OWN ${TARGET_SERVER})
list(APPEND TARGETS_LINK ${TARGET_SERVER})
@ -2413,9 +2423,7 @@ if(TOOLS)
set(TOOL_DEPS ${DEPS})
set(TOOL_LIBS ${LIBS})
if(TOOL MATCHES "^(dilate|map_convert_07|map_optimize|map_extract|map_replace_image)$")
list(APPEND TOOL_DEPS ${PNGLITE_DEP})
list(APPEND TOOL_LIBS ${PNGLITE_LIBRARIES})
list(APPEND TOOL_INCLUDE_DIRS ${PNGLITE_INCLUDE_DIRS})
list(APPEND TOOL_INCLUDE_DIRS ${PNG_INCLUDE_DIRS})
endif()
if(TOOL MATCHES "^config_")
list(APPEND EXTRA_TOOL_SRC "src/tools/config_common.h")

View file

@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y mingw-w64 \
libglew-dev \
libogg-dev \
libopus-dev \
libpnglite-dev \
libpng-dev \
libwavpack-dev \
libopusfile-dev \
libsdl2-dev \

View file

@ -34,7 +34,7 @@ Dependencies on Linux / macOS
You can install the required libraries on your system, `touch CMakeLists.txt` and CMake will use the system-wide libraries by default. You can install all required dependencies and CMake on Debian or Ubuntu like this:
sudo apt install build-essential cmake git google-mock libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python libx264-dev libavfilter-dev libavdevice-dev libavformat-dev libavcodec-extra libavutil-dev libvulkan-dev glslang-tools spirv-tools
sudo apt install build-essential cmake git google-mock libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python libx264-dev libavfilter-dev libavdevice-dev libavformat-dev libavcodec-extra libavutil-dev libvulkan-dev glslang-tools spirv-tools libpng-dev
On older distributions like Ubuntu 18.04 don't install `google-mock`, but instead set `-DDOWNLOAD_GTEST=ON` when building to get a more recent gtest/gmock version.
@ -44,9 +44,7 @@ Or on CentOS, RedHat and AlmaLinux like this:
Or on Arch Linux like this:
sudo pacman -S --needed base-devel cmake curl freetype2 git glew gmock libnotify opusfile python sdl2 sqlite wavpack x264 ffmpeg vulkan-icd-loader vulkan-headers glslang spirv-tools
There is an [AUR package for pnglite](https://aur.archlinux.org/packages/pnglite/). For instructions on installing it, see [AUR packages installation instructions on ArchWiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
sudo pacman -S --needed base-devel cmake curl freetype2 git glew gmock libnotify opusfile python sdl2 sqlite wavpack x264 ffmpeg vulkan-icd-loader vulkan-headers glslang spirv-tools libpng
On macOS you can use [homebrew](https://brew.sh/) to install build dependencies like this:

46
cmake/FindPNG.cmake Normal file
View file

@ -0,0 +1,46 @@
if(NOT PREFER_BUNDLED_LIBS)
set(CMAKE_MODULE_PATH ${ORIGINAL_CMAKE_MODULE_PATH})
find_package(PNG)
set(CMAKE_MODULE_PATH ${OWN_CMAKE_MODULE_PATH})
if(PNG_FOUND)
set(PNG_BUNDLED OFF)
set(PNG_DEP)
endif()
endif()
if(NOT PNG_FOUND)
set_extra_dirs_lib(PNG png)
find_library(PNG_LIBRARY
NAMES png16 libpng16 libpng16-16 png16-16
HINTS ${HINTS_PNG_LIBDIR} ${PC_PNG_LIBDIR} ${PC_PNG_LIBRARY_DIRS}
PATHS ${PATHS_PNG_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(PNG png "${PNG_LIBRARY}")
find_path(PNG_INCLUDEDIR
NAMES png.h
HINTS ${HINTS_PNG_INCLUDEDIR} ${PC_PNG_INCLUDEDIR} ${PC_PNG_INCLUDE_DIRS}
PATHS ${PATHS_PNG_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
mark_as_advanced(PNG_LIBRARY PNG_INCLUDEDIR)
if(PNG_LIBRARY AND PNG_INCLUDEDIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PNG DEFAULT_MSG PNG_LIBRARY PNG_INCLUDEDIR)
set(PNG_LIBRARIES ${PNG_LIBRARY})
set(PNG_INCLUDE_DIRS ${PNG_INCLUDEDIR})
endif()
endif()
if(PNG_FOUND)
is_bundled(PNG_BUNDLED "${PNG_LIBRARY}")
if(PNG_BUNDLED AND TARGET_OS STREQUAL "windows")
set(PNG_COPY_FILES "${EXTRA_PNG_LIBDIR}/libpng16-16.dll")
else()
set(PNG_COPY_FILES)
endif()
endif()

View file

@ -1,46 +0,0 @@
if(NOT PREFER_BUNDLED_LIBS)
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_PNGLITE pnglite)
endif()
find_library(PNGLITE_LIBRARY
NAMES pnglite
HINTS ${PC_PNGLITE_LIBDIR} ${PC_PNGLITE_LIBRARY_DIRS}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
find_path(PNGLITE_INCLUDEDIR
NAMES pnglite.h
HINTS ${PC_PNGLITE_INCLUDEDIR} ${PC_PNGLITE_INCLUDE_DIRS}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
mark_as_advanced(PNGLITE_LIBRARY PNGLITE_INCLUDEDIR)
if(PNGLITE_LIBRARY AND PNGLITE_INCLUDEDIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Pnglite DEFAULT_MSG PNGLITE_LIBRARY PNGLITE_INCLUDEDIR)
set(PNGLITE_LIBRARIES ${PNGLITE_LIBRARY})
set(PNGLITE_INCLUDE_DIRS ${PNGLITE_INCLUDEDIR})
set(PNGLITE_BUNDLED OFF)
endif()
endif()
if(NOT PNGLITE_FOUND)
set(PNGLITE_SRC_DIR src/engine/external/pnglite)
set_src(PNGLITE_SRC GLOB ${PNGLITE_SRC_DIR} pnglite.c pnglite.h)
add_library(pnglite EXCLUDE_FROM_ALL OBJECT ${PNGLITE_SRC})
list(APPEND TARGETS_DEP pnglite)
set(PNGLITE_INCLUDEDIR ${PNGLITE_SRC_DIR})
target_include_directories(pnglite PRIVATE ${ZLIB_INCLUDE_DIRS})
set(PNGLITE_DEP $<TARGET_OBJECTS:pnglite>)
set(PNGLITE_INCLUDE_DIRS ${PNGLITE_INCLUDEDIR})
set(PNGLITE_LIBRARIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Pnglite DEFAULT_MSG PNGLITE_INCLUDEDIR)
set(PNGLITE_BUNDLED ON)
endif()

@ -1 +1 @@
Subproject commit 02f8b2e0f3436347820cd36f35455f632ec8c590
Subproject commit 8d38bd56b441fbf473d53c65499dd8f1f3071b66

View file

@ -10,7 +10,7 @@
#include <base/system.h>
#include <pnglite.h>
#include <engine/shared/image_loader.h>
#include <engine/console.h>
#include <engine/graphics.h>
@ -135,8 +135,6 @@ CGraphics_Threaded::CGraphics_Threaded()
m_RenderEnable = true;
m_DoScreenshot = false;
png_init(0, 0);
}
void CGraphics_Threaded::ClipEnable(int x, int y, int w, int h)
@ -627,54 +625,49 @@ bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureID, int x, int
int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
{
char aCompleteFilename[IO_MAX_PATH_LENGTH];
// open file for reading
IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType, aCompleteFilename, sizeof(aCompleteFilename));
if(!File)
if(File)
{
io_seek(File, 0, IOSEEK_END);
unsigned int FileSize = io_tell(File);
io_seek(File, 0, IOSEEK_START);
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
ByteBuffer.resize(FileSize);
io_read(File, &ByteBuffer.front(), FileSize);
io_close(File);
uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat;
if(::LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{
pImg->m_pData = pImgBuffer;
if(ImageFormat == IMAGE_FORMAT_RGB) // ignore_convention
pImg->m_Format = CImageInfo::FORMAT_RGB;
else if(ImageFormat == IMAGE_FORMAT_RGBA) // ignore_convention
pImg->m_Format = CImageInfo::FORMAT_RGBA;
else
{
free(pImgBuffer);
return 0;
}
}
else
{
dbg_msg("game/png", "image had unsupported image format. filename='%s'", pFilename);
return 0;
}
}
else
{
dbg_msg("game/png", "failed to open file. filename='%s'", pFilename);
return 0;
}
png_t Png;
int Error = png_open_read(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("game/png", "failed to open file. filename='%s', pnglite: %s", aCompleteFilename, png_error_string(Error));
io_close(File);
return 0;
}
if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA))
{
dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename);
io_close(File);
return 0;
}
unsigned char *pBuffer = (unsigned char *)malloc((size_t)Png.width * Png.height * Png.bpp);
Error = png_get_data(&Png, pBuffer);
if(Error != PNG_NO_ERROR)
{
dbg_msg("game/png", "failed to read image. filename='%s', pnglite: %s", aCompleteFilename, png_error_string(Error));
free(pBuffer);
io_close(File);
return 0;
}
io_close(File);
pImg->m_Width = Png.width;
pImg->m_Height = Png.height;
if(Png.color_type == PNG_TRUECOLOR)
pImg->m_Format = CImageInfo::FORMAT_RGB;
else if(Png.color_type == PNG_TRUECOLOR_ALPHA)
pImg->m_Format = CImageInfo::FORMAT_RGBA;
else
{
free(pBuffer);
return 0;
}
pImg->m_pData = pBuffer;
return 1;
}
@ -806,21 +799,21 @@ bool CGraphics_Threaded::ScreenshotDirect()
{
// find filename
char aWholePath[1024];
png_t Png;
IOHANDLE File = m_pStorage->OpenFile(m_aScreenshotName, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath));
if(!File)
if(File)
{
dbg_msg("game/screenshot", "failed to open file. filename='%s'", aWholePath);
}
else
{
// save png
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, ColorRGBA(1.0f, 0.6f, 0.3f, 1.0f));
png_open_write(&Png, 0, File);
png_set_data(&Png, Image.m_Width, Image.m_Height, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)Image.m_pData);
// save png
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, ColorRGBA{1.0f, 0.6f, 0.3f, 1.0f});
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
if(SavePNG(IMAGE_FORMAT_RGBA, (const uint8_t *)Image.m_pData, ImageByteBuffer, Image.m_Width, Image.m_Height))
io_write(File, &ByteBuffer.front(), ByteBuffer.size());
io_close(File);
}

View file

@ -1 +0,0 @@
0.1.17

View file

@ -1,877 +0,0 @@
/* pnglite.c - pnglite library
For conditions of distribution and use, see copyright notice in pnglite.h
*/
#define DO_CRC_CHECKS 1
#define USE_ZLIB 1
#if USE_ZLIB
#include <zlib.h>
#else
#include "zlite.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pnglite.h"
static png_alloc_t png_alloc;
static png_free_t png_free;
static size_t file_read(png_t* png, void* out, size_t size, size_t numel)
{
size_t result;
if(png->read_fun)
{
result = png->read_fun(out, size, numel, png->user_pointer);
}
else
{
if(!out)
{
result = fseek(png->user_pointer, (long)(size*numel), SEEK_CUR);
}
else
{
result = fread(out, size, numel, png->user_pointer);
}
}
return result;
}
static size_t file_write(png_t* png, void* p, size_t size, size_t numel)
{
size_t result;
if(png->write_fun)
{
result = png->write_fun(p, size, numel, png->user_pointer);
}
else
{
result = fwrite(p, size, numel, png->user_pointer);
}
return result;
}
static int file_read_ul(png_t* png, unsigned *out)
{
unsigned char buf[4];
if(file_read(png, buf, 1, 4) != 4)
return PNG_FILE_ERROR;
*out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
return PNG_NO_ERROR;
}
static int file_write_ul(png_t* png, unsigned in)
{
unsigned char buf[4];
buf[0] = (in>>24) & 0xff;
buf[1] = (in>>16) & 0xff;
buf[2] = (in>>8) & 0xff;
buf[3] = (in) & 0xff;
if(file_write(png, buf, 1, 4) != 4)
return PNG_FILE_ERROR;
return PNG_NO_ERROR;
}
static unsigned get_ul(unsigned char* buf)
{
unsigned result;
unsigned char foo[4];
memcpy(foo, buf, 4);
result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
return result;
}
static unsigned set_ul(unsigned char* buf, unsigned in)
{
buf[0] = (in>>24) & 0xff;
buf[1] = (in>>16) & 0xff;
buf[2] = (in>>8) & 0xff;
buf[3] = (in) & 0xff;
return PNG_NO_ERROR;
}
int png_init(png_alloc_t pngalloc, png_free_t pngfree)
{
if(pngalloc)
png_alloc = pngalloc;
else
png_alloc = (png_alloc_t)&malloc;
if(pngfree)
png_free = pngfree;
else
png_free = &free;
return PNG_NO_ERROR;
}
static int png_get_bpp(png_t* png)
{
int bpp;
switch(png->color_type)
{
case PNG_GREYSCALE:
bpp = 1; break;
case PNG_TRUECOLOR:
bpp = 3; break;
case PNG_INDEXED:
bpp = 1; break;
case PNG_GREYSCALE_ALPHA:
bpp = 2; break;
case PNG_TRUECOLOR_ALPHA:
bpp = 4; break;
default:
return PNG_FILE_ERROR;
}
bpp *= png->depth/8;
return bpp;
}
static int png_read_ihdr(png_t* png)
{
unsigned length;
#if DO_CRC_CHECKS
unsigned orig_crc;
unsigned calc_crc;
#endif
unsigned char ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
file_read_ul(png, &length);
if(length != 13)
{
printf("%d\n", length);
return PNG_CRC_ERROR;
}
if(file_read(png, ihdr, 1, 13+4) != 13+4)
return PNG_EOF_ERROR;
#if DO_CRC_CHECKS
file_read_ul(png, &orig_crc);
calc_crc = crc32(0L, 0, 0);
calc_crc = crc32(calc_crc, ihdr, 13+4);
if(orig_crc != calc_crc)
return PNG_CRC_ERROR;
#else
file_read_ul(png);
#endif
png->width = get_ul(ihdr+4);
png->height = get_ul(ihdr+8);
png->depth = ihdr[12];
png->color_type = ihdr[13];
png->compression_method = ihdr[14];
png->filter_method = ihdr[15];
png->interlace_method = ihdr[16];
if(png->color_type == PNG_INDEXED)
return PNG_NOT_SUPPORTED;
if(png->depth != 8 && png->depth != 16)
return PNG_NOT_SUPPORTED;
if(png->interlace_method)
return PNG_NOT_SUPPORTED;
return PNG_NO_ERROR;
}
static int png_write_ihdr(png_t* png)
{
unsigned char ihdr[13+4];
unsigned char *p = ihdr;
unsigned crc;
file_write(png, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 1, 8);
file_write_ul(png, 13);
*p = 'I'; p++;
*p = 'H'; p++;
*p = 'D'; p++;
*p = 'R'; p++;
set_ul(p, png->width); p+=4;
set_ul(p, png->height); p+=4;
*p = png->depth; p++;
*p = png->color_type; p++;
*p = 0; p++;
*p = 0; p++;
*p = 0; p++;
file_write(png, ihdr, 1, 13+4);
crc = crc32(0L, 0, 0);
crc = crc32(crc, ihdr, 13+4);
file_write_ul(png, crc);
return PNG_NO_ERROR;
}
void png_print_info(png_t* png)
{
printf("PNG INFO:\n");
printf("\twidth:\t\t%d\n", png->width);
printf("\theight:\t\t%d\n", png->height);
printf("\tdepth:\t\t%d\n", png->depth);
printf("\tcolor:\t\t");
switch(png->color_type)
{
case PNG_GREYSCALE: printf("greyscale\n"); break;
case PNG_TRUECOLOR: printf("truecolor\n"); break;
case PNG_INDEXED: printf("palette\n"); break;
case PNG_GREYSCALE_ALPHA: printf("greyscale with alpha\n"); break;
case PNG_TRUECOLOR_ALPHA: printf("truecolor with alpha\n"); break;
default: printf("unknown, this is not good\n"); break;
}
printf("\tcompression:\t%s\n", png->compression_method?"unknown, this is not good":"inflate/deflate");
printf("\tfilter:\t\t%s\n", png->filter_method?"unknown, this is not good":"adaptive");
printf("\tinterlace:\t%s\n", png->interlace_method?"interlace":"no interlace");
}
int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer)
{
char header[8];
int result;
png->read_fun = read_fun;
png->write_fun = 0;
png->user_pointer = user_pointer;
if(!read_fun && !user_pointer)
return PNG_WRONG_ARGUMENTS;
if(file_read(png, header, 1, 8) != 8)
return PNG_EOF_ERROR;
if(memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0)
return PNG_HEADER_ERROR;
result = png_read_ihdr(png);
png->bpp = (unsigned char)png_get_bpp(png);
return result;
}
int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer)
{
png->write_fun = write_fun;
png->read_fun = 0;
png->user_pointer = user_pointer;
if(!write_fun && !user_pointer)
return PNG_WRONG_ARGUMENTS;
return PNG_NO_ERROR;
}
int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer)
{
return png_open_read(png, read_fun, user_pointer);
}
int png_open_file_read(png_t *png, const char* filename)
{
FILE* fp = fopen(filename, "rb");
if(!fp)
return PNG_FILE_ERROR;
return png_open_read(png, 0, fp);
}
int png_open_file_write(png_t *png, const char* filename)
{
FILE* fp = fopen(filename, "wb");
if(!fp)
return PNG_FILE_ERROR;
return png_open_write(png, 0, fp);
}
int png_open_file(png_t *png, const char* filename)
{
return png_open_file_read(png, filename);
}
int png_close_file(png_t* png)
{
fclose(png->user_pointer);
return PNG_NO_ERROR;
}
static int png_init_deflate(png_t* png, unsigned char* data, int datalen)
{
z_stream *stream;
png->zs = png_alloc(sizeof(z_stream));
stream = png->zs;
if(!stream)
return PNG_MEMORY_ERROR;
memset(stream, 0, sizeof(z_stream));
if(deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK)
return PNG_ZLIB_ERROR;
stream->next_in = data;
stream->avail_in = datalen;
return PNG_NO_ERROR;
}
static int png_init_inflate(png_t* png)
{
#if USE_ZLIB
z_stream *stream;
png->zs = png_alloc(sizeof(z_stream));
#else
zl_stream *stream;
png->zs = png_alloc(sizeof(zl_stream));
#endif
stream = png->zs;
if(!stream)
return PNG_MEMORY_ERROR;
#if USE_ZLIB
memset(stream, 0, sizeof(z_stream));
if(inflateInit(stream) != Z_OK)
return PNG_ZLIB_ERROR;
#else
memset(stream, 0, sizeof(zl_stream));
if(z_inflateInit(stream) != Z_OK)
return PNG_ZLIB_ERROR;
#endif
stream->next_out = png->png_data;
stream->avail_out = png->png_datalen;
return PNG_NO_ERROR;
}
static int png_end_deflate(png_t* png)
{
z_stream *stream = png->zs;
if(!stream)
return PNG_MEMORY_ERROR;
deflateEnd(stream);
png_free(png->zs);
return PNG_NO_ERROR;
}
static int png_end_inflate(png_t* png)
{
#if USE_ZLIB
z_stream *stream = png->zs;
#else
zl_stream *stream = png->zs;
#endif
if(!stream)
return PNG_MEMORY_ERROR;
#if USE_ZLIB
if(inflateEnd(stream) != Z_OK)
#else
if(z_inflateEnd(stream) != Z_OK)
#endif
{
printf("ZLIB says: %s\n", stream->msg);
return PNG_ZLIB_ERROR;
}
png_free(png->zs);
return PNG_NO_ERROR;
}
static int png_inflate(png_t* png, char* data, int len)
{
int result;
#if USE_ZLIB
z_stream *stream = png->zs;
#else
zl_stream *stream = png->zs;
#endif
if(!stream)
return PNG_MEMORY_ERROR;
stream->next_in = (unsigned char*)data;
stream->avail_in = len;
#if USE_ZLIB
result = inflate(stream, Z_SYNC_FLUSH);
#else
result = z_inflate(stream);
#endif
if(result != Z_STREAM_END && result != Z_OK)
{
printf("%s\n", stream->msg);
return PNG_ZLIB_ERROR;
}
if(stream->avail_in != 0)
return PNG_ZLIB_ERROR;
return PNG_NO_ERROR;
}
static int png_deflate(png_t* png, char* outdata, int outlen, int *outwritten)
{
int result;
z_stream *stream = png->zs;
if(!stream)
return PNG_MEMORY_ERROR;
stream->next_out = (unsigned char*)outdata;
stream->avail_out = outlen;
result = deflate(stream, Z_SYNC_FLUSH);
*outwritten = outlen - stream->avail_out;
if(result != Z_STREAM_END && result != Z_OK)
{
printf("%s\n", stream->msg);
return PNG_ZLIB_ERROR;
}
return result;
}
static int png_write_idats(png_t* png, unsigned char* data)
{
unsigned char *chunk;
unsigned long written;
unsigned long crc;
unsigned size = png->width * png->height * png->bpp + png->height;
(void)png_init_deflate;
(void)png_end_deflate;
(void)png_deflate;
chunk = png_alloc(size+8);
memcpy(chunk, "IDAT", 4);
written = size;
compress(chunk+4, &written, data, size);
crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, chunk, written+4);
set_ul(chunk+written+4, crc);
file_write_ul(png, written);
file_write(png, chunk, 1, written+8);
png_free(chunk);
file_write_ul(png, 0);
file_write(png, "IEND", 1, 4);
crc = crc32(0L, (const unsigned char *)"IEND", 4);
file_write_ul(png, crc);
return PNG_NO_ERROR;
}
static int png_read_idat(png_t* png, unsigned firstlen)
{
unsigned type = 0;
char *chunk;
int result;
unsigned length = firstlen;
unsigned old_len = length;
#if DO_CRC_CHECKS
unsigned orig_crc;
unsigned calc_crc;
#endif
chunk = png_alloc(firstlen);
result = png_init_inflate(png);
if(result != PNG_NO_ERROR)
{
png_end_inflate(png);
png_free(chunk);
return result;
}
do
{
if(file_read(png, chunk, 1, length) != length)
{
png_end_inflate(png);
png_free(chunk);
return PNG_FILE_ERROR;
}
#if DO_CRC_CHECKS
calc_crc = crc32(0L, Z_NULL, 0);
calc_crc = crc32(calc_crc, (unsigned char*)"IDAT", 4);
calc_crc = crc32(calc_crc, (unsigned char*)chunk, length);
file_read_ul(png, &orig_crc);
if(orig_crc != calc_crc)
{
result = PNG_CRC_ERROR;
break;
}
#else
file_read_ul(png);
#endif
result = png_inflate(png, chunk, length);
if(result != PNG_NO_ERROR) break;
file_read_ul(png, &length);
if(length > old_len)
{
png_free(chunk);
chunk = png_alloc(length);
old_len = length;
}
if(file_read(png, &type, 1, 4) != 4)
{
result = PNG_FILE_ERROR;
break;
}
}while(type == *(unsigned int*)"IDAT");
if(type == *(unsigned int*)"IEND")
result = PNG_DONE;
png_free(chunk);
png_end_inflate(png);
return result;
}
static int png_process_chunk(png_t* png)
{
int result = PNG_NO_ERROR;
unsigned type;
unsigned length;
file_read_ul(png, &length);
if(file_read(png, &type, 1, 4) != 4)
return PNG_FILE_ERROR;
if(type == *(unsigned int*)"IDAT") /* if we found an idat, all other idats should be followed with no other chunks in between */
{
png->png_datalen = png->width * png->height * png->bpp + png->height;
png->png_data = png_alloc(png->png_datalen);
if(!png->png_data)
return PNG_MEMORY_ERROR;
return png_read_idat(png, length);
}
else if(type == *(unsigned int*)"IEND")
{
return PNG_DONE;
}
else
{
file_read(png, 0, 1, length + 4); /* unknown chunk */
}
return result;
}
static void png_filter_sub(int stride, unsigned char* in, unsigned char* out, int len)
{
int i;
unsigned char a = 0;
for(i = 0; i < len; i++)
{
if(i >= stride)
a = out[i - stride];
out[i] = in[i] + a;
}
}
static void png_filter_up(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
{
int i;
if(prev_line)
{
for(i = 0; i < len; i++)
out[i] = in[i] + prev_line[i];
}
else
memcpy(out, in, len);
}
static void png_filter_average(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
{
int i;
unsigned char a = 0;
unsigned char b = 0;
unsigned int sum = 0;
for(i = 0; i < len; i++)
{
if(prev_line)
b = prev_line[i];
if(i >= stride)
a = out[i - stride];
sum = a;
sum += b;
out[i] = (char)(in[i] + sum/2);
}
}
static unsigned char png_paeth(unsigned char a, unsigned char b, unsigned char c)
{
int p = (int)a + b - c;
int pa = abs(p - a);
int pb = abs(p - b);
int pc = abs(p - c);
int pr;
if(pa <= pb && pa <= pc)
pr = a;
else if(pb <= pc)
pr = b;
else
pr = c;
return (char)pr;
}
static void png_filter_paeth(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
{
int i;
unsigned char a;
unsigned char b;
unsigned char c;
for(i = 0; i < len; i++)
{
if(prev_line && i >= stride)
{
a = out[i - stride];
b = prev_line[i];
c = prev_line[i - stride];
}
else
{
if(prev_line)
b = prev_line[i];
else
b = 0;
if(i >= stride)
a = out[i - stride];
else
a = 0;
c = 0;
}
out[i] = in[i] + png_paeth(a, b, c);
}
}
static int png_filter(png_t* png, unsigned char* data)
{
return PNG_NO_ERROR;
}
static int png_unfilter(png_t* png, unsigned char* data)
{
unsigned i;
unsigned pos = 0;
unsigned outpos = 0;
unsigned char *filtered = png->png_data;
int stride = png->bpp;
while(pos < png->png_datalen)
{
unsigned char filter = filtered[pos];
pos++;
if(png->depth == 16)
{
for(i = 0; i < png->width * stride; i+=2)
{
*(short*)(filtered+pos+i) = (filtered[pos+i] << 8) | filtered[pos+i+1];
}
}
switch(filter)
{
case 0: /* none */
memcpy(data+outpos, filtered+pos, png->width * stride);
break;
case 1: /* sub */
png_filter_sub(stride, filtered+pos, data+outpos, png->width * stride);
break;
case 2: /* up */
if(outpos)
png_filter_up(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
else
png_filter_up(stride, filtered+pos, data+outpos, 0, png->width*stride);
break;
case 3: /* average */
if(outpos)
png_filter_average(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
else
png_filter_average(stride, filtered+pos, data+outpos, 0, png->width*stride);
break;
case 4: /* paeth */
if(outpos)
png_filter_paeth(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
else
png_filter_paeth(stride, filtered+pos, data+outpos, 0, png->width*stride);
break;
default:
return PNG_UNKNOWN_FILTER;
}
outpos += png->width * stride;
pos += png->width * stride;
}
return PNG_NO_ERROR;
}
int png_get_data(png_t* png, unsigned char* data)
{
int result = PNG_NO_ERROR;
while(result == PNG_NO_ERROR)
{
result = png_process_chunk(png);
}
if(result != PNG_DONE)
{
png_free(png->png_data);
return result;
}
result = png_unfilter(png, data);
png_free(png->png_data);
return result;
}
int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data)
{
int i;
unsigned char *filtered;
png->width = width;
png->height = height;
png->depth = depth;
png->color_type = color;
png->bpp = png_get_bpp(png);
filtered = png_alloc(width * height * png->bpp + height);
for(i = 0; i < png->height; i++)
{
filtered[i*png->width*png->bpp+i] = 0;
memcpy(&filtered[i*png->width*png->bpp+i+1], data + i * png->width*png->bpp, png->width*png->bpp);
}
png_filter(png, filtered);
png_write_ihdr(png);
png_write_idats(png, filtered);
png_free(filtered);
return PNG_NO_ERROR;
}
char* png_error_string(int error)
{
switch(error)
{
case PNG_NO_ERROR:
return "No error";
case PNG_FILE_ERROR:
return "Unknown file error.";
case PNG_HEADER_ERROR:
return "No PNG header found. Are you sure this is a PNG?";
case PNG_IO_ERROR:
return "Failure while reading file.";
case PNG_EOF_ERROR:
return "Reached end of file.";
case PNG_CRC_ERROR:
return "CRC or chunk length error.";
case PNG_MEMORY_ERROR:
return "Could not allocate memory.";
case PNG_ZLIB_ERROR:
return "zlib reported an error.";
case PNG_UNKNOWN_FILTER:
return "Unknown filter method used in scanline.";
case PNG_DONE:
return "PNG done";
case PNG_NOT_SUPPORTED:
return "The PNG is unsupported by pnglite, too bad for you!";
case PNG_WRONG_ARGUMENTS:
return "Wrong combination of arguments passed to png_open. You must use either a read_function or supply a file pointer to use.";
default:
return "Unknown error.";
};
}

View file

@ -1,227 +0,0 @@
/* pnglite.h - Interface for pnglite library
Copyright (c) 2007 Daniel Karling
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Daniel Karling
daniel.karling@gmail.com
*/
#ifndef _PNGLITE_H_
#define _PNGLITE_H_
#ifdef __cplusplus
extern "C"{
#endif
/*
Enumerations for pnglite.
Negative numbers are error codes and 0 and up are okay responses.
*/
enum
{
PNG_DONE = 1,
PNG_NO_ERROR = 0,
PNG_FILE_ERROR = -1,
PNG_HEADER_ERROR = -2,
PNG_IO_ERROR = -3,
PNG_EOF_ERROR = -4,
PNG_CRC_ERROR = -5,
PNG_MEMORY_ERROR = -6,
PNG_ZLIB_ERROR = -7,
PNG_UNKNOWN_FILTER = -8,
PNG_NOT_SUPPORTED = -9,
PNG_WRONG_ARGUMENTS = -10
};
/*
The five different kinds of color storage in PNG files.
*/
enum
{
PNG_GREYSCALE = 0,
PNG_TRUECOLOR = 2,
PNG_INDEXED = 3,
PNG_GREYSCALE_ALPHA = 4,
PNG_TRUECOLOR_ALPHA = 6
};
/*
Typedefs for callbacks.
*/
typedef unsigned (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);
typedef unsigned (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer);
typedef void (*png_free_t)(void* p);
typedef void * (*png_alloc_t)(unsigned long s);
typedef struct
{
void* zs; /* pointer to z_stream */
png_read_callback_t read_fun;
png_write_callback_t write_fun;
void* user_pointer;
unsigned char* png_data;
unsigned png_datalen;
unsigned width;
unsigned height;
unsigned char depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlace_method;
unsigned char bpp;
}png_t;
/*
Function: png_init
This function initializes pnglite. The parameters can be used to set your own memory allocation routines following these formats:
> void* (*custom_alloc)(unsigned long s)
> void (*custom_free)(void* p)
Parameters:
pngalloc - Pointer to custom allocation routine. If 0 is passed, malloc from libc will be used.
pngfree - Pointer to custom free routine. If 0 is passed, free from libc will be used.
Returns:
Always returns PNG_NO_ERROR.
*/
int png_init(png_alloc_t pngalloc, png_free_t pngfree);
/*
Function: png_open_file
This function is used to open a png file with the internal file IO system. This function should be used instead of
png_open if no custom read function is used.
Parameters:
png - Empty png_t struct.
filename - Filename of the file to be opened.
Returns:
PNG_NO_ERROR on success, otherwise an error code.
*/
int png_open_file(png_t *png, const char* filename);
int png_open_file_read(png_t *png, const char* filename);
int png_open_file_write(png_t *png, const char* filename);
/*
Function: png_open
This function reads or writes a png from/to the specified callback. The callbacks should be of the format:
> unsigned long (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);
> unsigned long (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer).
Only one callback has to be specified. The read callback in case of PNG reading, otherwise the write callback.
Writing:
The callback will be called like fwrite.
Reading:
The callback will be called each time pnglite needs more data. The callback should read as much data as requested,
or return 0. This should always be possible if the PNG is sane. If the output-buffer is a null-pointer the callback
should only skip ahead the specified number of elements. If the callback is a null-pointer the user_pointer will be
treated as a file pointer (use png_open_file instead).
Parameters:
png - png_t struct
read_fun - Callback function for reading.
user_pointer - User pointer to be passed to read_fun.
Returns:
PNG_NO_ERROR on success, otherwise an error code.
*/
int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer);
int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer);
int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer);
/*
Function: png_print_info
This function prints some info about the opened png file to stdout.
Parameters:
png - png struct to get info from.
*/
void png_print_info(png_t* png);
/*
Function: png_error_string
This function translates an error code to a human readable string.
Parameters:
error - Error code.
Returns:
Pointer to string.
*/
char* png_error_string(int error);
/*
Function: png_get_data
This function decodes the opened png file and stores the result in data. data should be big enough to hold the decoded png. Required size will be:
> width*height*(bytes per pixel)
Parameters:
data - Where to store result.
Returns:
PNG_NO_ERROR on success, otherwise an error code.
*/
int png_get_data(png_t* png, unsigned char* data);
int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data);
/*
Function: png_close_file
Closes an open png file pointer. Should only be used when the png has been opened with png_open_file.
Parameters:
png - png to close.
Returns:
PNG_NO_ERROR
*/
int png_close_file(png_t* png);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,287 @@
#include "image_loader.h"
#include <base/system.h>
#include <cstdlib>
#include <png.h>
struct SLibPNGWarningItem
{
SImageByteBuffer *m_pByteLoader;
const char *pFileName;
};
static void LibPNGError(png_structp png_ptr, png_const_charp error_msg)
{
SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr);
pUserStruct->m_pByteLoader->m_Err = -1;
dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->pFileName, error_msg);
}
static void LibPNGWarning(png_structp png_ptr, png_const_charp warning_msg)
{
SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr);
dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->pFileName, warning_msg);
}
static bool FileMatchesImageType(SImageByteBuffer &ByteLoader)
{
if(ByteLoader.m_pLoadedImageBytes->size() >= 8)
return png_sig_cmp((png_bytep) & (*ByteLoader.m_pLoadedImageBytes)[0], 0, 8) == 0;
return false;
}
static void ReadDataFromLoadedBytes(png_structp pPNGStruct, png_bytep pOutBytes, png_size_t ByteCountToRead)
{
png_voidp pIO_Ptr = png_get_io_ptr(pPNGStruct);
SImageByteBuffer *pByteLoader = (SImageByteBuffer *)pIO_Ptr;
if(pByteLoader->m_pLoadedImageBytes->size() >= pByteLoader->m_LoadOffset + (size_t)ByteCountToRead)
{
mem_copy(pOutBytes, &(*pByteLoader->m_pLoadedImageBytes)[pByteLoader->m_LoadOffset], (size_t)ByteCountToRead);
pByteLoader->m_LoadOffset += (size_t)ByteCountToRead;
}
else
{
pByteLoader->m_Err = -1;
dbg_msg("png", "could not read bytes, file was too small.");
}
}
static int LibPNGGetColorChannelCount(int LibPNGColorType)
{
if(LibPNGColorType == PNG_COLOR_TYPE_GRAY)
return 1;
else if(LibPNGColorType == PNG_COLOR_TYPE_PALETTE || LibPNGColorType == PNG_COLOR_TYPE_RGB)
return 3;
else if(LibPNGColorType == PNG_COLOR_TYPE_RGBA)
return 4;
return 4;
}
static void LibPNGSetImageFormat(EImageFormat &ImageFormat, int LibPNGColorType)
{
ImageFormat = IMAGE_FORMAT_RGBA;
if(LibPNGColorType == PNG_COLOR_TYPE_GRAY)
ImageFormat = IMAGE_FORMAT_R;
else if(LibPNGColorType == PNG_COLOR_TYPE_PALETTE || LibPNGColorType == PNG_COLOR_TYPE_RGB)
ImageFormat = IMAGE_FORMAT_RGB;
else if(LibPNGColorType == PNG_COLOR_TYPE_RGBA)
ImageFormat = IMAGE_FORMAT_RGBA;
}
static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo)
{
png_destroy_info_struct(pPNGStruct, &pPNGInfo);
png_destroy_read_struct(&pPNGStruct, NULL, NULL);
}
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat)
{
png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(pPNGStruct == NULL)
{
dbg_msg("png", "libpng internal failure: png_create_read_struct failed.");
return false;
}
png_infop pPNGInfo = png_create_info_struct(pPNGStruct);
if(pPNGInfo == NULL)
{
png_destroy_read_struct(&pPNGStruct, NULL, NULL);
dbg_msg("png", "libpng internal failure: png_create_info_struct failed.");
return false;
}
SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName};
png_set_error_fn(pPNGStruct, &UserErrorStruct, LibPNGError, LibPNGWarning);
if(!FileMatchesImageType(ByteLoader))
{
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
}
ByteLoader.m_LoadOffset = 8;
png_set_read_fn(pPNGStruct, (png_bytep)&ByteLoader, ReadDataFromLoadedBytes);
png_set_sig_bytes(pPNGStruct, 8);
png_read_info(pPNGStruct, pPNGInfo);
if(ByteLoader.m_Err != 0)
{
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
}
Width = png_get_image_width(pPNGStruct, pPNGInfo);
Height = png_get_image_height(pPNGStruct, pPNGInfo);
int ColorType = png_get_color_type(pPNGStruct, pPNGInfo);
png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo);
bool PNGErr = false;
if(BitDepth == 16)
png_set_strip_16(pPNGStruct);
else if(BitDepth > 8)
{
dbg_msg("png", "non supported bit depth.");
PNGErr = true;
}
if(Width == 0 || Height == 0 || BitDepth == 0)
{
dbg_msg("png", "image had width or height of 0.");
PNGErr = true;
}
if(!PNGErr)
{
if(ColorType == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(pPNGStruct);
if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8)
png_set_expand_gray_1_2_4_to_8(pPNGStruct);
if(png_get_valid(pPNGStruct, pPNGInfo, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pPNGStruct);
png_read_update_info(pPNGStruct, pPNGInfo);
int ColorChannelCount = LibPNGGetColorChannelCount(ColorType);
int BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo);
if(BytesInRow == Width * ColorChannelCount)
{
png_bytepp pRowPointers = new png_bytep[Height];
for(int y = 0; y < Height; ++y)
{
pRowPointers[y] = new png_byte[BytesInRow];
}
png_read_image(pPNGStruct, pRowPointers);
if(ByteLoader.m_Err == 0)
pImageBuff = (uint8_t *)malloc((size_t)Height * (size_t)Width * (size_t)ColorChannelCount * sizeof(uint8_t));
else
PNGErr = true;
for(int i = 0; i < Height; ++i)
{
if(ByteLoader.m_Err == 0)
mem_copy(&pImageBuff[i * BytesInRow], pRowPointers[i], BytesInRow);
delete[] pRowPointers[i];
}
delete[] pRowPointers;
LibPNGSetImageFormat(ImageFormat, ColorType);
}
else
PNGErr = true;
}
png_destroy_info_struct(pPNGStruct, &pPNGInfo);
png_destroy_read_struct(&pPNGStruct, NULL, NULL);
return !PNGErr;
}
static void WriteDataFromLoadedBytes(png_structp pPNGStruct, png_bytep pOutBytes, png_size_t ByteCountToWrite)
{
if(ByteCountToWrite > 0)
{
png_voidp pIO_Ptr = png_get_io_ptr(pPNGStruct);
SImageByteBuffer *pByteLoader = (SImageByteBuffer *)pIO_Ptr;
size_t NewSize = pByteLoader->m_LoadOffset + (size_t)ByteCountToWrite;
pByteLoader->m_pLoadedImageBytes->resize(NewSize);
mem_copy(&(*pByteLoader->m_pLoadedImageBytes)[pByteLoader->m_LoadOffset], pOutBytes, (size_t)ByteCountToWrite);
pByteLoader->m_LoadOffset = NewSize;
}
}
static void FlushPNGWrite(png_structp png_ptr) {}
static int ImageLoaderHelperFormatToColorChannel(EImageFormat Format)
{
if(Format == IMAGE_FORMAT_R)
return 1;
else if(Format == IMAGE_FORMAT_RGB)
return 3;
else if(Format == IMAGE_FORMAT_RGBA)
return 4;
return 4;
}
bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuffer &WrittenBytes, int Width, int Height)
{
png_structp pPNGStruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(pPNGStruct == NULL)
{
dbg_msg("png", "libpng internal failure: png_create_write_struct failed.");
return false;
}
png_infop pPNGInfo = png_create_info_struct(pPNGStruct);
if(pPNGInfo == NULL)
{
png_destroy_read_struct(&pPNGStruct, NULL, NULL);
dbg_msg("png", "libpng internal failure: png_create_info_struct failed.");
return false;
}
WrittenBytes.m_LoadOffset = 0;
WrittenBytes.m_pLoadedImageBytes->clear();
png_set_write_fn(pPNGStruct, (png_bytep)&WrittenBytes, WriteDataFromLoadedBytes, FlushPNGWrite);
int ColorType = PNG_COLOR_TYPE_RGB;
int WriteBytesPerPixel = ImageLoaderHelperFormatToColorChannel(ImageFormat);
if(ImageFormat == IMAGE_FORMAT_R)
{
ColorType = PNG_COLOR_TYPE_GRAY;
}
else if(ImageFormat == IMAGE_FORMAT_RGBA)
{
ColorType = PNG_COLOR_TYPE_RGBA;
}
png_set_IHDR(pPNGStruct, pPNGInfo, Width, Height, 8, ColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(pPNGStruct, pPNGInfo);
png_bytepp pRowPointers = new png_bytep[Height];
int WidthBytes = Width * WriteBytesPerPixel;
ptrdiff_t BufferOffset = 0;
for(int y = 0; y < Height; ++y)
{
pRowPointers[y] = new png_byte[WidthBytes];
mem_copy(pRowPointers[y], pRawBuffer + BufferOffset, WidthBytes);
BufferOffset += (ptrdiff_t)WidthBytes;
}
png_write_image(pPNGStruct, pRowPointers);
png_write_end(pPNGStruct, pPNGInfo);
for(int y = 0; y < Height; ++y)
{
delete[](pRowPointers[y]);
}
delete[](pRowPointers);
png_destroy_info_struct(pPNGStruct, &pPNGInfo);
png_destroy_write_struct(&pPNGStruct, NULL);
return true;
}

View file

@ -0,0 +1,28 @@
#ifndef ENGINE_SHARED_IMAGE_LOADER_H
#define ENGINE_SHARED_IMAGE_LOADER_H
#include <stddef.h>
#include <stdint.h>
#include <vector>
enum EImageFormat
{
IMAGE_FORMAT_R = 0,
IMAGE_FORMAT_RGB,
IMAGE_FORMAT_RGBA,
};
typedef std::vector<uint8_t> TImageByteBuffer;
struct SImageByteBuffer
{
SImageByteBuffer(TImageByteBuffer *pBuff) :
m_LoadOffset(0), m_pLoadedImageBytes(pBuff), m_Err(0) {}
size_t m_LoadOffset;
TImageByteBuffer *m_pLoadedImageBytes;
int m_Err;
};
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat);
bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuffer &WrittenBytes, int Width, int Height);
#endif

View file

@ -2,71 +2,74 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/logger.h>
#include <base/system.h>
#include <engine/graphics.h>
#include <engine/shared/image_loader.h>
#include <engine/shared/image_manipulation.h>
#include <pnglite.h>
int DilateFile(const char *pFilename)
{
IOHANDLE File = io_open(pFilename, IOFLAG_READ);
if(!File)
if(File)
{
dbg_msg("dilate", "failed to open file. filename='%s'", pFilename);
return 0;
}
io_seek(File, 0, IOSEEK_END);
unsigned int FileSize = io_tell(File);
io_seek(File, 0, IOSEEK_START);
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
ByteBuffer.resize(FileSize);
io_read(File, &ByteBuffer.front(), FileSize);
png_init(0, 0);
png_t Png;
int Error = png_open_read(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("dilate", "failed to open image file. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
io_close(File);
return 0;
}
if(Png.color_type != PNG_TRUECOLOR_ALPHA)
CImageInfo Img;
uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, Img.m_Width, Img.m_Height, pImgBuffer, ImageFormat))
{
if(ImageFormat != IMAGE_FORMAT_RGBA)
{
free(pImgBuffer);
dbg_msg("dilate", "%s: not an RGBA image", pFilename);
return -1;
}
Img.m_pData = pImgBuffer;
unsigned char *pBuffer = (unsigned char *)Img.m_pData;
int w = Img.m_Width;
int h = Img.m_Height;
DilateImage(pBuffer, w, h, 4);
// save here
IOHANDLE SaveFile = io_open(pFilename, IOFLAG_WRITE);
if(SaveFile)
{
TImageByteBuffer ByteBuffer2;
SImageByteBuffer ImageByteBuffer2(&ByteBuffer2);
if(SavePNG(IMAGE_FORMAT_RGBA, (const uint8_t *)pBuffer, ImageByteBuffer2, w, h))
io_write(SaveFile, &ByteBuffer2.front(), ByteBuffer2.size());
io_close(SaveFile);
free(pBuffer);
}
}
else
{
dbg_msg("dilate", "failed unknown image format: %s", pFilename);
return -1;
}
}
else
{
dbg_msg("dilate", "%s: not an RGBA image", pFilename);
return 1;
dbg_msg("dilate", "failed to open image file. filename='%s'", pFilename);
return -1;
}
unsigned char *pBuffer = (unsigned char *)malloc((size_t)Png.width * Png.height * sizeof(unsigned char) * 4);
Error = png_get_data(&Png, pBuffer);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_convert_07", "failed to read image. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
free(pBuffer);
io_close(File);
return 0;
}
io_close(File);
int w = Png.width;
int h = Png.height;
DilateImage(pBuffer, w, h, 4);
// save here
File = io_open(pFilename, IOFLAG_WRITE);
if(!File)
{
dbg_msg("dilate", "failed to open file. filename='%s'", pFilename);
free(pBuffer);
return 0;
}
Error = png_open_write(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("dilate", "failed to open image file. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
io_close(File);
return 0;
}
png_set_data(&Png, w, h, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)pBuffer);
io_close(File);
free(pBuffer);
return 0;
}

View file

@ -9,7 +9,7 @@
#include <game/gamecore.h>
#include <game/mapitems.h>
#include <pnglite.h>
#include <engine/shared/image_loader.h>
/*
Usage: map_convert_07 <source map filepath> <dest map filepath>
*/
@ -29,43 +29,40 @@ int g_aImageIDs[64];
int LoadPNG(CImageInfo *pImg, const char *pFilename)
{
IOHANDLE File = io_open(pFilename, IOFLAG_READ);
if(!File)
if(File)
{
dbg_msg("map_convert_07", "failed to open file. filename='%s'", pFilename);
return 0;
}
io_seek(File, 0, IOSEEK_END);
unsigned int FileSize = io_tell(File);
io_seek(File, 0, IOSEEK_START);
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
ByteBuffer.resize(FileSize);
io_read(File, &ByteBuffer.front(), FileSize);
png_t Png;
int Error = png_open_read(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_convert_07", "failed to open image file. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
io_close(File);
return 0;
}
if(Png.depth != 8 || Png.color_type != PNG_TRUECOLOR_ALPHA || Png.width > (2 << 12) || Png.height > (2 << 12))
{
dbg_msg("map_convert_07", "invalid image format. filename='%s'", pFilename);
io_close(File);
return 0;
}
uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{
pImg->m_pData = pImgBuffer;
unsigned char *pBuffer = (unsigned char *)malloc((size_t)Png.width * Png.height * Png.bpp);
Error = png_get_data(&Png, pBuffer);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_convert_07", "failed to read image. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
free(pBuffer);
io_close(File);
return 0;
if(ImageFormat == IMAGE_FORMAT_RGBA && pImg->m_Width <= (2 << 13) && pImg->m_Height <= (2 << 13))
{
pImg->m_Format = CImageInfo::FORMAT_RGBA;
}
else
{
dbg_msg("map_convert_07", "invalid image format. filename='%s'", pFilename);
return 0;
}
}
else
return 0;
}
io_close(File);
pImg->m_Width = Png.width;
pImg->m_Height = Png.height;
pImg->m_Format = CImageInfo::FORMAT_RGBA;
pImg->m_pData = pBuffer;
else
return 0;
return 1;
}
@ -188,8 +185,6 @@ int main(int argc, const char **argv)
return -1;
}
png_init(0, 0);
g_NextDataItemID = g_DataReader.NumData();
int i = 0;

View file

@ -5,7 +5,7 @@
#include <engine/storage.h>
#include <game/mapitems.h>
#include <pnglite.h>
#include <engine/shared/image_loader.h>
bool Process(IStorage *pStorage, const char *pMapName, const char *pPathSave)
{
@ -52,22 +52,15 @@ bool Process(IStorage *pStorage, const char *pMapName, const char *pPathSave)
// copy image data
IOHANDLE File = io_open(aBuf, IOFLAG_WRITE);
if(!File)
if(File)
{
dbg_msg("map_extract", "failed to open file. filename='%s'", aBuf);
continue;
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
if(SavePNG(IMAGE_FORMAT_RGBA, (const uint8_t *)Reader.GetData(pItem->m_ImageData), ImageByteBuffer, pItem->m_Width, pItem->m_Height))
io_write(File, &ByteBuffer.front(), ByteBuffer.size());
io_close(File);
}
png_t Png;
int Error = png_open_write(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_extract", "failed to write image file. filename='%s', pnglite: %s", aBuf, png_error_string(Error));
}
else
{
png_set_data(&Png, pItem->m_Width, pItem->m_Height, 8, PNG_TRUECOLOR_ALPHA, (unsigned char *)Reader.GetData(pItem->m_ImageData));
}
io_close(File);
}
// load sounds
@ -123,8 +116,6 @@ int main(int argc, const char *argv[])
return -1;
}
png_init(0, 0);
int Result = Process(pStorage, argv[1], pDir) ? 0 : 1;
return Result;
}

View file

@ -8,7 +8,7 @@
#include <engine/storage.h>
#include <game/mapitems.h>
#include <pnglite.h>
#include <engine/shared/image_loader.h>
/*
Usage: map_replace_image <source map filepath> <dest map filepath> <current image name> <new image filepath>
Notes: map filepath must be relative to user default teeworlds folder
@ -26,52 +26,44 @@ void *g_pNewData = nullptr;
int LoadPNG(CImageInfo *pImg, const char *pFilename)
{
png_t Png;
IOHANDLE File = io_open(pFilename, IOFLAG_READ);
if(!File)
if(File)
{
dbg_msg("map_replace_image", "failed to open file. filename='%s'", pFilename);
return 0;
}
int Error = png_open_read(&Png, 0, File);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_replace_image", "failed to open image file. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
io_close(File);
return 0;
}
io_seek(File, 0, IOSEEK_END);
unsigned int FileSize = io_tell(File);
io_seek(File, 0, IOSEEK_START);
TImageByteBuffer ByteBuffer;
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA) || Png.width > (2 << 12) || Png.height > (2 << 12))
{
dbg_msg("map_replace_image", "invalid image format. filename='%s'", pFilename);
io_close(File);
return 0;
}
ByteBuffer.resize(FileSize);
io_read(File, &ByteBuffer.front(), FileSize);
unsigned char *pBuffer = (unsigned char *)malloc((size_t)Png.width * Png.height * Png.bpp);
Error = png_get_data(&Png, pBuffer);
if(Error != PNG_NO_ERROR)
{
dbg_msg("map_replace_image", "failed to read image. filename='%s', pnglite: %s", pFilename, png_error_string(Error));
free(pBuffer);
io_close(File);
return 0;
}
io_close(File);
pImg->m_Width = Png.width;
pImg->m_Height = Png.height;
if(Png.color_type == PNG_TRUECOLOR)
pImg->m_Format = CImageInfo::FORMAT_RGB;
else if(Png.color_type == PNG_TRUECOLOR_ALPHA)
pImg->m_Format = CImageInfo::FORMAT_RGBA;
uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{
if((ImageFormat == IMAGE_FORMAT_RGBA || ImageFormat == IMAGE_FORMAT_RGB) && pImg->m_Width <= (2 << 13) && pImg->m_Height <= (2 << 13))
{
pImg->m_pData = pImgBuffer;
if(ImageFormat == IMAGE_FORMAT_RGB) // ignore_convention
pImg->m_Format = CImageInfo::FORMAT_RGB;
else if(ImageFormat == IMAGE_FORMAT_RGBA) // ignore_convention
pImg->m_Format = CImageInfo::FORMAT_RGBA;
else
{
free(pImgBuffer);
return 0;
}
}
}
else
return 0;
}
else
{
free(pBuffer);
return 0;
}
pImg->m_pData = pBuffer;
return 1;
}
@ -146,8 +138,6 @@ int main(int argc, const char **argv)
return -1;
}
png_init(0, 0);
// add all items
for(int Index = 0; Index < g_DataReader.NumItems(); Index++)
{