修改,使其完全支持png图片

This commit is contained in:
xiaoyuluoke 2024-03-14 21:56:51 +08:00
parent 999d4fb76c
commit f8daa75315
3 changed files with 195 additions and 43 deletions

View File

@ -85,13 +85,12 @@ if(NANA_CMAKE_ENABLE_AUDIO)
/audio/detail
)
endif()
add_library(spng spng/spng.c)
# collect all source files in the source-sub-dir
foreach(subdir ${NANA_SOURCE_SUBDIRS})
aux_source_directory(${NANA_SOURCE_DIR}${subdir} SOURCES) # todo: use GLOB to add headers too ??
endforeach()
target_sources(nana PRIVATE spng/spng.c ${SOURCES} )
target_sources(nana PRIVATE ${SOURCES} )
### collect all headers sub-directories in a list to avoid duplication ###
# To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library / target_sources
@ -154,13 +153,14 @@ if(NANA_CMAKE_AUTOMATIC_GUI_TESTING)
# todo: enable_testing() # ??
endif()
############# Optional libraries #####################
add_definitions(-DSPNG_STATIC)
set(PNG_DIR $ENV{EXPLAB_LIBS}/libpng)
include_directories(${PNG_DIR}/include)
set(ZLIB_DIR $ENV{EXPLAB_LIBS}/zlib)
include_directories(${ZLIB_DIR}/include)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
target_link_libraries(spng ${ZLIB_DIR}/lib/zlibstaticd.lib)
set(PNG_LIBRARS ${ZLIB_DIR}/lib/zlibstaticd.lib ${PNG_DIR}/lib/libpng16_staticd.lib)
else()
link_libraries(${ZLIB_DIR}/lib/zlibstatic.lib)
set(PNG_LIBRARS ${ZLIB_DIR}/lib/zlibstatic.lib ${PNG_DIR}/lib/libpng16_static.lib)
endif()
target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG)

View File

@ -17,11 +17,10 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN
target_compile_options(nana PRIVATE)
# todo: set in target property of nana
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -mtune=native")
set(THREADS_PREFER_PTHREAD_FLAG ON) # todo - test this
find_package(Threads REQUIRED)
target_link_libraries(nana PRIVATE Threads::Threads )
target_link_libraries(nana PRIVATE Threads::Threads ${PNG_LIBRARS})
# target_compile_options(nana PUBLIC -pthread)

View File

@ -6,9 +6,11 @@
//Separate the libpng from the package that system provides.
#if defined(NANA_LIBPNG)
#include "spng.h"
#include <nana_extrlib/png.h>
#else
#include <png.h>
#endif
#include "spng.h"
#include <stdio.h>
@ -19,54 +21,205 @@ namespace nana
class image_png
: public basic_image_pixbuf
{
void _m_read_png(png_structp png_ptr, png_infop info_ptr)
{
::png_read_info(png_ptr, info_ptr);
const int png_width = ::png_get_image_width(png_ptr, info_ptr);
const int png_height = ::png_get_image_height(png_ptr, info_ptr);
png_byte color_type = ::png_get_color_type(png_ptr, info_ptr);
const auto bit_depth = ::png_get_bit_depth(png_ptr, info_ptr);
pixbuf_.open(png_width, png_height);
//do some extra work for palette/gray color type
if (PNG_COLOR_TYPE_PALETTE == color_type)
::png_set_palette_to_rgb(png_ptr);
else if ((PNG_COLOR_TYPE_GRAY == color_type) || (PNG_COLOR_TYPE_GRAY_ALPHA == color_type))
::png_set_gray_to_rgb(png_ptr);
auto is_alpha_enabled = (::png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0);
if (is_alpha_enabled)
::png_set_tRNS_to_alpha(png_ptr);
is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0);
pixbuf_.alpha_channel(is_alpha_enabled);
//make sure 8-bit per channel
if (16 == bit_depth)
::png_set_strip_16(png_ptr);
::png_set_interlace_handling(png_ptr);
::png_read_update_info(png_ptr, info_ptr);
//The following codes may longjmp while image_read error.
png_bytep * row_ptrs = new png_bytep[png_height];
const std::size_t png_rowbytes = ::png_get_rowbytes(png_ptr, info_ptr);
if (is_alpha_enabled && (png_rowbytes == png_width * sizeof(pixel_argb_t)))
{
for (int i = 0; i < png_height; ++i)
row_ptrs[i] = reinterpret_cast<png_bytep>(pixbuf_.raw_ptr(i));
::png_read_image(png_ptr, row_ptrs);
if (std::is_same<pixel_argb_t, pixel_color_t>::value)
{
for (int i = 0; i < png_height; ++i)
{
auto p = pixbuf_.raw_ptr(i);
for (int u = 0; u < png_width; ++u)
{
auto t = p[u].element.red;
p[u].element.red = p[u].element.blue;
p[u].element.blue = t;
}
}
}
}
else
{
png_byte * png_pixbuf = new png_byte[png_height * png_rowbytes];
for (int i = 0; i < png_height; ++i)
row_ptrs[i] = reinterpret_cast<png_bytep>(png_pixbuf + png_rowbytes * i);
::png_read_image(png_ptr, row_ptrs);
std::size_t png_pixel_bytes = png_rowbytes / png_width;
pixel_argb_t * rgb_row_ptr = pixbuf_.raw_ptr(0);
for (int y = 0; y < png_height; ++y)
{
png_bytep png_ptr = row_ptrs[y];
pixel_argb_t * rgb_end = rgb_row_ptr + png_width;
if (is_alpha_enabled)
{
for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i)
{
i->element.red = png_ptr[0];
i->element.green = png_ptr[1];
i->element.blue = png_ptr[2];
i->element.alpha_channel = png_ptr[3];
png_ptr += png_pixel_bytes;
}
}
else
{
for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i)
{
i->element.red = png_ptr[0];
i->element.green = png_ptr[1];
i->element.blue = png_ptr[2];
i->element.alpha_channel = 255;
png_ptr += png_pixel_bytes;
}
}
rgb_row_ptr = rgb_end;
}
delete[] png_pixbuf;
}
delete[] row_ptrs;
}
public:
bool open(const std::filesystem::path& png_file) override
{
int r = 0;
auto fp = ::fopen(to_osmbstr(to_utf8(png_file.native())).c_str(), "rb");
if(nullptr == fp) return false;
spng_ctx *ctx = spng_ctx_new(0);
bool is_opened = false;
r = spng_set_png_file(ctx, fp);
// r = spng_set_png_buffer(ctx, test_case->source.buffer, test_case->source.png_size);
struct spng_ihdr tmp;
struct spng_ihdr *ihdr;
if(!ihdr) ihdr = &tmp;
spng_get_ihdr(ctx, ihdr);
size_t len;
spng_decoded_image_size(ctx,SPNG_FMT_RGBA8,&len);
pixbuf_.open(ihdr->width, ihdr->height);
printf("png size: w: %d h: %d\n",ihdr->width,ihdr->height);
spng_decode_image(ctx, pixbuf_[point{0,0}],len , SPNG_FMT_RGBA8, SPNG_DECODE_TRNS);
switch (ihdr->color_type) {
case SPNG_COLOR_TYPE_GRAYSCALE :
case SPNG_COLOR_TYPE_TRUECOLOR :
case SPNG_COLOR_TYPE_INDEXED :
pixbuf_.alpha_channel(false);
break;
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA :
pixbuf_.alpha_channel(true);
break;
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA :
pixbuf_.alpha_channel(true);
break;
default:
pixbuf_.alpha_channel(false);
break;
}
if(r)
png_byte png_sig[8];
::fread(png_sig, 1, 8, fp);
//Test whether the file is a png.
if(0 == png_sig_cmp(png_sig, 0, 8))
{
printf("spng_set_png_file/buffer() error: %s\n", spng_strerror(r));
png_structp png_ptr = ::png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if(png_ptr)
{
png_infop info_ptr = ::png_create_info_struct(png_ptr);
if(info_ptr)
{
if(!setjmp(png_jmpbuf(png_ptr)))
{
//The following codes may longjmp while init_io error.
::png_init_io(png_ptr, fp);
//8-byte of sig has been read, tell the libpng there are some bytes missing from start of file
::png_set_sig_bytes(png_ptr, 8);
_m_read_png(png_ptr, info_ptr);
is_opened = true;
}
}
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
}
}
spng_ctx_free(ctx);
::fclose(fp);
is_opened = true;
return is_opened;
}
class png_reader
{
public:
png_reader(const void* data, std::size_t bytes) noexcept
: data_ptr_(reinterpret_cast<const char*>(data)), bytes_(bytes)
{
}
static void PNGCAPI read(png_structp png_ptr, png_bytep buf, png_size_t bytes)
{
auto self = reinterpret_cast<png_reader*>(::png_get_io_ptr(png_ptr));
auto read_bytes = self->bytes_ < bytes ? self->bytes_ : bytes;
if (read_bytes)
std::memcpy(buf, self->data_ptr_, read_bytes);
self->bytes_ -= read_bytes;
self->data_ptr_ += read_bytes;
}
private:
const char* data_ptr_;
std::size_t bytes_;
};
bool open(const void* data, std::size_t bytes) override
{
printf("%s --->\n",data);
if (bytes < 8 || 0 != ::png_sig_cmp(reinterpret_cast<png_const_bytep>(data), 0, 8))
return false;
auto png_ptr = ::png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr)
return false;
bool is_opened = false;
png_infop info_ptr = ::png_create_info_struct(png_ptr);
if (info_ptr)
{
png_reader reader{ data, bytes };
if (!setjmp(png_jmpbuf(png_ptr)))
{
::png_set_read_fn(png_ptr, &reader, &png_reader::read);
_m_read_png(png_ptr, info_ptr);
is_opened = true;
}
}
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return is_opened;
}
};