spng encoder/decoder added as optional png codec

pull/22226/head
Berke 2 years ago committed by ocpalo
parent 50e8ad285b
commit 3929e26276
  1. 47
      3rdparty/libspng/CMakeLists.txt
  2. 25
      3rdparty/libspng/LICENSE
  3. 6978
      3rdparty/libspng/spng.c
  4. 537
      3rdparty/libspng/spng.h
  5. 11
      CMakeLists.txt
  6. 16
      cmake/OpenCVFindLibsGrfmt.cmake
  7. 3
      cmake/templates/cvconfig.h.in
  8. 4
      modules/highgui/src/window_w32.cpp
  9. 8
      modules/imgcodecs/CMakeLists.txt
  10. 41
      modules/imgcodecs/perf/perf_png.cpp
  11. 754
      modules/imgcodecs/src/grfmt_spng.cpp
  12. 60
      modules/imgcodecs/src/grfmt_spng.hpp
  13. 1
      modules/imgcodecs/src/grfmts.hpp
  14. 5
      modules/imgcodecs/src/loadsave.cpp
  15. 2
      modules/imgcodecs/test/test_grfmt.cpp
  16. 221
      modules/imgcodecs/test/test_png.cpp
  17. 4
      modules/imgcodecs/test/test_read_write.cpp
  18. 2
      modules/imgproc/test/test_drawing.cpp

@ -0,0 +1,47 @@
# ----------------------------------------------------------------------------
# CMake file for libspng. See root CMakeLists.txt
#
# ----------------------------------------------------------------------------
project(${SPNG_LIBRARY})
set(CURR_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}")
set_property(GLOBAL PROPERTY SPNG_INCLUDE_DIR ${CURR_INCLUDE_DIR})
ocv_include_directories(${ZLIB_INCLUDE_DIRS})
file(GLOB_RECURSE spng_headers RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "*.h")
file(GLOB_RECURSE spng_sources RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "*.c")
message(STATUS "libspng will be used as PNG codec")
# ----------------------------------------------------------------------------------
# Define the library target:
# ----------------------------------------------------------------------------------
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
endif(MSVC)
add_library(${SPNG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${spng_headers} ${spng_sources})
ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-variable)
target_link_libraries(${SPNG_LIBRARY} ${ZLIB_LIBRARIES})
set_target_properties(${SPNG_LIBRARY}
PROPERTIES OUTPUT_NAME ${SPNG_LIBRARY}
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
COMPILE_PDB_NAME ${SPNG_LIBRARY}
COMPILE_PDB_NAME_DEBUG "${SPNG_LIBRARY}${OPENCV_DEBUG_POSTFIX}"
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
)
target_compile_definitions(${SPNG_LIBRARY} PUBLIC SPNG_STATIC)
if(ENABLE_SOLUTION_FOLDERS)
set_target_properties(${SPNG_LIBRARY} PROPERTIES FOLDER "3rdparty")
endif()
if(NOT BUILD_SHARED_LIBS)
ocv_install_target(${SPNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
endif()
ocv_install_3rdparty_licenses(${SPNG_LIBRARY} LICENSE)

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2018-2022, Randy <randy408@protonmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,537 @@
/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */
#ifndef SPNG_H
#define SPNG_H
#ifdef __cplusplus
extern "C" {
#endif
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC)
#if defined(SPNG__BUILD)
#define SPNG_API __declspec(dllexport)
#else
#define SPNG_API __declspec(dllimport)
#endif
#else
#define SPNG_API
#endif
#if defined(_MSC_VER)
#define SPNG_CDECL __cdecl
#else
#define SPNG_CDECL
#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#define SPNG_VERSION_MAJOR 0
#define SPNG_VERSION_MINOR 7
#define SPNG_VERSION_PATCH 3
enum spng_errno
{
SPNG_IO_ERROR = -2,
SPNG_IO_EOF = -1,
SPNG_OK = 0,
SPNG_EINVAL,
SPNG_EMEM,
SPNG_EOVERFLOW,
SPNG_ESIGNATURE,
SPNG_EWIDTH,
SPNG_EHEIGHT,
SPNG_EUSER_WIDTH,
SPNG_EUSER_HEIGHT,
SPNG_EBIT_DEPTH,
SPNG_ECOLOR_TYPE,
SPNG_ECOMPRESSION_METHOD,
SPNG_EFILTER_METHOD,
SPNG_EINTERLACE_METHOD,
SPNG_EIHDR_SIZE,
SPNG_ENOIHDR,
SPNG_ECHUNK_POS,
SPNG_ECHUNK_SIZE,
SPNG_ECHUNK_CRC,
SPNG_ECHUNK_TYPE,
SPNG_ECHUNK_UNKNOWN_CRITICAL,
SPNG_EDUP_PLTE,
SPNG_EDUP_CHRM,
SPNG_EDUP_GAMA,
SPNG_EDUP_ICCP,
SPNG_EDUP_SBIT,
SPNG_EDUP_SRGB,
SPNG_EDUP_BKGD,
SPNG_EDUP_HIST,
SPNG_EDUP_TRNS,
SPNG_EDUP_PHYS,
SPNG_EDUP_TIME,
SPNG_EDUP_OFFS,
SPNG_EDUP_EXIF,
SPNG_ECHRM,
SPNG_EPLTE_IDX,
SPNG_ETRNS_COLOR_TYPE,
SPNG_ETRNS_NO_PLTE,
SPNG_EGAMA,
SPNG_EICCP_NAME,
SPNG_EICCP_COMPRESSION_METHOD,
SPNG_ESBIT,
SPNG_ESRGB,
SPNG_ETEXT,
SPNG_ETEXT_KEYWORD,
SPNG_EZTXT,
SPNG_EZTXT_COMPRESSION_METHOD,
SPNG_EITXT,
SPNG_EITXT_COMPRESSION_FLAG,
SPNG_EITXT_COMPRESSION_METHOD,
SPNG_EITXT_LANG_TAG,
SPNG_EITXT_TRANSLATED_KEY,
SPNG_EBKGD_NO_PLTE,
SPNG_EBKGD_PLTE_IDX,
SPNG_EHIST_NO_PLTE,
SPNG_EPHYS,
SPNG_ESPLT_NAME,
SPNG_ESPLT_DUP_NAME,
SPNG_ESPLT_DEPTH,
SPNG_ETIME,
SPNG_EOFFS,
SPNG_EEXIF,
SPNG_EIDAT_TOO_SHORT,
SPNG_EIDAT_STREAM,
SPNG_EZLIB,
SPNG_EFILTER,
SPNG_EBUFSIZ,
SPNG_EIO,
SPNG_EOF,
SPNG_EBUF_SET,
SPNG_EBADSTATE,
SPNG_EFMT,
SPNG_EFLAGS,
SPNG_ECHUNKAVAIL,
SPNG_ENCODE_ONLY,
SPNG_EOI,
SPNG_ENOPLTE,
SPNG_ECHUNK_LIMITS,
SPNG_EZLIB_INIT,
SPNG_ECHUNK_STDLEN,
SPNG_EINTERNAL,
SPNG_ECTXTYPE,
SPNG_ENOSRC,
SPNG_ENODST,
SPNG_EOPSTATE,
SPNG_ENOTFINAL,
};
enum spng_text_type
{
SPNG_TEXT = 1,
SPNG_ZTXT = 2,
SPNG_ITXT = 3
};
enum spng_color_type
{
SPNG_COLOR_TYPE_GRAYSCALE = 0,
SPNG_COLOR_TYPE_TRUECOLOR = 2,
SPNG_COLOR_TYPE_INDEXED = 3,
SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4,
SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
};
enum spng_filter
{
SPNG_FILTER_NONE = 0,
SPNG_FILTER_SUB = 1,
SPNG_FILTER_UP = 2,
SPNG_FILTER_AVERAGE = 3,
SPNG_FILTER_PAETH = 4
};
enum spng_filter_choice
{
SPNG_DISABLE_FILTERING = 0,
SPNG_FILTER_CHOICE_NONE = 8,
SPNG_FILTER_CHOICE_SUB = 16,
SPNG_FILTER_CHOICE_UP = 32,
SPNG_FILTER_CHOICE_AVG = 64,
SPNG_FILTER_CHOICE_PAETH = 128,
SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128)
};
enum spng_interlace_method
{
SPNG_INTERLACE_NONE = 0,
SPNG_INTERLACE_ADAM7 = 1
};
/* Channels are always in byte-order */
enum spng_format
{
SPNG_FMT_RGBA8 = 1,
SPNG_FMT_RGBA16 = 2,
SPNG_FMT_RGB8 = 4,
/* Partially implemented, see documentation */
SPNG_FMT_GA8 = 16,
SPNG_FMT_GA16 = 32,
SPNG_FMT_G8 = 64,
/* No conversion or scaling */
SPNG_FMT_PNG = 256,
SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */
};
enum spng_ctx_flags
{
SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */
SPNG_CTX_ENCODER = 2 /* Create an encoder context */
};
enum spng_decode_flags
{
SPNG_DECODE_USE_TRNS = 1, /* Deprecated */
SPNG_DECODE_USE_GAMA = 2, /* Deprecated */
SPNG_DECODE_USE_SBIT = 8, /* Undocumented */
SPNG_DECODE_TRNS = 1, /* Apply transparency */
SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */
SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */
};
enum spng_crc_action
{
/* Default for critical chunks */
SPNG_CRC_ERROR = 0,
/* Discard chunk, invalid for critical chunks.
Since v0.6.2: default for ancillary chunks */
SPNG_CRC_DISCARD = 1,
/* Ignore and don't calculate checksum.
Since v0.6.2: also ignores checksums in DEFLATE streams */
SPNG_CRC_USE = 2
};
enum spng_encode_flags
{
SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */
SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */
};
struct spng_ihdr
{
uint32_t width;
uint32_t height;
uint8_t bit_depth;
uint8_t color_type;
uint8_t compression_method;
uint8_t filter_method;
uint8_t interlace_method;
};
struct spng_plte_entry
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha; /* Reserved for internal use */
};
struct spng_plte
{
uint32_t n_entries;
struct spng_plte_entry entries[256];
};
struct spng_trns
{
uint16_t gray;
uint16_t red;
uint16_t green;
uint16_t blue;
uint32_t n_type3_entries;
uint8_t type3_alpha[256];
};
struct spng_chrm_int
{
uint32_t white_point_x;
uint32_t white_point_y;
uint32_t red_x;
uint32_t red_y;
uint32_t green_x;
uint32_t green_y;
uint32_t blue_x;
uint32_t blue_y;
};
struct spng_chrm
{
double white_point_x;
double white_point_y;
double red_x;
double red_y;
double green_x;
double green_y;
double blue_x;
double blue_y;
};
struct spng_iccp
{
char profile_name[80];
size_t profile_len;
char *profile;
};
struct spng_sbit
{
uint8_t grayscale_bits;
uint8_t red_bits;
uint8_t green_bits;
uint8_t blue_bits;
uint8_t alpha_bits;
};
struct spng_text
{
char keyword[80];
int type;
size_t length;
char *text;
uint8_t compression_flag; /* iTXt only */
uint8_t compression_method; /* iTXt, ztXt only */
char *language_tag; /* iTXt only */
char *translated_keyword; /* iTXt only */
};
struct spng_bkgd
{
uint16_t gray; /* Only for gray/gray alpha */
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t plte_index; /* Only for indexed color */
};
struct spng_hist
{
uint16_t frequency[256];
};
struct spng_phys
{
uint32_t ppu_x, ppu_y;
uint8_t unit_specifier;
};
struct spng_splt_entry
{
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t alpha;
uint16_t frequency;
};
struct spng_splt
{
char name[80];
uint8_t sample_depth;
uint32_t n_entries;
struct spng_splt_entry *entries;
};
struct spng_time
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
struct spng_offs
{
int32_t x, y;
uint8_t unit_specifier;
};
struct spng_exif
{
size_t length;
char *data;
};
struct spng_chunk
{
size_t offset;
uint32_t length;
uint8_t type[4];
uint32_t crc;
};
enum spng_location
{
SPNG_AFTER_IHDR = 1,
SPNG_AFTER_PLTE = 2,
SPNG_AFTER_IDAT = 8,
};
struct spng_unknown_chunk
{
uint8_t type[4];
size_t length;
void *data;
enum spng_location location;
};
enum spng_option
{
SPNG_KEEP_UNKNOWN_CHUNKS = 1,
SPNG_IMG_COMPRESSION_LEVEL,
SPNG_IMG_WINDOW_BITS,
SPNG_IMG_MEM_LEVEL,
SPNG_IMG_COMPRESSION_STRATEGY,
SPNG_TEXT_COMPRESSION_LEVEL,
SPNG_TEXT_WINDOW_BITS,
SPNG_TEXT_MEM_LEVEL,
SPNG_TEXT_COMPRESSION_STRATEGY,
SPNG_FILTER_CHOICE,
SPNG_CHUNK_COUNT_LIMIT,
SPNG_ENCODE_TO_BUFFER,
};
typedef void* SPNG_CDECL spng_malloc_fn(size_t size);
typedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size);
typedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size);
typedef void SPNG_CDECL spng_free_fn(void* ptr);
struct spng_alloc
{
spng_malloc_fn *malloc_fn;
spng_realloc_fn *realloc_fn;
spng_calloc_fn *calloc_fn;
spng_free_fn *free_fn;
};
struct spng_row_info
{
uint32_t scanline_idx;
uint32_t row_num; /* deinterlaced row index */
int pass;
uint8_t filter;
};
typedef struct spng_ctx spng_ctx;
typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length);
typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length);
typedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length);
SPNG_API spng_ctx *spng_ctx_new(int flags);
SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags);
SPNG_API void spng_ctx_free(spng_ctx *ctx);
SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size);
SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user);
SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file);
SPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error);
SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height);
SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height);
SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size);
SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size);
SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary);
SPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value);
SPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value);
SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len);
/* Decode */
SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags);
/* Progressive decode */
SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len);
SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len);
SPNG_API int spng_decode_chunks(spng_ctx *ctx);
/* Encode/decode */
SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info);
/* Encode */
SPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags);
/* Progressive encode */
SPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len);
SPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len);
SPNG_API int spng_encode_chunks(spng_ctx *ctx);
SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma);
SPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int);
SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent);
SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text);
SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt);
SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time);
SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks);
/* Official extensions */
SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif);
SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma);
SPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma);
SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent);
SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text);
SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt);
SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);
SPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks);
/* Official extensions */
SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);
SPNG_API const char *spng_strerror(int err);
SPNG_API const char *spng_version_string(void);
#ifdef __cplusplus
}
#endif
#endif /* SPNG_H */

@ -327,6 +327,9 @@ OCV_OPTION(WITH_OPENNI2 "Include OpenNI2 support" OFF
OCV_OPTION(WITH_PNG "Include PNG support" ON
VISIBLE_IF TRUE
VERIFY HAVE_PNG)
OCV_OPTION(WITH_SPNG "Include SPNG support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_SPNG)
OCV_OPTION(WITH_GDCM "Include DICOM support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_GDCM)
@ -1326,8 +1329,12 @@ if(WITH_WEBP OR HAVE_WEBP)
status(" WEBP:" WEBP_FOUND THEN "${WEBP_LIBRARY} (ver ${WEBP_VERSION})" ELSE "build (ver ${WEBP_VERSION})")
endif()
if(WITH_PNG OR HAVE_PNG)
status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION})" ELSE "build (ver ${PNG_VERSION})")
if(WITH_PNG OR HAVE_PNG OR WITH_SPNG)
if(WITH_SPNG)
status(" PNG:" "build-${SPNG_LIBRARY} (ver ${SPNG_VERSION})")
else()
status(" PNG:" PNG_FOUND THEN "${PNG_LIBRARY} (ver ${PNG_VERSION})" ELSE "build (ver ${PNG_VERSION})")
endif()
endif()
if(WITH_TIFF OR HAVE_TIFF)

@ -221,8 +221,21 @@ if(WITH_JASPER AND NOT HAVE_OPENJPEG)
endif()
endif()
if(WITH_SPNG)
set(SPNG_LIBRARY libspng CACHE INTERNAL "")
set(SPNG_LIBRARIES ${SPNG_LIBRARY})
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libspng")
set(SPNG_INCLUDE_DIR "${${SPNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "")
set(SPNG_DEFINITIONS "")
ocv_parse_header("${SPNG_INCLUDE_DIR}/spng.h" SPNG_VERSION_LINES SPNG_VERSION_MAJOR SPNG_VERSION_MINOR SPNG_VERSION_PATCH)
set(HAVE_SPNG YES)
set(SPNG_VERSION "${SPNG_VERSION_MAJOR}.${SPNG_VERSION_MINOR}.${SPNG_VERSION_PATCH}")
message(STATUS "imgcodecs: PNG codec will use SPNG, version: ${SPNG_VERSION} ")
endif()
# --- libpng (optional, should be searched after zlib) ---
if(WITH_PNG)
if(NOT HAVE_SPNG AND WITH_PNG)
if(BUILD_PNG)
ocv_clear_vars(PNG_FOUND)
else()
@ -254,6 +267,7 @@ if(WITH_PNG)
set(PNG_VERSION "${PNG_LIBPNG_VER_MAJOR}.${PNG_LIBPNG_VER_MINOR}.${PNG_LIBPNG_VER_RELEASE}")
endif()
# --- OpenEXR (optional) ---
if(WITH_OPENEXR)
ocv_clear_vars(HAVE_OPENEXR)

@ -106,6 +106,9 @@
/* PNG codec */
#cmakedefine HAVE_PNG
/* PNG codec */
#cmakedefine HAVE_SPNG
/* Posix threads (pthreads) */
#cmakedefine HAVE_PTHREAD

@ -2155,7 +2155,7 @@ static void showSaveDialog(CvWindow& window)
#endif
ofn.hwndOwner = window.hwnd;
ofn.lpstrFilter =
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
"Portable Network Graphics files (*.png)\0*.png\0"
#endif
"Windows bitmap (*.bmp;*.dib)\0*.bmp;*.dib\0"
@ -2181,7 +2181,7 @@ static void showSaveDialog(CvWindow& window)
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN | OFN_NOCHANGEDIR;
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
ofn.lpstrDefExt = "png";
#else
ofn.lpstrDefExt = "bmp";

@ -24,6 +24,12 @@ if(HAVE_WEBP)
list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES})
endif()
if(HAVE_SPNG)
add_definitions(${SPNG_DEFINITIONS})
ocv_include_directories(${SPNG_INCLUDE_DIR})
list(APPEND GRFMT_LIBS ${SPNG_LIBRARY})
endif()
if(HAVE_PNG)
add_definitions(${PNG_DEFINITIONS})
ocv_include_directories(${PNG_INCLUDE_DIR})
@ -67,7 +73,7 @@ if(HAVE_OPENEXR)
endif()
endif()
if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR)
if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR OR HAVE_SPNG)
ocv_include_directories(${ZLIB_INCLUDE_DIRS})
list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES})
endif()

@ -0,0 +1,41 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
typedef perf::TestBaseWithParam<std::string> PNG;
PERF_TEST(PNG, decode)
{
String filename = getDataPath("perf/2560x1600.png");
FILE *f = fopen(filename.c_str(), "rb");
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
vector<uchar> file_buf((size_t)len);
EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f));
fclose(f); f = NULL;
TEST_CYCLE() imdecode(file_buf, IMREAD_UNCHANGED);
SANITY_CHECK_NOTHING();
}
PERF_TEST(PNG, encode)
{
String filename = getDataPath("perf/2560x1600.png");
cv::Mat src = imread(filename);
vector<uchar> buf;
TEST_CYCLE() imencode(".png", src, buf);
SANITY_CHECK_NOTHING();
}
} // namespace

@ -0,0 +1,754 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#ifdef HAVE_SPNG
/****************************************************************************************\
This part of the file implements PNG codec on base of libspng library,
in particular, this code is based on example.c from libspng
(see 3rdparty/libspng/LICENSE for copyright notice)
\****************************************************************************************/
#ifndef _LFS64_LARGEFILE
#define _LFS64_LARGEFILE 0
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 0
#endif
#include <spng.h>
#include <zlib.h>
#include "grfmt_spng.hpp"
/*
* libspng does not support RGB -> Gray conversion. In order to decode colorful images as grayscale
* we need conversion functions. In the previous png implementation(grfmt_png), the author was set
* to particular values for rgb coefficients. OpenCV icvCvt_BGR2Gray function values does not match
* with these values. (png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 );) For this codec implementation,
* slightly modified versions are implemented in the below of this page.
*/
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb);
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb);
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
ushort *gray, int gray_step,
cv::Size size, int ncn, int _swap_rb);
namespace cv
{
/////////////////////// SPngDecoder ///////////////////
SPngDecoder::SPngDecoder()
{
m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa";
m_color_type = 0;
m_ctx = 0;
m_f = 0;
m_buf_supported = true;
m_buf_pos = 0;
m_bit_depth = 0;
}
SPngDecoder::~SPngDecoder()
{
close();
}
ImageDecoder SPngDecoder::newDecoder() const
{
return makePtr<SPngDecoder>();
}
void SPngDecoder::close()
{
if (m_f)
{
fclose(m_f);
m_f = 0;
}
if (m_ctx)
{
struct spng_ctx *ctx = (struct spng_ctx *)m_ctx;
spng_ctx_free(ctx);
m_ctx = 0;
}
}
int SPngDecoder::readDataFromBuf(void *sp_ctx, void *user, void *dst, size_t size)
{
/*
* typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length)
* Type definition for callback passed to spng_set_png_stream() for decoders.
* A read callback function should copy length bytes to dest and return 0 or SPNG_IO_EOF/SPNG_IO_ERROR on error.
*/
CV_UNUSED(sp_ctx);
SPngDecoder *decoder = (SPngDecoder *)(user);
CV_Assert(decoder);
const Mat &buf = decoder->m_buf;
if (decoder->m_buf_pos + size > buf.cols * buf.rows * buf.elemSize())
{
return SPNG_IO_ERROR;
}
memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size);
decoder->m_buf_pos += size;
return 0;
}
bool SPngDecoder::readHeader()
{
volatile bool result = false;
close();
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32);
if (!ctx)
{
spng_ctx_free(ctx);
return false;
}
m_ctx = ctx;
spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE);
if (!m_buf.empty())
spng_set_png_stream((struct spng_ctx *)m_ctx, (spng_rw_fn *)readDataFromBuf, this);
else
{
m_f = fopen(m_filename.c_str(), "rb");
if (m_f)
{
spng_set_png_file(ctx, m_f);
}
}
if (!m_buf.empty() || m_f)
{
struct spng_ihdr ihdr;
int ret = spng_get_ihdr(ctx, &ihdr);
if (ret == SPNG_OK)
{
m_width = static_cast<int>(ihdr.width);
m_height = static_cast<int>(ihdr.height);
m_color_type = ihdr.color_type;
m_bit_depth = ihdr.bit_depth;
if (ihdr.bit_depth <= 8 || ihdr.bit_depth == 16)
{
int num_trans;
switch (ihdr.color_type)
{
case SPNG_COLOR_TYPE_TRUECOLOR:
case SPNG_COLOR_TYPE_INDEXED:
struct spng_trns trns;
num_trans = !spng_get_trns(ctx, &trns);
if (num_trans > 0)
m_type = CV_8UC4;
else
m_type = CV_8UC3;
break;
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
m_type = CV_8UC4;
break;
default:
m_type = CV_8UC1;
}
if (ihdr.bit_depth == 16)
m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
result = true;
}
}
}
return result;
}
bool SPngDecoder::readData(Mat &img)
{
volatile bool result = false;
bool color = img.channels() > 1;
struct spng_ctx *png_ptr = (struct spng_ctx *)m_ctx;
if (m_ctx && m_width && m_height)
{
int fmt = SPNG_FMT_PNG;
struct spng_trns trns;
int have_trns = spng_get_trns((struct spng_ctx *)m_ctx, &trns);
int decode_flags = 0;
if (have_trns == SPNG_OK)
{
decode_flags = SPNG_DECODE_TRNS;
}
if (img.channels() == 4)
{
if (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR ||
m_color_type == SPNG_COLOR_TYPE_INDEXED ||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE)
fmt = m_bit_depth == 16 ? SPNG_FMT_GA16 : SPNG_FMT_GA8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
}
else
fmt = SPNG_FMT_RGBA8;
}
if (img.channels() == 3)
{
fmt = SPNG_FMT_RGB8;
if ((m_color_type == SPNG_COLOR_TYPE_GRAYSCALE || m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) &&
m_bit_depth == 16)
fmt = SPNG_FMT_RGB8;
else if (m_bit_depth == 16)
fmt = SPNG_FMT_PNG;
}
else if (img.channels() == 1)
{
if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth <= 8)
fmt = SPNG_FMT_G8;
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth == 16)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = SPNG_FMT_PNG;
}
}
else if (m_color_type == SPNG_COLOR_TYPE_INDEXED ||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8;
}
}
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || fmt == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
{
if (img.depth() == CV_8U || img.depth() == CV_8S)
{
fmt = SPNG_FMT_RGB8;
}
else
{
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
}
}
else
fmt = SPNG_FMT_RGB8;
}
size_t image_width, image_size = 0;
int ret = spng_decoded_image_size(png_ptr, fmt, &image_size);
struct spng_ihdr ihdr;
spng_get_ihdr(png_ptr, &ihdr);
if (ret == SPNG_OK)
{
image_width = image_size / m_height;
ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags);
if (ret == SPNG_OK)
{
struct spng_row_info row_info{};
// If user wants to read image as grayscale(IMREAD_GRAYSCALE), but image format is not
// decode image then convert to grayscale
if (!color && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA8 || fmt == SPNG_FMT_RGBA16))
{
if (ihdr.interlace_method == 0)
{
AutoBuffer<unsigned char> buffer;
buffer.allocate(image_width);
if (fmt == SPNG_FMT_RGB8)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGR2Gray_8u_C3C1R(
buffer.data(),
0,
img.data + row_info.row_num * img.step,
0, Size(m_width, 1), 2);
} while (ret == SPNG_OK);
}
else if (fmt == SPNG_FMT_RGBA8)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGRA2Gray_8u_C4C1R(
buffer.data(),
0,
img.data + row_info.row_num * img.step,
0, Size(m_width, 1), 2);
} while (ret == SPNG_OK);
}
else if (fmt == SPNG_FMT_RGBA16)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
spngCvt_BGRA2Gray_16u_CnC1R(
reinterpret_cast<const ushort *>(buffer.data()), 0,
reinterpret_cast<ushort *>(img.data + row_info.row_num * img.step),
0, Size(m_width, 1),
4, 2);
} while (ret == SPNG_OK);
}
}
else
{
AutoBuffer<unsigned char> imageBuffer(image_size);
spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0);
int step = m_width * img.channels();
if (fmt == SPNG_FMT_RGB8)
{
spngCvt_BGR2Gray_8u_C3C1R(
imageBuffer.data(),
step,
img.data,
step, Size(m_width, m_height), 2);
}
else if (fmt == SPNG_FMT_RGBA8)
{
spngCvt_BGRA2Gray_8u_C4C1R(
imageBuffer.data(),
step,
img.data,
step, Size(m_width, m_height), 2);
}
else if (fmt == SPNG_FMT_RGBA16)
{
spngCvt_BGRA2Gray_16u_CnC1R(
reinterpret_cast<const ushort *>(imageBuffer.data()), step / 3,
reinterpret_cast<ushort *>(img.data),
step / 3, Size(m_width, m_height),
4, 2);
}
}
}
else if (color)
{ // RGB -> BGR, convert row by row if png is non-interlaced, otherwise convert image as one
int step = m_width * img.channels();
AutoBuffer<uchar *> _buffer(m_height);
uchar **buffer = _buffer.data();
for (int y = 0; y < m_height; y++)
{
buffer[y] = img.data + y * img.step;
}
if (img.channels() == 4 && m_bit_depth == 16)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0,
Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(img.data), step * 2, reinterpret_cast<ushort *>(img.data), step * 2, Size(m_width, m_height));
}
}
else if (img.channels() == 4)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGBA2BGRA_8u_C4R(img.data, step, img.data, step, Size(m_width, m_height));
}
}
else if (fmt == SPNG_FMT_PNG)
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step,
reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height));
}
}
else
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
if (ihdr.interlace_method == 0)
{
icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
}
} while (ret == SPNG_OK);
if (ihdr.interlace_method)
{
icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height));
}
}
}
else
{
do
{
ret = spng_get_row_info(png_ptr, &row_info);
if (ret)
break;
ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width);
} while (ret == SPNG_OK);
}
}
if (ret == SPNG_EOI)
{
struct spng_exif exif_s{};
ret = spng_get_exif(png_ptr, &exif_s);
if (ret == SPNG_OK)
{
if (exif_s.data && exif_s.length > 0)
{
m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length);
}
}
result = true;
}
}
}
return result;
}
/////////////////////// SPngEncoder ///////////////////
SPngEncoder::SPngEncoder()
{
m_description = "Portable Network Graphics files (*.png)";
m_buf_supported = true;
}
SPngEncoder::~SPngEncoder()
{
}
bool SPngEncoder::isFormatSupported(int depth) const
{
return depth == CV_8U || depth == CV_16U;
}
ImageEncoder SPngEncoder::newEncoder() const
{
return makePtr<SPngEncoder>();
}
int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length)
{
CV_UNUSED(ctx);
SPngEncoder *encoder = (SPngEncoder *)(user);
CV_Assert(encoder && encoder->m_buf);
size_t cursz = encoder->m_buf->size();
encoder->m_buf->resize(cursz + length);
memcpy(&(*encoder->m_buf)[cursz], dst_src, length);
return 0;
}
bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
{
int fmt;
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER);
FILE *volatile f = 0;
int width = img.cols, height = img.rows;
int depth = img.depth(), channels = img.channels();
volatile bool result = false;
if (depth != CV_8U && depth != CV_16U)
return false;
if (ctx)
{
struct spng_ihdr ihdr = {};
ihdr.height = height;
ihdr.width = width;
int compression_level = -1; // Invalid value to allow setting 0-9 as valid
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
bool isBilevel = false;
for (size_t i = 0; i < params.size(); i += 2)
{
if (params[i] == IMWRITE_PNG_COMPRESSION)
{
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
compression_level = params[i + 1];
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
}
if (params[i] == IMWRITE_PNG_STRATEGY)
{
compression_strategy = params[i + 1];
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
}
if (params[i] == IMWRITE_PNG_BILEVEL)
{
isBilevel = params[i + 1] != 0;
}
}
fmt = channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR
: SPNG_COLOR_TYPE_TRUECOLOR_ALPHA;
ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16;
ihdr.color_type = fmt;
ihdr.interlace_method = SPNG_INTERLACE_NONE;
ihdr.filter_method = SPNG_FILTER_NONE;
ihdr.compression_method = 0;
spng_set_ihdr(ctx, &ihdr);
if (m_buf)
{
spng_set_png_stream(ctx, (spng_rw_fn *)writeDataToBuf, this);
}
else
{
f = fopen(m_filename.c_str(), "wb");
if (f)
spng_set_png_file(ctx, f);
}
if (m_buf || f)
{
if (compression_level >= 0)
{
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level);
}
else
{
spng_set_option(ctx, SPNG_FILTER_CHOICE, SPNG_FILTER_CHOICE_SUB);
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, Z_BEST_SPEED);
}
spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy);
int ret;
spng_encode_chunks(ctx);
ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE);
if (channels > 1)
{
int error;
if (ret == SPNG_OK)
{
if (depth == CV_16U)
{
AutoBuffer<ushort *> buff2;
buff2.allocate(height);
for (int y = 0; y < height; y++)
buff2[y] = reinterpret_cast<unsigned short *>(img.data + y * img.step);
AutoBuffer<ushort> _buffer;
_buffer.allocate(width * channels * 2);
for (int y = 0; y < height; y++)
{
if (channels == 3)
{
icvCvt_BGR2RGB_16u_C3R(buff2[y], 0,
_buffer.data(), 0, Size(width, 1));
}
else if (channels == 4)
{
icvCvt_BGRA2RGBA_16u_C4R(buff2[y], 0,
_buffer.data(), 0, Size(width, 1));
}
error = spng_encode_row(ctx, _buffer.data(), width * channels * 2);
if (error)
break;
}
}
else
{
AutoBuffer<uchar *> buff;
buff.allocate(height);
for (int y = 0; y < height; y++)
buff[y] = img.data + y * img.step;
AutoBuffer<uchar> _buffer;
_buffer.allocate(width * channels);
for (int y = 0; y < height; y++)
{
if (channels == 3)
{
icvCvt_BGR2RGB_8u_C3R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
}
else if (channels == 4)
{
icvCvt_BGRA2RGBA_8u_C4R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
}
error = spng_encode_row(ctx, _buffer.data(), width * channels);
if (error)
break;
}
}
if (error == SPNG_EOI)
{ // success
spng_encode_chunks(ctx);
ret = SPNG_OK;
}
}
}
else
{
int error;
for (int y = 0; y < height; y++)
{
error = spng_encode_row(ctx, img.data + y * img.step, width * channels * (depth == CV_16U ? 2 : 1));
if (error)
break;
}
if (error == SPNG_EOI)
{ // success
spng_encode_chunks(ctx);
ret = SPNG_OK;
}
}
if (ret == SPNG_OK)
result = true;
}
}
spng_ctx_free(ctx);
if (f)
fclose((FILE *)f);
return result;
}
}
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgr += 3)
{
int t = static_cast<int>(cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]);
gray[i] = (uchar)t;
}
bgr += bgr_step - size.width * 3;
}
}
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
uchar *gray, int gray_step,
cv::Size size, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgra += 4)
{
int t = cBGR0 * bgra[0] + 0.5869750977 * bgra[1] + cBGR2 * bgra[2];
gray[i] = (uchar)t;
}
bgra += rgba_step - size.width * 4;
}
}
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
ushort *gray, int gray_step,
cv::Size size, int ncn, int _swap_rb)
{
int i;
for (; size.height--; gray += gray_step)
{
double cBGR0 = 0.1140441895;
double cBGR2 = 0.2989807129;
if (_swap_rb)
std::swap(cBGR0, cBGR2);
for (i = 0; i < size.width; i++, bgr += ncn)
{
int t = cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2];
gray[i] = (ushort)t;
}
bgr += bgr_step - size.width * ncn;
}
}
#endif
/* End of file. */

@ -0,0 +1,60 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef _GRFMT_SPNG_H_
#define _GRFMT_SPNG_H_
#ifdef HAVE_SPNG
#include "grfmt_base.hpp"
#include "bitstrm.hpp"
namespace cv
{
class SPngDecoder CV_FINAL : public BaseImageDecoder
{
public:
SPngDecoder();
virtual ~SPngDecoder();
bool readData( Mat& img ) CV_OVERRIDE;
bool readHeader() CV_OVERRIDE;
void close();
ImageDecoder newDecoder() const CV_OVERRIDE;
protected:
static int readDataFromBuf(void* sp_ctx, void *user, void* dst, size_t size);
int m_bit_depth;
void* m_ctx;
FILE* m_f;
int m_color_type;
size_t m_buf_pos;
};
class SPngEncoder CV_FINAL : public BaseImageEncoder
{
public:
SPngEncoder();
virtual ~SPngEncoder();
bool isFormatSupported( int depth ) const CV_OVERRIDE;
bool write( const Mat& img, const std::vector<int>& params ) CV_OVERRIDE;
ImageEncoder newEncoder() const CV_OVERRIDE;
protected:
static int writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length);
};
}
#endif
#endif/*_GRFMT_PNG_H_*/

@ -49,6 +49,7 @@
#include "grfmt_pxm.hpp"
#include "grfmt_pfm.hpp"
#include "grfmt_tiff.hpp"
#include "grfmt_spng.hpp"
#include "grfmt_png.hpp"
#include "grfmt_jpeg2000.hpp"
#include "grfmt_jpeg2000_openjpeg.hpp"

@ -167,7 +167,10 @@ struct ImageCodecInitializer
decoders.push_back( makePtr<TiffDecoder>() );
encoders.push_back( makePtr<TiffEncoder>() );
#endif
#ifdef HAVE_PNG
#ifdef HAVE_SPNG
decoders.push_back( makePtr<SPngDecoder>() );
encoders.push_back( makePtr<SPngEncoder>() );
#elif defined(HAVE_PNG)
decoders.push_back( makePtr<PngDecoder>() );
encoders.push_back( makePtr<PngEncoder>() );
#endif

@ -211,7 +211,7 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq)
const string all_exts[] =
{
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
".png",
#endif
#ifdef HAVE_TIFF

@ -5,7 +5,7 @@
namespace opencv_test { namespace {
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
TEST(Imgcodecs_Png, write_big)
{
@ -186,6 +186,225 @@ const string exif_files[] =
INSTANTIATE_TEST_CASE_P(ExifFiles, Imgcodecs_PNG_Exif,
testing::ValuesIn(exif_files));
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite;
TEST_P(Imgcodecs_Png_PngSuite, decode)
{
const string root = cvtest::TS::ptr()->get_data_path();
const string filename = root + "pngsuite/" + GetParam() + ".png";
const string xml_filename = root + "pngsuite/" + GetParam() + ".xml";
FileStorage fs(xml_filename, FileStorage::READ);
EXPECT_TRUE(fs.isOpened());
Mat src = imread(filename, IMREAD_UNCHANGED);
Mat gt;
fs.getFirstTopLevelNode() >> gt;
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), src, gt);
}
const string pngsuite_files[] =
{
"basi0g01",
"basi0g02",
"basi0g04",
"basi0g08",
"basi0g16",
"basi2c08",
"basi2c16",
"basi3p01",
"basi3p02",
"basi3p04",
"basi3p08",
"basi4a08",
"basi4a16",
"basi6a08",
"basi6a16",
"basn0g01",
"basn0g02",
"basn0g04",
"basn0g08",
"basn0g16",
"basn2c08",
"basn2c16",
"basn3p01",
"basn3p02",
"basn3p04",
"basn3p08",
"basn4a08",
"basn4a16",
"basn6a08",
"basn6a16",
"bgai4a08",
"bgai4a16",
"bgan6a08",
"bgan6a16",
"bgbn4a08",
"bggn4a16",
"bgwn6a08",
"bgyn6a16",
"ccwn2c08",
"ccwn3p08",
"cdfn2c08",
"cdhn2c08",
"cdsn2c08",
"cdun2c08",
"ch1n3p04",
"ch2n3p08",
"cm0n0g04",
"cm7n0g04",
"cm9n0g04",
"cs3n2c16",
"cs3n3p08",
"cs5n2c08",
"cs5n3p08",
"cs8n2c08",
"cs8n3p08",
"ct0n0g04",
"ct1n0g04",
"cten0g04",
"ctfn0g04",
"ctgn0g04",
"cthn0g04",
"ctjn0g04",
"ctzn0g04",
"exif2c08",
"f00n0g08",
"f00n2c08",
"f01n0g08",
"f01n2c08",
"f02n0g08",
"f02n2c08",
"f03n0g08",
"f03n2c08",
"f04n0g08",
"f04n2c08",
"f99n0g04",
"g03n0g16",
"g03n2c08",
"g03n3p04",
"g04n0g16",
"g04n2c08",
"g04n3p04",
"g05n0g16",
"g05n2c08",
"g05n3p04",
"g07n0g16",
"g07n2c08",
"g07n3p04",
"g10n0g16",
"g10n2c08",
"g10n3p04",
"g25n0g16",
"g25n2c08",
"g25n3p04",
"oi1n0g16",
"oi1n2c16",
"oi2n0g16",
"oi2n2c16",
"oi4n0g16",
"oi4n2c16",
"oi9n0g16",
"oi9n2c16",
"pp0n2c16",
"pp0n6a08",
"ps1n0g08",
"ps1n2c16",
"ps2n0g08",
"ps2n2c16",
"s01i3p01",
"s01n3p01",
"s02i3p01",
"s02n3p01",
"s03i3p01",
"s03n3p01",
"s04i3p01",
"s04n3p01",
"s05i3p02",
"s05n3p02",
"s06i3p02",
"s06n3p02",
"s07i3p02",
"s07n3p02",
"s08i3p02",
"s08n3p02",
"s09i3p02",
"s09n3p02",
"s32i3p04",
"s32n3p04",
"s33i3p04",
"s33n3p04",
"s34i3p04",
"s34n3p04",
"s35i3p04",
"s35n3p04",
"s36i3p04",
"s36n3p04",
"s37i3p04",
"s37n3p04",
"s38i3p04",
"s38n3p04",
"s39i3p04",
"s39n3p04",
"s40i3p04",
"s40n3p04",
"tbbn0g04",
"tbbn2c16",
"tbbn3p08",
"tbgn2c16",
"tbgn3p08",
"tbrn2c08",
"tbwn0g16",
"tbwn3p08",
"tbyn3p08",
"tm3n3p02",
"tp0n0g08",
"tp0n2c08",
"tp0n3p08",
"tp1n3p08",
"z00n2c08",
"z03n2c08",
"z06n2c08",
"z09n2c08",
};
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite,
testing::ValuesIn(pngsuite_files));
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite_Corrupted;
TEST_P(Imgcodecs_Png_PngSuite_Corrupted, decode)
{
const string root = cvtest::TS::ptr()->get_data_path();
const string filename = root + "pngsuite/" + GetParam() + ".png";
Mat src = imread(filename, IMREAD_UNCHANGED);
// Corrupted files should not be read
EXPECT_TRUE(src.empty());
}
const string pngsuite_files_corrupted[] = {
"xc1n0g08",
"xc9n2c08",
"xcrn0g04",
"xcsn0g01",
"xd0n2c08",
"xd3n2c08",
"xd9n2c08",
"xdtn0g01",
"xhdn0g08",
"xlfn0g04",
"xs1n0g01",
"xs2n0g01",
"xs4n0g01",
"xs7n0g01",
};
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted,
testing::ValuesIn(pngsuite_files_corrupted));
#endif // HAVE_PNG
}} // namespace

@ -28,7 +28,7 @@ const tuple<string, Size> images[] =
#ifdef HAVE_JPEG
make_tuple<string, Size>("../cv/imgproc/stuff.jpg", Size(640, 480)),
#endif
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
make_tuple<string, Size>("../cv/shared/pic1.png", Size(400, 300)),
#endif
make_tuple<string, Size>("../highgui/readwrite/ordinary.bmp", Size(480, 272)),
@ -148,7 +148,7 @@ typedef string Ext;
typedef testing::TestWithParam<Ext> Imgcodecs_Image;
const string exts[] = {
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
"png",
#endif
#ifdef HAVE_TIFF

@ -71,7 +71,7 @@ void CV_DrawingTest::run( int )
{
//imwrite( filename, testImg );
ts->printf( ts->LOG, "test image can not be read");
#ifdef HAVE_PNG
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
#else
ts->printf( ts->LOG, "PNG image support is not available");

Loading…
Cancel
Save