Image: Use OpenImageIO for loading and saving a variety of image formats

This checkin will use OIIO to replace the image save/load code for BMP,
DDS, DPX, HDR, PNG, TGA, and TIFF.

This simplifies our build environment, reduces binary duplication,
removes large amounts of hard to maintain code, and fixes some bugs
along the way.

It should also help reduce rare differences between Blender and Cycles
which already uses OIIO for most situations. Or potentially makes them
easier to solve once discovered.

This is a continuation of the work for #101413

Pull Request: https://projects.blender.org/blender/blender/pulls/105785
This commit is contained in:
Jesse Yurkovich 2023-04-12 05:22:26 +02:00 committed by Jesse Yurkovich
parent c6d6869171
commit aa3bdfd76a
42 changed files with 793 additions and 3603 deletions

View File

@ -333,10 +333,7 @@ option(WITH_MOD_OCEANSIM "Enable Ocean Modifier" ON)
# Image format support
option(WITH_IMAGE_OPENEXR "Enable OpenEXR Support (http://www.openexr.com)" ON)
option(WITH_IMAGE_OPENJPEG "Enable OpenJpeg Support (http://www.openjpeg.org)" ON)
option(WITH_IMAGE_TIFF "Enable LibTIFF Support" ON)
option(WITH_IMAGE_DDS "Enable DDS Image Support" ON)
option(WITH_IMAGE_CINEON "Enable CINEON and DPX Image Support" ON)
option(WITH_IMAGE_HDR "Enable HDR Image Support" ON)
option(WITH_IMAGE_WEBP "Enable WebP Image Support" ON)
# Audio/Video format support
@ -896,9 +893,6 @@ set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_ALEMBIC OFF)
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_VULKAN_BACKEND OFF)
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_CYCLES_OSL OFF)
# Haru needs `TIFFFaxBlackCodes` & `TIFFFaxWhiteCodes` symbols from TIFF.
set_and_warn_dependency(WITH_IMAGE_TIFF WITH_HARU OFF)
# auto enable openimageio for cycles
if(WITH_CYCLES)
# auto enable llvm for cycles_osl
@ -1941,11 +1935,8 @@ if(FIRST_RUN)
info_cfg_text("Image Formats:")
info_cfg_option(WITH_IMAGE_CINEON)
info_cfg_option(WITH_IMAGE_DDS)
info_cfg_option(WITH_IMAGE_HDR)
info_cfg_option(WITH_IMAGE_OPENEXR)
info_cfg_option(WITH_IMAGE_OPENJPEG)
info_cfg_option(WITH_IMAGE_TIFF)
info_cfg_text("Audio:")
info_cfg_option(WITH_CODEC_AVI)

View File

@ -26,11 +26,8 @@ set(WITH_HARU ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_DDS ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_HDR ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENEXR ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENJPEG ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_TIFF ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_WEBP ON CACHE BOOL "" FORCE)
set(WITH_INPUT_NDOF ON CACHE BOOL "" FORCE)
set(WITH_INPUT_IME ON CACHE BOOL "" FORCE)

View File

@ -27,11 +27,8 @@ set(WITH_HARU OFF CACHE BOOL "" FORCE)
set(WITH_IK_ITASC OFF CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_DDS OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_HDR OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENEXR OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENJPEG OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_TIFF OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_WEBP OFF CACHE BOOL "" FORCE)
set(WITH_INPUT_IME OFF CACHE BOOL "" FORCE)
set(WITH_INPUT_NDOF OFF CACHE BOOL "" FORCE)

View File

@ -27,11 +27,8 @@ set(WITH_HARU ON CACHE BOOL "" FORCE)
set(WITH_IK_ITASC ON CACHE BOOL "" FORCE)
set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_DDS ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_HDR ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENEXR ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_OPENJPEG ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_TIFF ON CACHE BOOL "" FORCE)
set(WITH_IMAGE_WEBP ON CACHE BOOL "" FORCE)
set(WITH_INPUT_NDOF ON CACHE BOOL "" FORCE)
set(WITH_INPUT_IME ON CACHE BOOL "" FORCE)

View File

@ -221,10 +221,8 @@ find_package(PNG REQUIRED)
set(JPEG_ROOT ${LIBDIR}/jpeg)
find_package(JPEG REQUIRED)
if(WITH_IMAGE_TIFF)
set(TIFF_ROOT ${LIBDIR}/tiff)
find_package(TIFF REQUIRED)
endif()
set(TIFF_ROOT ${LIBDIR}/tiff)
find_package(TIFF REQUIRED)
if(WITH_IMAGE_WEBP)
set(WEBP_ROOT_DIR ${LIBDIR}/webp)

View File

@ -109,6 +109,10 @@ find_package_wrapper(ZLIB REQUIRED)
find_package_wrapper(Zstd REQUIRED)
find_package_wrapper(Epoxy REQUIRED)
# XXX Linking errors with debian static tiff :/
# find_package_wrapper(TIFF REQUIRED)
find_package(TIFF)
if(WITH_VULKAN_BACKEND)
find_package_wrapper(Vulkan REQUIRED)
find_package_wrapper(ShaderC REQUIRED)
@ -190,13 +194,6 @@ if(WITH_IMAGE_OPENJPEG)
set_and_warn_library_found("OpenJPEG" OPENJPEG_FOUND WITH_IMAGE_OPENJPEG)
endif()
if(WITH_IMAGE_TIFF)
# XXX Linking errors with debian static tiff :/
# find_package_wrapper(TIFF)
find_package(TIFF)
set_and_warn_library_found("TIFF" TIFF_FOUND WITH_IMAGE_TIFF)
endif()
if(WITH_OPENAL)
find_package_wrapper(OpenAL)
set_and_warn_library_found("OpenAL" OPENAL_FOUND WITH_OPENAL)

View File

@ -487,14 +487,12 @@ if(WITH_IMAGE_OPENEXR)
endif()
endif()
if(WITH_IMAGE_TIFF)
# Try to find tiff first then complain and set static and maybe wrong paths
windows_find_package(TIFF)
if(NOT TIFF_FOUND)
warn_hardcoded_paths(libtiff)
set(TIFF_LIBRARY ${LIBDIR}/tiff/lib/libtiff.lib)
set(TIFF_INCLUDE_DIR ${LIBDIR}/tiff/include)
endif()
# Try to find tiff first then complain and set static and maybe wrong paths
windows_find_package(TIFF)
if(NOT TIFF_FOUND)
warn_hardcoded_paths(libtiff)
set(TIFF_LIBRARY ${LIBDIR}/tiff/lib/libtiff.lib)
set(TIFF_INCLUDE_DIR ${LIBDIR}/tiff/include)
endif()
if(WITH_JACK)

View File

@ -165,10 +165,6 @@ if(WITH_IMAGE_OPENEXR)
add_subdirectory(imbuf/intern/openexr)
endif()
if(WITH_IMAGE_DDS)
add_subdirectory(imbuf/intern/dds)
endif()
if(WITH_IMAGE_CINEON)
add_subdirectory(imbuf/intern/cineon)
endif()

View File

@ -594,26 +594,14 @@ if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
if(WITH_IMAGE_TIFF)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_IMAGE_OPENJPEG)
add_definitions(-DWITH_OPENJPEG)
endif()
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
if(WITH_IMAGE_WEBP)
add_definitions(-DWITH_WEBP)
endif()

View File

@ -80,28 +80,22 @@ int BKE_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options)
if (imtype == R_IMF_IMTYPE_IRIS) {
return IMB_FTYPE_IMAGIC;
}
#ifdef WITH_HDR
if (imtype == R_IMF_IMTYPE_RADHDR) {
return IMB_FTYPE_RADHDR;
}
#endif
if (imtype == R_IMF_IMTYPE_PNG) {
r_options->quality = 15;
return IMB_FTYPE_PNG;
}
#ifdef WITH_DDS
if (imtype == R_IMF_IMTYPE_DDS) {
return IMB_FTYPE_DDS;
}
#endif
if (imtype == R_IMF_IMTYPE_BMP) {
return IMB_FTYPE_BMP;
}
#ifdef WITH_TIFF
if (imtype == R_IMF_IMTYPE_TIFF) {
return IMB_FTYPE_TIF;
}
#endif
if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
return IMB_FTYPE_OPENEXR;
}
@ -139,27 +133,21 @@ char BKE_ftype_to_imtype(const int ftype, const ImbFormatOptions *options)
if (ftype == IMB_FTYPE_IMAGIC) {
return R_IMF_IMTYPE_IRIS;
}
#ifdef WITH_HDR
if (ftype == IMB_FTYPE_RADHDR) {
return R_IMF_IMTYPE_RADHDR;
}
#endif
if (ftype == IMB_FTYPE_PNG) {
return R_IMF_IMTYPE_PNG;
}
#ifdef WITH_DDS
if (ftype == IMB_FTYPE_DDS) {
return R_IMF_IMTYPE_DDS;
}
#endif
if (ftype == IMB_FTYPE_BMP) {
return R_IMF_IMTYPE_BMP;
}
#ifdef WITH_TIFF
if (ftype == IMB_FTYPE_TIF) {
return R_IMF_IMTYPE_TIFF;
}
#endif
if (ftype == IMB_FTYPE_OPENEXR) {
return R_IMF_IMTYPE_OPENEXR;
}
@ -327,11 +315,9 @@ char BKE_imtype_from_arg(const char *imtype_arg)
if (STREQ(imtype_arg, "IRIS")) {
return R_IMF_IMTYPE_IRIS;
}
#ifdef WITH_DDS
if (STREQ(imtype_arg, "DDS")) {
return R_IMF_IMTYPE_DDS;
}
#endif
if (STREQ(imtype_arg, "JPEG")) {
return R_IMF_IMTYPE_JPEG90;
}
@ -353,16 +339,12 @@ char BKE_imtype_from_arg(const char *imtype_arg)
if (STREQ(imtype_arg, "BMP")) {
return R_IMF_IMTYPE_BMP;
}
#ifdef WITH_HDR
if (STREQ(imtype_arg, "HDR")) {
return R_IMF_IMTYPE_RADHDR;
}
#endif
#ifdef WITH_TIFF
if (STREQ(imtype_arg, "TIFF")) {
return R_IMF_IMTYPE_TIFF;
}
#endif
#ifdef WITH_OPENEXR
if (STREQ(imtype_arg, "OPEN_EXR")) {
return R_IMF_IMTYPE_OPENEXR;
@ -422,13 +404,11 @@ static bool do_add_image_extension(char *string,
extension = extension_test;
}
}
#ifdef WITH_HDR
else if (imtype == R_IMF_IMTYPE_RADHDR) {
if (!BLI_path_extension_check(string, extension_test = ".hdr")) {
extension = extension_test;
}
}
#endif
else if (ELEM(imtype,
R_IMF_IMTYPE_PNG,
R_IMF_IMTYPE_FFMPEG,
@ -440,13 +420,11 @@ static bool do_add_image_extension(char *string,
extension = extension_test;
}
}
#ifdef WITH_DDS
else if (imtype == R_IMF_IMTYPE_DDS) {
if (!BLI_path_extension_check(string, extension_test = ".dds")) {
extension = extension_test;
}
}
#endif
else if (ELEM(imtype, R_IMF_IMTYPE_TARGA, R_IMF_IMTYPE_RAWTGA)) {
if (!BLI_path_extension_check(string, extension_test = ".tga")) {
extension = extension_test;
@ -457,13 +435,11 @@ static bool do_add_image_extension(char *string,
extension = extension_test;
}
}
#ifdef WITH_TIFF
else if (imtype == R_IMF_IMTYPE_TIFF) {
if (!BLI_path_extension_check_n(string, extension_test = ".tif", ".tiff", nullptr)) {
extension = extension_test;
}
}
#endif
else if (imtype == R_IMF_IMTYPE_PSD) {
if (!BLI_path_extension_check(string, extension_test = ".psd")) {
extension = extension_test;
@ -617,11 +593,9 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
if (imtype == R_IMF_IMTYPE_IRIS) {
ibuf->ftype = IMB_FTYPE_IMAGIC;
}
#ifdef WITH_HDR
else if (imtype == R_IMF_IMTYPE_RADHDR) {
ibuf->ftype = IMB_FTYPE_RADHDR;
}
#endif
else if (ELEM(imtype,
R_IMF_IMTYPE_PNG,
R_IMF_IMTYPE_FFMPEG,
@ -639,15 +613,12 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
ibuf->foptions.quality = compress;
}
}
#ifdef WITH_DDS
else if (imtype == R_IMF_IMTYPE_DDS) {
ibuf->ftype = IMB_FTYPE_DDS;
}
#endif
else if (imtype == R_IMF_IMTYPE_BMP) {
ibuf->ftype = IMB_FTYPE_BMP;
}
#ifdef WITH_TIFF
else if (imtype == R_IMF_IMTYPE_TIFF) {
ibuf->ftype = IMB_FTYPE_TIF;
@ -667,7 +638,6 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
ibuf->foptions.flag |= TIF_COMPRESS_PACKBITS;
}
}
#endif
#ifdef WITH_OPENEXR
else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
ibuf->ftype = IMB_FTYPE_OPENEXR;
@ -787,11 +757,9 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
if (ftype == IMB_FTYPE_IMAGIC) {
im_format->imtype = R_IMF_IMTYPE_IRIS;
}
#ifdef WITH_HDR
else if (ftype == IMB_FTYPE_RADHDR) {
im_format->imtype = R_IMF_IMTYPE_RADHDR;
}
#endif
else if (ftype == IMB_FTYPE_PNG) {
im_format->imtype = R_IMF_IMTYPE_PNG;
@ -801,16 +769,12 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
im_format->compress = quality;
}
#ifdef WITH_DDS
else if (ftype == IMB_FTYPE_DDS) {
im_format->imtype = R_IMF_IMTYPE_DDS;
}
#endif
else if (ftype == IMB_FTYPE_BMP) {
im_format->imtype = R_IMF_IMTYPE_BMP;
}
#ifdef WITH_TIFF
else if (ftype == IMB_FTYPE_TIF) {
im_format->imtype = R_IMF_IMTYPE_TIFF;
if (custom_flags & TIF_16BIT) {
@ -829,7 +793,6 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
im_format->tiff_codec = R_IMF_TIFF_CODEC_PACKBITS;
}
}
#endif
#ifdef WITH_OPENEXR
else if (ftype == IMB_FTYPE_OPENEXR) {

View File

@ -66,26 +66,14 @@ if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
if(WITH_IMAGE_TIFF)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_IMAGE_OPENJPEG)
add_definitions(-DWITH_OPENJPEG)
endif()
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
if(WITH_IMAGE_WEBP)
add_definitions(-DWITH_WEBP)
endif()

View File

@ -54,10 +54,6 @@ if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
if(WITH_IMAGE_TIFF)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()

View File

@ -738,10 +738,6 @@ if(WITH_MOD_FLUID)
add_definitions(-DWITH_FLUID)
endif()
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
if(WITH_OPENCOLORIO)
add_definitions(-DWITH_OCIO)
endif()

View File

@ -16,7 +16,6 @@ set(INC
set(INC_SYS
${JPEG_INCLUDE_DIR}
${PNG_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${OPENIMAGEIO_INCLUDE_DIRS}
)
@ -24,13 +23,19 @@ set(INC_SYS
set(SRC
intern/allocimbuf.c
intern/anim_movie.c
intern/bmp.c
intern/colormanagement.c
intern/colormanagement_inline.c
intern/divers.c
intern/filetype.c
intern/filter.c
intern/format_bmp.cc
intern/format_dds.cc
intern/format_dpx.cc
intern/format_hdr.cc
intern/format_png.cc
intern/format_psd.cc
intern/format_targa.cc
intern/format_tiff.cc
intern/imageprocess.c
intern/indexer.c
intern/iris.c
@ -38,13 +43,11 @@ set(SRC
intern/metadata.c
intern/module.c
intern/moviecache.cc
intern/png.c
intern/readimage.c
intern/rectop.c
intern/rotate.c
intern/scaling.c
intern/stereoimbuf.c
intern/targa.c
intern/thumbs.c
intern/thumbs_blend.c
intern/thumbs_font.c
@ -81,7 +84,6 @@ set(LIB
bf_intern_memutil
bf_intern_opencolorio
${PNG_LIBRARIES}
${JPEG_LIBRARIES}
)
@ -96,19 +98,6 @@ else()
)
endif()
if(WITH_IMAGE_TIFF)
list(APPEND INC_SYS
${TIFF_INCLUDE_DIR}
)
list(APPEND SRC
intern/tiff.c
)
list(APPEND LIB
${TIFF_LIBRARY}
)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_IMAGE_OPENJPEG)
list(APPEND INC_SYS
${OPENJPEG_INCLUDE_DIRS}
@ -147,13 +136,6 @@ if(WITH_CODEC_FFMPEG)
add_definitions(-DWITH_FFMPEG)
endif()
if(WITH_IMAGE_DDS)
list(APPEND LIB
bf_imbuf_dds
)
add_definitions(-DWITH_DDS)
endif()
if(WITH_IMAGE_CINEON)
list(APPEND LIB
bf_imbuf_cineon
@ -161,13 +143,6 @@ if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
if(WITH_IMAGE_HDR)
list(APPEND SRC
intern/radiance_hdr.c
)
add_definitions(-DWITH_HDR)
endif()
if(WITH_IMAGE_WEBP)
list(APPEND SRC
intern/webp.c

View File

@ -63,20 +63,14 @@ enum eImbFileType {
#ifdef WITH_OPENJPEG
IMB_FTYPE_JP2 = 8,
#endif
#ifdef WITH_HDR
IMB_FTYPE_RADHDR = 9,
#endif
#ifdef WITH_TIFF
IMB_FTYPE_TIF = 10,
#endif
#ifdef WITH_CINEON
IMB_FTYPE_CINEON = 11,
IMB_FTYPE_DPX = 12,
#endif
#ifdef WITH_DDS
IMB_FTYPE_DDS = 13,
#endif
#ifdef WITH_WEBP
IMB_FTYPE_WEBP = 14,
#endif
@ -113,13 +107,11 @@ enum eImbFileType {
#define RAWTGA 1
#ifdef WITH_TIFF
# define TIF_16BIT (1 << 8)
# define TIF_COMPRESS_NONE (1 << 7)
# define TIF_COMPRESS_DEFLATE (1 << 6)
# define TIF_COMPRESS_LZW (1 << 5)
# define TIF_COMPRESS_PACKBITS (1 << 4)
#endif
#define TIF_16BIT (1 << 8)
#define TIF_COMPRESS_NONE (1 << 7)
#define TIF_COMPRESS_DEFLATE (1 << 6)
#define TIF_COMPRESS_LZW (1 << 5)
#define TIF_COMPRESS_PACKBITS (1 << 4)
typedef struct ImbFormatOptions {
short flag;
@ -295,25 +287,24 @@ enum {
/** \} */
/* dds */
#ifdef WITH_DDS
# ifndef DDS_MAKEFOURCC
# define DDS_MAKEFOURCC(ch0, ch1, ch2, ch3) \
((unsigned long)(unsigned char)(ch0) | ((unsigned long)(unsigned char)(ch1) << 8) | \
((unsigned long)(unsigned char)(ch2) << 16) | ((unsigned long)(unsigned char)(ch3) << 24))
# endif /* DDS_MAKEFOURCC */
#ifndef DDS_MAKEFOURCC
# define DDS_MAKEFOURCC(ch0, ch1, ch2, ch3) \
((unsigned long)(unsigned char)(ch0) | ((unsigned long)(unsigned char)(ch1) << 8) | \
((unsigned long)(unsigned char)(ch2) << 16) | ((unsigned long)(unsigned char)(ch3) << 24))
#endif /* DDS_MAKEFOURCC */
/*
* FOURCC codes for DX compressed-texture pixel formats.
*/
# define FOURCC_DDS (DDS_MAKEFOURCC('D', 'D', 'S', ' '))
# define FOURCC_DXT1 (DDS_MAKEFOURCC('D', 'X', 'T', '1'))
# define FOURCC_DXT2 (DDS_MAKEFOURCC('D', 'X', 'T', '2'))
# define FOURCC_DXT3 (DDS_MAKEFOURCC('D', 'X', 'T', '3'))
# define FOURCC_DXT4 (DDS_MAKEFOURCC('D', 'X', 'T', '4'))
# define FOURCC_DXT5 (DDS_MAKEFOURCC('D', 'X', 'T', '5'))
#define FOURCC_DDS (DDS_MAKEFOURCC('D', 'D', 'S', ' '))
#define FOURCC_DX10 (DDS_MAKEFOURCC('D', 'X', '1', '0'))
#define FOURCC_DXT1 (DDS_MAKEFOURCC('D', 'X', 'T', '1'))
#define FOURCC_DXT2 (DDS_MAKEFOURCC('D', 'X', 'T', '2'))
#define FOURCC_DXT3 (DDS_MAKEFOURCC('D', 'X', 'T', '3'))
#define FOURCC_DXT4 (DDS_MAKEFOURCC('D', 'X', 'T', '4'))
#define FOURCC_DXT5 (DDS_MAKEFOURCC('D', 'X', 'T', '5'))
#endif /* DDS */
extern const char *imb_ext_image[];
extern const char *imb_ext_movie[];
extern const char *imb_ext_audio[];

View File

@ -80,11 +80,11 @@ void imb_filetypes_exit(void);
* \{ */
bool imb_is_a_png(const unsigned char *mem, size_t size);
struct ImBuf *imb_loadpng(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags);
struct ImBuf *imb_load_png(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_save_png(struct ImBuf *ibuf, const char *filepath, int flags);
/** \} */
@ -92,12 +92,12 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags);
/** \name Format: TARGA (#IMB_FTYPE_TGA)
* \{ */
bool imb_is_a_targa(const unsigned char *buf, size_t size);
struct ImBuf *imb_loadtarga(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_savetarga(struct ImBuf *ibuf, const char *filepath, int flags);
bool imb_is_a_tga(const unsigned char *mem, size_t size);
struct ImBuf *imb_load_tga(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_save_tga(struct ImBuf *ibuf, const char *filepath, int flags);
/** \} */
@ -157,12 +157,12 @@ struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
* \{ */
bool imb_is_a_bmp(const unsigned char *buf, size_t size);
struct ImBuf *imb_bmp_decode(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
struct ImBuf *imb_load_bmp(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
/* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */
bool imb_savebmp(struct ImBuf *ibuf, const char *filepath, int flags);
bool imb_save_bmp(struct ImBuf *ibuf, const char *filepath, int flags);
/** \} */
@ -197,11 +197,11 @@ struct ImBuf *imb_load_dpx(const unsigned char *mem,
* \{ */
bool imb_is_a_hdr(const unsigned char *buf, size_t size);
struct ImBuf *imb_loadhdr(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags);
struct ImBuf *imb_load_hdr(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
bool imb_save_hdr(struct ImBuf *ibuf, const char *filepath, int flags);
/** \} */
@ -209,7 +209,6 @@ bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags);
/** \name Format: TIFF (#IMB_FTYPE_TIF)
* \{ */
void imb_inittiff(void);
bool imb_is_a_tiff(const unsigned char *buf, size_t size);
/**
* Loads a TIFF file.
@ -220,10 +219,10 @@ bool imb_is_a_tiff(const unsigned char *buf, size_t size);
*
* \return A newly allocated #ImBuf structure if successful, otherwise NULL.
*/
struct ImBuf *imb_loadtiff(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
struct ImBuf *imb_load_tiff(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
/**
* Saves a TIFF file.
*
@ -238,12 +237,12 @@ struct ImBuf *imb_loadtiff(const unsigned char *mem,
*
* \return 1 if the function is successful, 0 on failure.
*/
bool imb_savetiff(struct ImBuf *ibuf, const char *filepath, int flags);
bool imb_save_tiff(struct ImBuf *ibuf, const char *filepath, int flags);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Format: TIFF (#IMB_FTYPE_WEBP)
/** \name Format: WEBP (#IMB_FTYPE_WEBP)
* \{ */
bool imb_is_a_webp(const unsigned char *buf, size_t size);
@ -261,13 +260,26 @@ bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Format: DDS (#IMB_FTYPE_DDS)
* \{ */
bool imb_is_a_dds(const unsigned char *buf, size_t size);
struct ImBuf *imb_load_dds(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Format: PSD (#IMB_FTYPE_PSD)
* \{ */
bool imb_is_a_psd(const unsigned char *buf, size_t size);
struct ImBuf *imb_load_psd(const uchar *mem,
struct ImBuf *imb_load_psd(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);

View File

@ -1,383 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup imbuf
*/
#include <math.h>
#include "BLI_fileops.h"
#include "BLI_utildefines.h"
#include "imbuf.h"
#include "IMB_filetype.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
/* Some code copied from article on microsoft.com,
* copied here for enhanced BMP support in the future:
* http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/mfcp1/mfcp1.htm&nav=/msj/0197/newnav.htm
*/
typedef struct BMPINFOHEADER {
uint biSize;
uint biWidth;
uint biHeight;
ushort biPlanes;
ushort biBitCount;
uint biCompression;
uint biSizeImage;
uint biXPelsPerMeter;
uint biYPelsPerMeter;
uint biClrUsed;
uint biClrImportant;
} BMPINFOHEADER;
#if 0
typedef struct BMPHEADER {
ushort biType;
uint biSize;
ushort biRes1;
ushort biRes2;
uint biOffBits;
} BMPHEADER;
#endif
#define BMP_FILEHEADER_SIZE 14
#define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1]))
#define CHECK_HEADER_FIELD_BMP(_mem) \
(CHECK_HEADER_FIELD(_mem, "BM") || CHECK_HEADER_FIELD(_mem, "BA") || \
CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \
CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT"))
static bool checkbmp(const uchar *mem, const size_t size)
{
if (size < (BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER))) {
return false;
}
if (!CHECK_HEADER_FIELD_BMP(mem)) {
return false;
}
bool ok = false;
BMPINFOHEADER bmi;
uint u;
/* Skip file-header. */
mem += BMP_FILEHEADER_SIZE;
/* for systems where an int needs to be 4 bytes aligned */
memcpy(&bmi, mem, sizeof(bmi));
u = LITTLE_LONG(bmi.biSize);
/* we only support uncompressed images for now. */
if (u >= sizeof(BMPINFOHEADER)) {
if (bmi.biCompression == 0) {
u = LITTLE_SHORT(bmi.biBitCount);
if (u > 0 && u <= 32) {
ok = true;
}
}
}
return ok;
}
bool imb_is_a_bmp(const uchar *buf, size_t size)
{
return checkbmp(buf, size);
}
static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth)
{
/* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride
*/
return (((x * depth) + 31) & ~31) >> 3;
}
ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImBuf *ibuf = NULL;
BMPINFOHEADER bmi;
int ibuf_depth;
const uchar *bmp;
uchar *rect;
ushort col;
bool top_to_bottom = false;
if (checkbmp(mem, size) == 0) {
return NULL;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
/* For systems where an int needs to be 4 bytes aligned. */
memcpy(&bmi, mem + BMP_FILEHEADER_SIZE, sizeof(bmi));
const size_t palette_offset = (size_t)BMP_FILEHEADER_SIZE + LITTLE_LONG(bmi.biSize);
const int depth = LITTLE_SHORT(bmi.biBitCount);
const int xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
const int yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
const int x = LITTLE_LONG(bmi.biWidth);
int y = LITTLE_LONG(bmi.biHeight);
/* Negative height means bitmap is stored top-to-bottom. */
if (y < 0) {
y = -y;
top_to_bottom = true;
}
/* Validate and cross-check offsets and sizes. */
if (x < 1 || !ELEM(depth, 1, 4, 8, 16, 24, 32)) {
return NULL;
}
const size_t pixel_data_offset = (size_t)LITTLE_LONG(*(int *)(mem + 10));
const size_t header_bytes = BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER);
const size_t num_actual_data_bytes = size - pixel_data_offset;
const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth);
const size_t num_expected_data_bytes = row_size_in_bytes * y;
if (num_actual_data_bytes < num_expected_data_bytes || num_actual_data_bytes > size ||
pixel_data_offset < header_bytes || pixel_data_offset > (size - num_expected_data_bytes) ||
palette_offset < header_bytes || palette_offset > pixel_data_offset) {
return NULL;
}
if (depth <= 8) {
ibuf_depth = 24;
}
else {
ibuf_depth = depth;
}
bmp = mem + pixel_data_offset;
#if 0
printf("palette_offset: %d, x: %d y: %d, depth: %d\n", palette_offset, x, y, depth);
#endif
if (flags & IB_test) {
ibuf = IMB_allocImBuf(x, y, ibuf_depth, 0);
}
else {
ibuf = IMB_allocImBuf(x, y, ibuf_depth, IB_rect);
if (!ibuf) {
return NULL;
}
rect = (uchar *)ibuf->rect;
if (depth <= 8) {
const char(*palette)[4] = (const char(*)[4])(mem + palette_offset);
const int startmask = ((1 << depth) - 1) << 8;
for (size_t i = y; i > 0; i--) {
int bitoffs = 8;
int bitmask = startmask;
int nbytes = 0;
const char *pcol;
if (top_to_bottom) {
rect = (uchar *)&ibuf->rect[(i - 1) * x];
}
for (size_t j = x; j > 0; j--) {
bitoffs -= depth;
bitmask >>= depth;
const int index = (bmp[0] & bitmask) >> bitoffs;
pcol = palette[index];
/* intentionally BGR -> RGB */
rect[0] = pcol[2];
rect[1] = pcol[1];
rect[2] = pcol[0];
rect[3] = 255;
rect += 4;
if (bitoffs == 0) {
/* Advance to the next byte */
bitoffs = 8;
bitmask = startmask;
nbytes += 1;
bmp += 1;
}
}
/* Advance to the next row */
bmp += (row_size_in_bytes - nbytes);
}
}
else if (depth == 16) {
for (size_t i = y; i > 0; i--) {
if (top_to_bottom) {
rect = (uchar *)&ibuf->rect[(i - 1) * x];
}
for (size_t j = x; j > 0; j--) {
col = bmp[0] + (bmp[1] << 8);
rect[0] = ((col >> 10) & 0x1f) << 3;
rect[1] = ((col >> 5) & 0x1f) << 3;
rect[2] = ((col >> 0) & 0x1f) << 3;
rect[3] = 255;
rect += 4;
bmp += 2;
}
}
}
else if (depth == 24) {
const int x_pad = x % 4;
for (size_t i = y; i > 0; i--) {
if (top_to_bottom) {
rect = (uchar *)&ibuf->rect[(i - 1) * x];
}
for (size_t j = x; j > 0; j--) {
rect[0] = bmp[2];
rect[1] = bmp[1];
rect[2] = bmp[0];
rect[3] = 255;
rect += 4;
bmp += 3;
}
/* for 24-bit images, rows are padded to multiples of 4 */
bmp += x_pad;
}
}
else if (depth == 32) {
for (size_t i = y; i > 0; i--) {
if (top_to_bottom) {
rect = (uchar *)&ibuf->rect[(i - 1) * x];
}
for (size_t j = x; j > 0; j--) {
rect[0] = bmp[2];
rect[1] = bmp[1];
rect[2] = bmp[0];
rect[3] = bmp[3];
rect += 4;
bmp += 4;
}
}
}
}
if (ibuf) {
ibuf->ppm[0] = xppm;
ibuf->ppm[1] = yppm;
ibuf->ftype = IMB_FTYPE_BMP;
}
return ibuf;
}
#undef CHECK_HEADER_FIELD_BMP
#undef CHECK_HEADER_FIELD
/* Couple of helper functions for writing our data */
static int putIntLSB(uint ui, FILE *ofile)
{
putc((ui >> 0) & 0xFF, ofile);
putc((ui >> 8) & 0xFF, ofile);
putc((ui >> 16) & 0xFF, ofile);
return putc((ui >> 24) & 0xFF, ofile);
}
static int putShortLSB(ushort us, FILE *ofile)
{
putc((us >> 0) & 0xFF, ofile);
return putc((us >> 8) & 0xFF, ofile);
}
bool imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags))
{
BMPINFOHEADER infoheader;
const size_t bytes_per_pixel = (ibuf->planes + 7) >> 3;
BLI_assert(ELEM(bytes_per_pixel, 1, 3));
const size_t pad_bytes_per_scanline = (4 - ibuf->x * bytes_per_pixel % 4) % 4;
const size_t bytesize = (ibuf->x * bytes_per_pixel + pad_bytes_per_scanline) * ibuf->y;
const uchar *data = (const uchar *)ibuf->rect;
FILE *ofile = BLI_fopen(filepath, "wb");
if (ofile == NULL) {
return 0;
}
const bool is_grayscale = bytes_per_pixel == 1;
const size_t palette_size = is_grayscale ? 255 * 4 : 0; /* RGBA32 */
const size_t pixel_array_start = BMP_FILEHEADER_SIZE + sizeof(infoheader) + palette_size;
putShortLSB(19778, ofile); /* "BM" */
putIntLSB(bytesize + pixel_array_start, ofile); /* Total file size */
putShortLSB(0, ofile); /* Res1 */
putShortLSB(0, ofile); /* Res2 */
putIntLSB(pixel_array_start, ofile); /* offset to start of pixel array */
putIntLSB(sizeof(infoheader), ofile);
putIntLSB(ibuf->x, ofile);
putIntLSB(ibuf->y, ofile);
putShortLSB(1, ofile);
putShortLSB(is_grayscale ? 8 : 24, ofile);
putIntLSB(0, ofile);
putIntLSB(bytesize, ofile);
putIntLSB(round(ibuf->ppm[0]), ofile);
putIntLSB(round(ibuf->ppm[1]), ofile);
putIntLSB(0, ofile);
putIntLSB(0, ofile);
/* color palette table, which is just every grayscale color, full alpha */
if (is_grayscale) {
for (char i = 0; i < 255; i++) {
putc(i, ofile);
putc(i, ofile);
putc(i, ofile);
putc(0xFF, ofile);
}
}
if (is_grayscale) {
for (size_t y = 0; y < ibuf->y; y++) {
for (size_t x = 0; x < ibuf->x; x++) {
const size_t ptr = (x + y * ibuf->x) * 4;
if (putc(data[ptr], ofile) == EOF) {
return 0;
}
}
/* Add padding here. */
for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
if (putc(0, ofile) == EOF) {
return 0;
}
}
}
}
else {
/* Need to write out padded image data in BGR format. */
for (size_t y = 0; y < ibuf->y; y++) {
for (size_t x = 0; x < ibuf->x; x++) {
const size_t ptr = (x + y * ibuf->x) * 4;
if (putc(data[ptr + 2], ofile) == EOF) {
return 0;
}
if (putc(data[ptr + 1], ofile) == EOF) {
return 0;
}
if (putc(data[ptr], ofile) == EOF) {
return 0;
}
}
/* Add padding here. */
for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
if (putc(0, ofile) == EOF) {
return 0;
}
}
}
}
fflush(ofile);
fclose(ofile);
return 1;
}

View File

@ -182,21 +182,3 @@ ImBuf *imb_load_cineon(const uchar *mem, size_t size, int flags, char colorspace
}
return imb_load_dpx_cineon(mem, size, 1, flags, colorspace);
}
bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags)
{
return imb_save_dpx_cineon(buf, filepath, 0, flags);
}
bool imb_is_a_dpx(const uchar *buf, size_t size)
{
return logImageIsDpx(buf, size);
}
ImBuf *imb_load_dpx(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
if (!imb_is_a_dpx(mem, size)) {
return NULL;
}
return imb_load_dpx_cineon(mem, size, 0, flags, colorspace);
}

View File

@ -20,10 +20,6 @@
# include "openexr/openexr_api.h"
#endif
#ifdef WITH_DDS
# include "dds/dds_api.h"
#endif
const ImFileType IMB_FILE_TYPES[] = {
{
.init = NULL,
@ -41,10 +37,10 @@ const ImFileType IMB_FILE_TYPES[] = {
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_png,
.load = imb_loadpng,
.load = imb_load_png,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = imb_savepng,
.save = imb_save_png,
.flag = 0,
.filetype = IMB_FTYPE_PNG,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
@ -53,10 +49,10 @@ const ImFileType IMB_FILE_TYPES[] = {
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_bmp,
.load = imb_bmp_decode,
.load = imb_load_bmp,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = imb_savebmp,
.save = imb_save_bmp,
.flag = 0,
.filetype = IMB_FTYPE_BMP,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
@ -64,11 +60,11 @@ const ImFileType IMB_FILE_TYPES[] = {
{
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_targa,
.load = imb_loadtarga,
.is_a = imb_is_a_tga,
.load = imb_load_tga,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = imb_savetarga,
.save = imb_save_tga,
.flag = 0,
.filetype = IMB_FTYPE_TGA,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
@ -85,7 +81,6 @@ const ImFileType IMB_FILE_TYPES[] = {
.filetype = IMB_FTYPE_IMAGIC,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
},
#ifdef WITH_CINEON
{
.init = NULL,
.exit = NULL,
@ -98,6 +93,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.filetype = IMB_FTYPE_DPX,
.default_save_role = COLOR_ROLE_DEFAULT_FLOAT,
},
#ifdef WITH_CINEON
{
.init = NULL,
.exit = NULL,
@ -111,34 +107,30 @@ const ImFileType IMB_FILE_TYPES[] = {
.default_save_role = COLOR_ROLE_DEFAULT_FLOAT,
},
#endif
#ifdef WITH_TIFF
{
.init = imb_inittiff,
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_tiff,
.load = imb_loadtiff,
.load = imb_load_tiff,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = imb_savetiff,
.save = imb_save_tiff,
.flag = 0,
.filetype = IMB_FTYPE_TIF,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
},
#endif
#ifdef WITH_HDR
{
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_hdr,
.load = imb_loadhdr,
.load = imb_load_hdr,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = imb_savehdr,
.save = imb_save_hdr,
.flag = IM_FTYPE_FLOAT,
.filetype = IMB_FTYPE_RADHDR,
.default_save_role = COLOR_ROLE_DEFAULT_FLOAT,
},
#endif
#ifdef WITH_OPENEXR
{
.init = imb_initopenexr,
@ -167,7 +159,6 @@ const ImFileType IMB_FILE_TYPES[] = {
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
},
#endif
#ifdef WITH_DDS
{
.init = NULL,
.exit = NULL,
@ -180,7 +171,6 @@ const ImFileType IMB_FILE_TYPES[] = {
.filetype = IMB_FTYPE_DDS,
.default_save_role = COLOR_ROLE_DEFAULT_BYTE,
},
#endif
{
.init = NULL,
.exit = NULL,

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_bmp(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "bmp");
}
ImBuf *imb_load_bmp(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
/* Keep historical behavior - do not use a 1-channel format for a black-white image. */
config.attribute("bmp:monochrome_detect", 0);
ReadContext ctx{mem, size, "bmp", IMB_FTYPE_BMP, flags};
return imb_oiio_read(ctx, config, colorspace, spec);
}
bool imb_save_bmp(struct ImBuf *ibuf, const char *filepath, int flags)
{
const int file_channels = ibuf->planes >> 3;
const TypeDesc data_format = TypeDesc::UINT8;
WriteContext ctx = imb_create_write_context("bmp", ibuf, flags, false);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -0,0 +1,319 @@
/* SPDX-License-Identifier: GPL-2.0-or-later AND BSD-3-Clause
* Copyright 2009 Google Inc. All rights reserved. (BSD-3-Clause)
* Copyright 2023 Blender Foundation (GPL-2.0-or-later). */
/**
* Some portions of this file are from the Chromium project and have been adapted
* for Blender use when flipping DDS images to the OpenGL convention.
*/
#include <algorithm>
#include <memory>
#include "oiio/openimageio_support.hh"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
#ifdef __BIG_ENDIAN__
# include "BLI_endian_switch.h"
#endif
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
using std::unique_ptr;
extern "C" {
static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader);
bool imb_is_a_dds(const uchar *buf, size_t size)
{
return imb_oiio_check(buf, size, "dds");
}
ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
ReadContext ctx{mem, size, "dds", IMB_FTYPE_DDS, flags};
ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec);
/* Load compressed DDS information if available. */
if (ibuf && (flags & IB_test) == 0) {
Filesystem::IOMemReader mem_reader(cspan<uchar>(mem, size));
LoadDXTCImage(ibuf, mem_reader);
}
return ibuf;
}
/* A function that flips a DXTC block. */
using FlipBlockFunction = void (*)(uint8_t *block);
/* Flips a full DXT1 block in the y direction. */
static void FlipDXT1BlockFull(uint8_t *block)
{
/* A DXT1 block layout is:
* [0-1] color0.
* [2-3] color1.
* [4-7] color bitmap, 2 bits per pixel.
* So each of the 4-7 bytes represents one line, flipping a block is just
* flipping those bytes. */
uint8_t tmp = block[4];
block[4] = block[7];
block[7] = tmp;
tmp = block[5];
block[5] = block[6];
block[6] = tmp;
}
/* Flips the first 2 lines of a DXT1 block in the y direction. */
static void FlipDXT1BlockHalf(uint8_t *block)
{
/* See layout above. */
uint8_t tmp = block[4];
block[4] = block[5];
block[5] = tmp;
}
/* Flips a full DXT3 block in the y direction. */
static void FlipDXT3BlockFull(uint8_t *block)
{
/* A DXT3 block layout is:
* [0-7] alpha bitmap, 4 bits per pixel.
* [8-15] a DXT1 block. */
/* We can flip the alpha bits at the byte level (2 bytes per line). */
uint8_t tmp = block[0];
block[0] = block[6];
block[6] = tmp;
tmp = block[1];
block[1] = block[7];
block[7] = tmp;
tmp = block[2];
block[2] = block[4];
block[4] = tmp;
tmp = block[3];
block[3] = block[5];
block[5] = tmp;
/* And flip the DXT1 block using the above function. */
FlipDXT1BlockFull(block + 8);
}
/* Flips the first 2 lines of a DXT3 block in the y direction. */
static void FlipDXT3BlockHalf(uint8_t *block)
{
/* See layout above. */
uint8_t tmp = block[0];
block[0] = block[2];
block[2] = tmp;
tmp = block[1];
block[1] = block[3];
block[3] = tmp;
FlipDXT1BlockHalf(block + 8);
}
/* Flips a full DXT5 block in the y direction. */
static void FlipDXT5BlockFull(uint8_t *block)
{
/* A DXT5 block layout is:
* [0] alpha0.
* [1] alpha1.
* [2-7] alpha bitmap, 3 bits per pixel.
* [8-15] a DXT1 block. */
/* The alpha bitmap doesn't easily map lines to bytes, so we have to
* interpret it correctly. Extracted from
* http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt :
*
* The 6 "bits" bytes of the block are decoded into one 48-bit integer:
*
* bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 +
* 256 * (bits_4 + 256 * bits_5))))
*
* bits is a 48-bit unsigned-integer, from which a three-bit control code
* is extracted for a texel at location (x,y) in the block using:
*
* code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0]
*
* where bit 47 is the most significant and bit 0 is the least
* significant bit. */
uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
uint line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]);
/* swap lines 0 and 1 in line_0_1. */
uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
/* swap lines 2 and 3 in line_2_3. */
uint line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12);
block[2] = line_3_2 & 0xff;
block[3] = (line_3_2 & 0xff00) >> 8;
block[4] = (line_3_2 & 0xff0000) >> 16;
block[5] = line_1_0 & 0xff;
block[6] = (line_1_0 & 0xff00) >> 8;
block[7] = (line_1_0 & 0xff0000) >> 16;
/* And flip the DXT1 block using the above function. */
FlipDXT1BlockFull(block + 8);
}
/* Flips the first 2 lines of a DXT5 block in the y direction. */
static void FlipDXT5BlockHalf(uint8_t *block)
{
/* See layout above. */
uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
block[2] = line_1_0 & 0xff;
block[3] = (line_1_0 & 0xff00) >> 8;
block[4] = (line_1_0 & 0xff0000) >> 16;
FlipDXT1BlockHalf(block + 8);
}
/**
* Flips a DXTC image, by flipping and swapping DXTC blocks as appropriate.
*
* Use to flip vertically to fit OpenGL convention.
*/
static void FlipDXTCImage(ImBuf *ibuf)
{
uint32_t width = ibuf->x;
uint32_t height = ibuf->y;
uint32_t levels = ibuf->dds_data.nummipmaps;
int fourcc = ibuf->dds_data.fourcc;
uint8_t *data = ibuf->dds_data.data;
int data_size = ibuf->dds_data.size;
uint32_t *num_valid_levels = &ibuf->dds_data.nummipmaps;
*num_valid_levels = 0;
/* Must have valid dimensions. */
if (width == 0 || height == 0) {
return;
}
/* Height must be a power-of-two. */
if ((height & (height - 1)) != 0) {
return;
}
FlipBlockFunction full_block_function;
FlipBlockFunction half_block_function;
uint block_bytes = 0;
switch (fourcc) {
case FOURCC_DXT1:
full_block_function = FlipDXT1BlockFull;
half_block_function = FlipDXT1BlockHalf;
block_bytes = 8;
break;
case FOURCC_DXT3:
full_block_function = FlipDXT3BlockFull;
half_block_function = FlipDXT3BlockHalf;
block_bytes = 16;
break;
case FOURCC_DXT5:
full_block_function = FlipDXT5BlockFull;
half_block_function = FlipDXT5BlockHalf;
block_bytes = 16;
break;
default:
return;
}
*num_valid_levels = levels;
uint mip_width = width;
uint mip_height = height;
const uint8_t *data_end = data + data_size;
for (uint i = 0; i < levels; i++) {
uint blocks_per_row = (mip_width + 3) / 4;
uint blocks_per_col = (mip_height + 3) / 4;
uint blocks = blocks_per_row * blocks_per_col;
if (data + block_bytes * blocks > data_end) {
/* Stop flipping when running out of data to be modified, avoiding possible buffer overrun
* on a malformed files. */
*num_valid_levels = i;
break;
}
if (mip_height == 1) {
/* no flip to do, and we're done. */
break;
}
if (mip_height == 2) {
/* flip the first 2 lines in each block. */
for (uint i = 0; i < blocks_per_row; i++) {
half_block_function(data + i * block_bytes);
}
}
else {
/* flip each block. */
for (uint i = 0; i < blocks; i++) {
full_block_function(data + i * block_bytes);
}
/* Swap each block line in the first half of the image with the
* corresponding one in the second half.
* note that this is a no-op if mip_height is 4. */
uint row_bytes = block_bytes * blocks_per_row;
uint8_t *temp_line = new uint8_t[row_bytes];
for (uint y = 0; y < blocks_per_col / 2; y++) {
uint8_t *line1 = data + y * row_bytes;
uint8_t *line2 = data + (blocks_per_col - y - 1) * row_bytes;
memcpy(temp_line, line1, row_bytes);
memcpy(line1, line2, row_bytes);
memcpy(line2, temp_line, row_bytes);
}
delete[] temp_line;
}
/* mip levels are contiguous. */
data += block_bytes * blocks;
mip_width = std::max(1U, mip_width >> 1);
mip_height = std::max(1U, mip_height >> 1);
}
}
static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
{
/* Reach into memory and pull out the pixel format flags and mipmap counts. This is safe if
* we've made it this far. */
uint32_t flags = 0;
mem_reader.pread(&flags, sizeof(uint32_t), 8);
mem_reader.pread(&ibuf->dds_data.nummipmaps, sizeof(uint32_t), 28);
mem_reader.pread(&ibuf->dds_data.fourcc, sizeof(uint32_t), 84);
#ifdef __BIG_ENDIAN__
BLI_endian_switch_uint32(&ibuf->dds_data.nummipmaps);
#endif
const uint32_t DDSD_MIPMAPCOUNT = 0x00020000U;
if ((flags & DDSD_MIPMAPCOUNT) == 0) {
ibuf->dds_data.nummipmaps = 1;
}
/* Load the compressed data. */
if (ibuf->dds_data.fourcc != FOURCC_DDS) {
uint32_t dds_header_size = 128;
if (ibuf->dds_data.fourcc == FOURCC_DX10) {
dds_header_size += 20;
}
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
/* Flip compressed image data to match OpenGL convention. */
FlipDXTCImage(ibuf);
}
}
}

View File

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_colormanagement.h"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_dpx(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "dpx");
}
ImBuf *imb_load_dpx(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
ReadContext ctx{mem, size, "dpx", IMB_FTYPE_DPX, flags};
ctx.use_colorspace_role = COLOR_ROLE_DEFAULT_FLOAT;
ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec);
if (ibuf) {
if (flags & IB_alphamode_detect) {
ibuf->flags |= IB_alphamode_premul;
}
}
return ibuf;
}
bool imb_save_dpx(struct ImBuf *ibuf, const char *filepath, int flags)
{
int bits_per_sample = 8;
if (ibuf->foptions.flag & CINEON_10BIT) {
bits_per_sample = 10;
}
else if (ibuf->foptions.flag & CINEON_12BIT) {
bits_per_sample = 12;
}
else if (ibuf->foptions.flag & CINEON_16BIT) {
bits_per_sample = 16;
}
const int file_channels = ibuf->planes >> 3;
const TypeDesc data_format = bits_per_sample == 8 ? TypeDesc::UINT8 : TypeDesc::UINT16;
WriteContext ctx = imb_create_write_context("dpx", ibuf, flags);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
const float max_value = powf(2, bits_per_sample) - 1.0f;
file_spec.attribute("oiio:BitsPerSample", bits_per_sample);
file_spec.attribute("dpx:WhiteLevel", 685.0f / 1023.0f * max_value);
file_spec.attribute("dpx:BlackLevel", 95.0f / 1023.0f * max_value);
file_spec.attribute("dpx:HighData", max_value);
file_spec.attribute("dpx:LowData", 0);
file_spec.attribute("dpx:LowQuantity", 0.0f);
if (ibuf->foptions.flag & CINEON_LOG) {
/* VERIFY: This matches previous code but seems odd. Needs a comment if confirmed. */
file_spec.attribute("dpx:Transfer", "Printing density");
file_spec.attribute("dpx:HighQuantity", 2.048f);
}
else {
file_spec.attribute("dpx:Transfer", "Linear");
file_spec.attribute("dpx:HighQuantity", max_value);
}
if (ELEM(bits_per_sample, 8, 16)) {
file_spec.attribute("dpx:Packing", "Packed");
}
else {
file_spec.attribute("dpx:Packing", "Filled, method A");
}
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_hdr(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "hdr");
}
ImBuf *imb_load_hdr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
ReadContext ctx{mem, size, "hdr", IMB_FTYPE_RADHDR, flags};
/* Always create ImBufs with a 4th alpha channel despite the format only supporting 3. */
ctx.use_all_planes = true;
ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec);
if (ibuf) {
if (flags & IB_alphamode_detect) {
ibuf->flags |= IB_alphamode_premul;
}
if (flags & IB_rect) {
IMB_rect_from_float(ibuf);
}
}
return ibuf;
}
bool imb_save_hdr(struct ImBuf *ibuf, const char *filepath, int flags)
{
const int file_channels = 3;
const TypeDesc data_format = TypeDesc::FLOAT;
WriteContext ctx = imb_create_write_context("hdr", ibuf, flags);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_colormanagement.h"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_png(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "png");
}
ImBuf *imb_load_png(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
config.attribute("oiio:UnassociatedAlpha", 1);
ReadContext ctx{mem, size, "png", IMB_FTYPE_PNG, flags};
/* Both 8 and 16 bit PNGs should be in default byte colorspace. */
ctx.use_colorspace_role = COLOR_ROLE_DEFAULT_BYTE;
ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec);
if (ibuf) {
if (spec.format == TypeDesc::UINT16) {
ibuf->flags |= PNG_16BIT;
}
}
return ibuf;
}
bool imb_save_png(struct ImBuf *ibuf, const char *filepath, int flags)
{
const bool is_16bit = (ibuf->foptions.flag & PNG_16BIT);
const int file_channels = ibuf->planes >> 3;
const TypeDesc data_format = is_16bit ? TypeDesc::UINT16 : TypeDesc::UINT8;
WriteContext ctx = imb_create_write_context("png", ibuf, flags, is_16bit);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
/* Skip if the float buffer was managed already. */
if (is_16bit && (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA))) {
file_spec.attribute("oiio:UnassociatedAlpha", 0);
}
else {
file_spec.attribute("oiio:UnassociatedAlpha", 1);
}
int compression = (int)((float)ibuf->foptions.quality / 11.1111f);
compression = compression < 0 ? 0 : (compression > 9 ? 9 : compression);
file_spec.attribute("png:compressionLevel", compression);
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_tga(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "tga");
}
ImBuf *imb_load_tga(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
config.attribute("oiio:UnassociatedAlpha", 1);
ReadContext ctx{mem, size, "tga", IMB_FTYPE_TGA, flags};
return imb_oiio_read(ctx, config, colorspace, spec);
}
bool imb_save_tga(struct ImBuf *ibuf, const char *filepath, int flags)
{
const int file_channels = ibuf->planes >> 3;
const TypeDesc data_format = TypeDesc::UINT8;
WriteContext ctx = imb_create_write_context("tga", ibuf, flags, false);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
file_spec.attribute("oiio:UnassociatedAlpha", 1);
file_spec.attribute("compression", (ibuf->foptions.flag & RAWTGA) ? "none" : "rle");
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_colormanagement.h"
#include "IMB_filetype.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_tiff(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "tif");
}
ImBuf *imb_load_tiff(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
config.attribute("oiio:UnassociatedAlpha", 1);
ReadContext ctx{mem, size, "tif", IMB_FTYPE_TIF, flags};
/* All TIFFs should be in default byte colorspace. */
ctx.use_colorspace_role = COLOR_ROLE_DEFAULT_BYTE;
ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec);
if (ibuf) {
if (flags & IB_alphamode_detect) {
if (spec.nchannels == 4 && spec.format == TypeDesc::UINT16) {
ibuf->flags |= IB_alphamode_premul;
}
}
}
return ibuf;
}
bool imb_save_tiff(struct ImBuf *ibuf, const char *filepath, int flags)
{
const bool is_16bit = ((ibuf->foptions.flag & TIF_16BIT) && ibuf->rect_float);
const int file_channels = ibuf->planes >> 3;
const TypeDesc data_format = is_16bit ? TypeDesc::UINT16 : TypeDesc::UINT8;
WriteContext ctx = imb_create_write_context("tif", ibuf, flags, is_16bit);
ImageSpec file_spec = imb_create_write_spec(ctx, file_channels, data_format);
if (is_16bit && file_channels == 4) {
file_spec.attribute("oiio:UnassociatedAlpha", 0);
}
else {
file_spec.attribute("oiio:UnassociatedAlpha", 1);
}
if (ibuf->foptions.flag & TIF_COMPRESS_DEFLATE) {
file_spec.attribute("compression", "zip");
}
else if (ibuf->foptions.flag & TIF_COMPRESS_LZW) {
file_spec.attribute("compression", "lzw");
}
else if (ibuf->foptions.flag & TIF_COMPRESS_PACKBITS) {
file_spec.attribute("compression", "packbits");
}
return imb_oiio_write(ctx, filepath, file_spec);
}
}

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "openimageio_support.hh"
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>
#include "BLI_blenlib.h"
@ -289,10 +291,19 @@ bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSp
return false;
}
auto write_op = [&out, &ctx]() {
return out->write_image(
ctx.mem_format, ctx.mem_start, ctx.mem_xstride, -ctx.mem_ystride, AutoStride);
};
ImageBuf orig_buf(ctx.mem_spec, ctx.mem_start, ctx.mem_xstride, -ctx.mem_ystride, AutoStride);
ImageBuf final_buf{};
/* Grayscale images need to be based on luminance weights rather than only
* using a single channel from the source. */
if (file_spec.nchannels == 1) {
float weights[3];
IMB_colormanagement_get_luminance_coefficients(weights);
ImageBufAlgo::channel_sum(final_buf, orig_buf, weights);
}
else {
final_buf = std::move(orig_buf);
}
bool ok = false;
if (ctx.flags & IB_mem) {
@ -302,11 +313,11 @@ bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSp
imb_addencodedbufferImBuf(ctx.ibuf);
out->set_ioproxy(&writer);
out->open("", file_spec);
ok = write_op();
ok = final_buf.write(out.get());
}
else {
out->open(filepath, file_spec);
ok = write_op();
ok = final_buf.write(out.get());
}
out->close();
@ -330,15 +341,15 @@ WriteContext imb_create_write_context(const char *file_format,
const int mem_channels = ibuf->channels ? ibuf->channels : 4;
ctx.mem_xstride = sizeof(float) * mem_channels;
ctx.mem_ystride = width * ctx.mem_xstride;
ctx.mem_format = TypeDesc::FLOAT;
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect_float);
ctx.mem_spec = ImageSpec(width, height, mem_channels, TypeDesc::FLOAT);
}
else {
const int mem_channels = 4;
ctx.mem_xstride = sizeof(uchar) * mem_channels;
ctx.mem_ystride = width * ctx.mem_xstride;
ctx.mem_format = TypeDesc::UINT8;
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect);
ctx.mem_spec = ImageSpec(width, height, mem_channels, TypeDesc::UINT8);
}
/* We always write using a negative y-stride so ensure we start at the end. */

View File

@ -40,13 +40,12 @@ struct ReadContext {
struct WriteContext {
const char *file_format;
ImBuf *ibuf;
int flags;
uchar *mem_start;
OIIO::stride_t mem_xstride;
OIIO::stride_t mem_ystride;
OIIO::TypeDesc mem_format;
uchar *mem_start;
int flags;
OIIO::ImageSpec mem_spec;
};
/**

View File

@ -1,814 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup imbuf
*
* \todo Save floats as 16 bits per channel, currently readonly.
*/
#include <png.h>
#include "BLI_fileops.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "DNA_ID.h" /* ID property definitions. */
#include "MEM_guardedalloc.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_allocimbuf.h"
#include "IMB_filetype.h"
#include "IMB_metadata.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
typedef struct PNGReadStruct {
const uchar *data;
uint size;
uint seek;
} PNGReadStruct;
static void ReadData(png_structp png_ptr, png_bytep data, png_size_t length);
static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length);
static void Flush(png_structp png_ptr);
BLI_INLINE ushort UPSAMPLE_8_TO_16(const uchar _val)
{
return (_val << 8) + _val;
}
bool imb_is_a_png(const uchar *mem, size_t size)
{
const int num_to_check = 8;
if (size < num_to_check) {
return false;
}
bool ok = false;
#if (PNG_LIBPNG_VER_MAJOR == 1) && (PNG_LIBPNG_VER_MINOR == 2)
/* Older version of libpng doesn't use const pointer to memory. */
ok = !png_sig_cmp((png_bytep)mem, 0, num_to_check);
#else
ok = !png_sig_cmp(mem, 0, num_to_check);
#endif
return ok;
}
static void Flush(png_structp png_ptr)
{
(void)png_ptr;
}
static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
{
ImBuf *ibuf = (ImBuf *)png_get_io_ptr(png_ptr);
/* if buffer is too small increase it. */
while (ibuf->encodedsize + length > ibuf->encodedbuffersize) {
imb_enlargeencodedbufferImBuf(ibuf);
}
memcpy(ibuf->encodedbuffer + ibuf->encodedsize, data, length);
ibuf->encodedsize += length;
}
static void ReadData(png_structp png_ptr, png_bytep data, png_size_t length)
{
PNGReadStruct *rs = (PNGReadStruct *)png_get_io_ptr(png_ptr);
if (rs) {
if (length <= rs->size - rs->seek) {
memcpy(data, rs->data + rs->seek, length);
rs->seek += length;
return;
}
}
printf("Reached EOF while decoding PNG\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
static float channel_colormanage_noop(float value)
{
return value;
}
/* wrap to avoid macro calling functions multiple times */
BLI_INLINE ushort ftoshort(float val)
{
return unit_float_to_ushort_clamp(val);
}
bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
{
png_structp png_ptr;
png_infop info_ptr;
uchar *pixels = NULL;
uchar *from, *to;
ushort *pixels16 = NULL, *to16;
float *from_float, from_straight[4];
png_bytepp row_pointers = NULL;
int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY;
FILE *fp = NULL;
bool is_16bit = (ibuf->foptions.flag & PNG_16BIT) != 0;
bool has_float = (ibuf->rect_float != NULL);
int channels_in_float = ibuf->channels ? ibuf->channels : 4;
float (*chanel_colormanage_cb)(float);
size_t num_bytes;
/* use the jpeg quality setting for compression */
int compression;
compression = (int)((float)(ibuf->foptions.quality) / 11.1111f);
compression = compression < 0 ? 0 : (compression > 9 ? 9 : compression);
if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
/* float buffer was managed already, no need in color space conversion */
chanel_colormanage_cb = channel_colormanage_noop;
}
else {
/* Standard linear-to-SRGB conversion if float buffer wasn't managed. */
chanel_colormanage_cb = linearrgb_to_srgb;
}
/* for prints */
if (flags & IB_mem) {
filepath = "<memory>";
}
bytesperpixel = (ibuf->planes + 7) >> 3;
if ((bytesperpixel > 4) || (bytesperpixel == 2)) {
printf(
"imb_savepng: Unsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, filepath);
return 0;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
printf("imb_savepng: Cannot png_create_write_struct for file: '%s'\n", filepath);
return 0;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
printf("imb_savepng: Cannot png_create_info_struct for file: '%s'\n", filepath);
return 0;
}
/* copy image data */
num_bytes = ((size_t)ibuf->x) * ibuf->y * bytesperpixel;
if (is_16bit) {
pixels16 = MEM_mallocN(num_bytes * sizeof(ushort), "png 16bit pixels");
}
else {
pixels = MEM_mallocN(num_bytes * sizeof(uchar), "png 8bit pixels");
}
if (pixels == NULL && pixels16 == NULL) {
printf(
"imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: "
"'%s'\n",
ibuf->x,
ibuf->y,
bytesperpixel,
filepath);
}
/* allocate memory for an array of row-pointers */
row_pointers = (png_bytepp)MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers");
if (row_pointers == NULL) {
printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", filepath);
}
if ((pixels == NULL && pixels16 == NULL) || (row_pointers == NULL) ||
setjmp(png_jmpbuf(png_ptr))) {
/* On error jump here, and free any resources. */
png_destroy_write_struct(&png_ptr, &info_ptr);
if (pixels) {
MEM_freeN(pixels);
}
if (pixels16) {
MEM_freeN(pixels16);
}
if (row_pointers) {
MEM_freeN(row_pointers);
}
if (fp) {
fflush(fp);
fclose(fp);
}
return 0;
}
from = (uchar *)ibuf->rect;
to = pixels;
from_float = ibuf->rect_float;
to16 = pixels16;
switch (bytesperpixel) {
case 4:
color_type = PNG_COLOR_TYPE_RGBA;
if (is_16bit) {
if (has_float) {
if (channels_in_float == 4) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
premul_to_straight_v4_v4(from_straight, from_float);
to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0]));
to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1]));
to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2]));
to16[3] = ftoshort(chanel_colormanage_cb(from_straight[3]));
to16 += 4;
from_float += 4;
}
}
else if (channels_in_float == 3) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = ftoshort(chanel_colormanage_cb(from_float[0]));
to16[1] = ftoshort(chanel_colormanage_cb(from_float[1]));
to16[2] = ftoshort(chanel_colormanage_cb(from_float[2]));
to16[3] = 65535;
to16 += 4;
from_float += 3;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = ftoshort(chanel_colormanage_cb(from_float[0]));
to16[2] = to16[1] = to16[0];
to16[3] = 65535;
to16 += 4;
from_float++;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = UPSAMPLE_8_TO_16(from[0]);
to16[1] = UPSAMPLE_8_TO_16(from[1]);
to16[2] = UPSAMPLE_8_TO_16(from[2]);
to16[3] = UPSAMPLE_8_TO_16(from[3]);
to16 += 4;
from += 4;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to[3] = from[3];
to += 4;
from += 4;
}
}
break;
case 3:
color_type = PNG_COLOR_TYPE_RGB;
if (is_16bit) {
if (has_float) {
if (channels_in_float == 4) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
premul_to_straight_v4_v4(from_straight, from_float);
to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0]));
to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1]));
to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2]));
to16 += 3;
from_float += 4;
}
}
else if (channels_in_float == 3) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = ftoshort(chanel_colormanage_cb(from_float[0]));
to16[1] = ftoshort(chanel_colormanage_cb(from_float[1]));
to16[2] = ftoshort(chanel_colormanage_cb(from_float[2]));
to16 += 3;
from_float += 3;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = ftoshort(chanel_colormanage_cb(from_float[0]));
to16[2] = to16[1] = to16[0];
to16 += 3;
from_float++;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = UPSAMPLE_8_TO_16(from[0]);
to16[1] = UPSAMPLE_8_TO_16(from[1]);
to16[2] = UPSAMPLE_8_TO_16(from[2]);
to16 += 3;
from += 4;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to += 3;
from += 4;
}
}
break;
case 1:
color_type = PNG_COLOR_TYPE_GRAY;
if (is_16bit) {
if (has_float) {
float rgb[3];
if (channels_in_float == 4) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
premul_to_straight_v4_v4(from_straight, from_float);
rgb[0] = chanel_colormanage_cb(from_straight[0]);
rgb[1] = chanel_colormanage_cb(from_straight[1]);
rgb[2] = chanel_colormanage_cb(from_straight[2]);
to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb));
to16++;
from_float += 4;
}
}
else if (channels_in_float == 3) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
rgb[0] = chanel_colormanage_cb(from_float[0]);
rgb[1] = chanel_colormanage_cb(from_float[1]);
rgb[2] = chanel_colormanage_cb(from_float[2]);
to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb));
to16++;
from_float += 3;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = ftoshort(chanel_colormanage_cb(from_float[0]));
to16++;
from_float++;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = UPSAMPLE_8_TO_16(from[0]);
to16++;
from += 4;
}
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to++;
from += 4;
}
}
break;
}
if (flags & IB_mem) {
/* create image in memory */
imb_addencodedbufferImBuf(ibuf);
ibuf->encodedsize = 0;
png_set_write_fn(png_ptr, (png_voidp)ibuf, WriteData, Flush);
}
else {
fp = BLI_fopen(filepath, "wb");
if (!fp) {
png_destroy_write_struct(&png_ptr, &info_ptr);
if (pixels) {
MEM_freeN(pixels);
}
if (pixels16) {
MEM_freeN(pixels16);
}
MEM_freeN(row_pointers);
printf("imb_savepng: Cannot open file for writing: '%s'\n", filepath);
return 0;
}
png_init_io(png_ptr, fp);
}
#if 0
png_set_filter(png_ptr,
0,
PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB |
PNG_FILTER_UP | PNG_FILTER_VALUE_UP | PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG |
PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH | PNG_ALL_FILTERS);
#endif
png_set_compression_level(png_ptr, compression);
/* png image settings */
png_set_IHDR(png_ptr,
info_ptr,
ibuf->x,
ibuf->y,
is_16bit ? 16 : 8,
color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
/* image text info */
if (ibuf->metadata) {
png_text *metadata;
IDProperty *prop;
int num_text = 0;
for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) {
if (prop->type == IDP_STRING) {
num_text++;
}
}
metadata = MEM_callocN(num_text * sizeof(png_text), "png_metadata");
num_text = 0;
for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) {
if (prop->type == IDP_STRING) {
metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
metadata[num_text].key = prop->name;
metadata[num_text].text = IDP_String(prop);
num_text++;
}
}
png_set_text(png_ptr, info_ptr, metadata, num_text);
MEM_freeN(metadata);
}
if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
png_set_pHYs(png_ptr,
info_ptr,
(uint)(ibuf->ppm[0] + 0.5),
(uint)(ibuf->ppm[1] + 0.5),
PNG_RESOLUTION_METER);
}
/* write the file header information */
png_write_info(png_ptr, info_ptr);
#ifdef __LITTLE_ENDIAN__
png_set_swap(png_ptr);
#endif
/* set the individual row-pointers to point at the correct offsets */
if (is_16bit) {
for (i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)((ushort *)pixels16 +
(((size_t)i) * ibuf->x) * bytesperpixel);
}
}
else {
for (i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) *
bytesperpixel *
sizeof(uchar));
}
}
/* write out the entire image data in one call */
png_write_image(png_ptr, row_pointers);
/* write the additional chunks to the PNG file (not really needed) */
png_write_end(png_ptr, info_ptr);
/* clean up */
if (pixels) {
MEM_freeN(pixels);
}
if (pixels16) {
MEM_freeN(pixels16);
}
MEM_freeN(row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
if (fp) {
fflush(fp);
fclose(fp);
}
return 1;
}
static void imb_png_warning(png_structp UNUSED(png_ptr), png_const_charp message)
{
/* We suppress iCCP warnings. That's how Blender always used to behave,
* and with new libpng it became too much picky, giving a warning on
* the splash screen even.
*/
if ((G.debug & G_DEBUG) == 0 && STRPREFIX(message, "iCCP")) {
return;
}
fprintf(stderr, "libpng warning: %s\n", message);
}
static void imb_png_error(png_structp UNUSED(png_ptr), png_const_charp message)
{
fprintf(stderr, "libpng error: %s\n", message);
}
ImBuf *imb_loadpng(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf = NULL;
png_structp png_ptr;
png_infop info_ptr;
uchar *pixels = NULL;
ushort *pixels16 = NULL;
png_bytepp row_pointers = NULL;
png_uint_32 width, height;
int bit_depth, color_type;
PNGReadStruct ps;
uchar *from, *to;
ushort *from16;
float *to_float;
uint channels;
if (imb_is_a_png(mem, size) == 0) {
return NULL;
}
/* both 8 and 16 bit PNGs are default to standard byte colorspace */
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
printf("Cannot png_create_read_struct\n");
return NULL;
}
png_set_error_fn(png_ptr, NULL, imb_png_error, imb_png_warning);
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
printf("Cannot png_create_info_struct\n");
return NULL;
}
ps.size = size; /* XXX, 4gig limit! */
ps.data = mem;
ps.seek = 0;
png_set_read_fn(png_ptr, (void *)&ps, ReadData);
if (setjmp(png_jmpbuf(png_ptr))) {
/* On error jump here, and free any resources. */
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
if (pixels) {
MEM_freeN(pixels);
}
if (pixels16) {
MEM_freeN(pixels16);
}
if (row_pointers) {
MEM_freeN(row_pointers);
}
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return NULL;
}
// png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
channels = png_get_channels(png_ptr, info_ptr);
switch (color_type) {
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
break;
case PNG_COLOR_TYPE_PALETTE:
png_set_palette_to_rgb(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
channels = 4;
}
else {
channels = 3;
}
break;
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_GRAY_ALPHA:
if (bit_depth < 8) {
png_set_expand(png_ptr);
bit_depth = 8;
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
/* PNG_COLOR_TYPE_GRAY may also have alpha 'values', like with palette. */
channels = 2;
}
}
break;
default:
printf("PNG format not supported\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
ibuf = IMB_allocImBuf(width, height, 8 * channels, 0);
if (ibuf) {
ibuf->ftype = IMB_FTYPE_PNG;
if (bit_depth == 16) {
ibuf->foptions.flag |= PNG_16BIT;
}
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
int unit_type;
png_uint_32 xres, yres;
if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
if (unit_type == PNG_RESOLUTION_METER) {
ibuf->ppm[0] = xres;
ibuf->ppm[1] = yres;
}
}
}
}
else {
printf("Couldn't allocate memory for PNG image\n");
}
if (ibuf && ((flags & IB_test) == 0)) {
if (bit_depth == 16) {
imb_addrectfloatImBuf(ibuf, 4);
png_set_swap(png_ptr);
pixels16 = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(png_uint_16), "pixels");
if (pixels16 == NULL || ibuf->rect_float == NULL) {
printf("Cannot allocate pixels array\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
/* allocate memory for an array of row-pointers */
row_pointers = (png_bytepp)MEM_mallocN((size_t)ibuf->y * sizeof(png_uint_16p),
"row_pointers");
if (row_pointers == NULL) {
printf("Cannot allocate row-pointers array\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
/* set the individual row-pointers to point at the correct offsets */
for (size_t i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)((png_uint_16 *)pixels16 +
(i * ibuf->x) * channels);
}
png_read_image(png_ptr, row_pointers);
/* copy image data */
to_float = ibuf->rect_float;
from16 = pixels16;
switch (channels) {
case 4:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to_float[0] = from16[0] / 65535.0;
to_float[1] = from16[1] / 65535.0;
to_float[2] = from16[2] / 65535.0;
to_float[3] = from16[3] / 65535.0;
to_float += 4;
from16 += 4;
}
break;
case 3:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to_float[0] = from16[0] / 65535.0;
to_float[1] = from16[1] / 65535.0;
to_float[2] = from16[2] / 65535.0;
to_float[3] = 1.0;
to_float += 4;
from16 += 3;
}
break;
case 2:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0;
to_float[3] = from16[1] / 65535.0;
to_float += 4;
from16 += 2;
}
break;
case 1:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0;
to_float[3] = 1.0;
to_float += 4;
from16++;
}
break;
}
}
else {
imb_addrectImBuf(ibuf);
pixels = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(uchar), "pixels");
if (pixels == NULL || ibuf->rect == NULL) {
printf("Cannot allocate pixels array\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
/* allocate memory for an array of row-pointers */
row_pointers = (png_bytepp)MEM_mallocN((size_t)ibuf->y * sizeof(png_bytep), "row_pointers");
if (row_pointers == NULL) {
printf("Cannot allocate row-pointers array\n");
longjmp(png_jmpbuf(png_ptr), 1);
}
/* set the individual row-pointers to point at the correct offsets */
for (int i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) *
channels *
sizeof(uchar));
}
png_read_image(png_ptr, row_pointers);
/* copy image data */
to = (uchar *)ibuf->rect;
from = pixels;
switch (channels) {
case 4:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to[3] = from[3];
to += 4;
from += 4;
}
break;
case 3:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to[3] = 0xff;
to += 4;
from += 3;
}
break;
case 2:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to[0] = to[1] = to[2] = from[0];
to[3] = from[1];
to += 4;
from += 2;
}
break;
case 1:
for (size_t i = (size_t)ibuf->x * (size_t)ibuf->y; i > 0; i--) {
to[0] = to[1] = to[2] = from[0];
to[3] = 0xff;
to += 4;
from++;
}
break;
}
}
if (flags & IB_metadata) {
png_text *text_chunks;
int count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL);
IMB_metadata_ensure(&ibuf->metadata);
for (int i = 0; i < count; i++) {
IMB_metadata_set_field(ibuf->metadata, text_chunks[i].key, text_chunks[i].text);
ibuf->flags |= IB_metadata;
}
}
png_read_end(png_ptr, info_ptr);
}
/* clean up */
if (pixels) {
MEM_freeN(pixels);
}
if (pixels16) {
MEM_freeN(pixels16);
}
if (row_pointers) {
MEM_freeN(row_pointers);
}
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
return ibuf;
}

View File

@ -1,438 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
* Radiance High Dynamic Range image file IO
* For description and code for reading/writing of radiance hdr files
* by Greg Ward, refer to:
* http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html
*/
#include "MEM_guardedalloc.h"
#include "BLI_fileops.h"
#include "BLI_utildefines.h"
#include "imbuf.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_allocimbuf.h"
#include "IMB_filetype.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
/* needed constants */
#define MINELEN 8
#define MAXELEN 0x7fff
#define MINRUN 4 /* minimum run length */
#define RED 0
#define GRN 1
#define BLU 2
#define EXP 3
#define COLXS 128
typedef uchar RGBE[4];
typedef float fCOLOR[3];
/* copy source -> dest */
#define COPY_RGBE(c1, c2) \
(c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP])
/* read routines */
static const uchar *oldreadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof)
{
size_t i, rshift = 0, len = xmax;
while (len > 0) {
if (UNLIKELY(mem_eof - mem < 4)) {
return NULL;
}
scan[0][RED] = *mem++;
scan[0][GRN] = *mem++;
scan[0][BLU] = *mem++;
scan[0][EXP] = *mem++;
if (scan[0][RED] == 1 && scan[0][GRN] == 1 && scan[0][BLU] == 1) {
for (i = scan[0][EXP] << rshift; i > 0 && len > 0; i--) {
COPY_RGBE(scan[-1], scan[0]);
scan++;
len--;
}
rshift += 8;
}
else {
scan++;
len--;
rshift = 0;
}
}
return mem;
}
static const uchar *freadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof)
{
if (UNLIKELY(mem_eof - mem < 4)) {
return NULL;
}
if (UNLIKELY((xmax < MINELEN) | (xmax > MAXELEN))) {
return oldreadcolrs(scan, mem, xmax, mem_eof);
}
int val = *mem++;
if (val != 2) {
return oldreadcolrs(scan, mem - 1, xmax, mem_eof);
}
scan[0][GRN] = *mem++;
scan[0][BLU] = *mem++;
val = *mem++;
if (scan[0][GRN] != 2 || scan[0][BLU] & 128) {
scan[0][RED] = 2;
scan[0][EXP] = val;
return oldreadcolrs(scan + 1, mem, xmax - 1, mem_eof);
}
if (UNLIKELY(((scan[0][BLU] << 8) | val) != xmax)) {
return NULL;
}
for (size_t i = 0; i < 4; i++) {
if (UNLIKELY(mem_eof - mem < 2)) {
return NULL;
}
for (size_t j = 0; j < xmax;) {
int code = *mem++;
if (code > 128) {
code &= 127;
if (UNLIKELY(code + j > xmax)) {
return NULL;
}
val = *mem++;
while (code--) {
scan[j++][i] = (uchar)val;
}
}
else {
if (UNLIKELY(mem_eof - mem < code)) {
return NULL;
}
if (UNLIKELY(code + j > xmax)) {
return NULL;
}
while (code--) {
scan[j++][i] = *mem++;
}
}
}
}
return mem;
}
/* helper functions */
/* rgbe -> float color */
static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
{
if (rgbe[EXP] == 0) {
fcol[RED] = fcol[GRN] = fcol[BLU] = 0;
}
else {
float f = ldexp(1.0, rgbe[EXP] - (COLXS + 8));
fcol[RED] = f * (rgbe[RED] + 0.5f);
fcol[GRN] = f * (rgbe[GRN] + 0.5f);
fcol[BLU] = f * (rgbe[BLU] + 0.5f);
}
}
/* float color -> rgbe */
static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
{
int e;
float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
if (fcol[BLU] > d) {
d = fcol[BLU];
}
if (d <= 1e-32f) {
rgbe[RED] = rgbe[GRN] = rgbe[BLU] = rgbe[EXP] = 0;
}
else {
d = (float)frexp(d, &e) * 256.0f / d;
rgbe[RED] = (uchar)(fcol[RED] * d);
rgbe[GRN] = (uchar)(fcol[GRN] * d);
rgbe[BLU] = (uchar)(fcol[BLU] * d);
rgbe[EXP] = (uchar)(e + COLXS);
}
}
/* ImBuf read */
bool imb_is_a_hdr(const uchar *buf, const size_t size)
{
/* NOTE: `#?RADIANCE` is used by other programs such as `ImageMagik`,
* Although there are some files in the wild that only use `#?` (from looking online).
* If this is ever a problem we could check for the longer header since this is part of the spec.
*
* We could check `32-bit_rle_rgbe` or `32-bit_rle_xyze` too since this is part of the format.
* Currently this isn't needed.
*
* See: http://paulbourke.net/dataformats/pic/
*/
const uchar magic[2] = {'#', '?'};
if (size < sizeof(magic)) {
return false;
}
return memcmp(buf, magic, sizeof(magic)) == 0;
}
struct ImBuf *imb_loadhdr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf;
RGBE *sline;
fCOLOR fcol;
float *rect_float;
int found = 0;
int width = 0, height = 0;
const uchar *ptr, *mem_eof = mem + size;
char oriY[3], oriX[3];
if (!imb_is_a_hdr(mem, size)) {
return NULL;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
/* find empty line, next line is resolution info */
size_t x;
for (x = 1; x < size; x++) {
if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
found = 1;
break;
}
}
if ((found && (x < (size - 1))) == 0) {
/* Data not found! */
return NULL;
}
x++;
/* sscanf requires a null-terminated buffer argument */
char buf[32] = {0};
memcpy(buf, &mem[x], MIN2(sizeof(buf) - 1, size - x));
if (sscanf(buf, "%2s %d %2s %d", (char *)&oriY, &height, (char *)&oriX, &width) != 4) {
return NULL;
}
if (width < 1 || height < 1) {
return NULL;
}
/* Checking that width x height does not extend past mem_eof is not easily possible
* since the format uses RLE compression. Can cause excessive memory allocation to occur. */
/* find end of this line, data right behind it */
ptr = (const uchar *)strchr((const char *)&mem[x], '\n');
if (ptr == NULL || ptr >= mem_eof) {
return NULL;
}
ptr++;
if (flags & IB_test) {
ibuf = IMB_allocImBuf(width, height, 32, 0);
}
else {
ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
}
if (UNLIKELY(ibuf == NULL)) {
return NULL;
}
ibuf->ftype = IMB_FTYPE_RADHDR;
if (flags & IB_alphamode_detect) {
ibuf->flags |= IB_alphamode_premul;
}
if (flags & IB_test) {
return ibuf;
}
/* read in and decode the actual data */
sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
rect_float = ibuf->rect_float;
for (size_t y = 0; y < height; y++) {
ptr = freadcolrs(sline, ptr, width, mem_eof);
if (ptr == NULL) {
printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
break;
}
for (x = 0; x < width; x++) {
/* Convert to LDR. */
RGBE2FLOAT(sline[x], fcol);
*rect_float++ = fcol[RED];
*rect_float++ = fcol[GRN];
*rect_float++ = fcol[BLU];
*rect_float++ = 1.0f;
}
}
MEM_freeN(sline);
if (oriY[0] == '-') {
IMB_flipy(ibuf);
}
if (flags & IB_rect) {
IMB_rect_from_float(ibuf);
}
return ibuf;
}
/* ImBuf write */
static int fwritecolrs(
FILE *file, int width, int channels, const uchar *ibufscan, const float *fpscan)
{
int beg, c2, count = 0;
fCOLOR fcol;
RGBE rgbe, *rgbe_scan;
if (UNLIKELY((ibufscan == NULL) && (fpscan == NULL))) {
return 0;
}
rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan");
/* Convert scan-line. */
for (size_t i = 0, j = 0; i < width; i++) {
if (fpscan) {
fcol[RED] = fpscan[j];
fcol[GRN] = (channels >= 2) ? fpscan[j + 1] : fpscan[j];
fcol[BLU] = (channels >= 3) ? fpscan[j + 2] : fpscan[j];
}
else {
fcol[RED] = (float)ibufscan[j] / 255.0f;
fcol[GRN] = (float)((channels >= 2) ? ibufscan[j + 1] : ibufscan[j]) / 255.0f;
fcol[BLU] = (float)((channels >= 3) ? ibufscan[j + 2] : ibufscan[j]) / 255.0f;
}
FLOAT2RGBE(fcol, rgbe);
COPY_RGBE(rgbe, rgbe_scan[i]);
j += channels;
}
if ((width < MINELEN) | (width > MAXELEN)) { /* OOBs, write out flat */
int x = fwrite((char *)rgbe_scan, sizeof(RGBE), width, file) - width;
MEM_freeN(rgbe_scan);
return x;
}
/* put magic header */
putc(2, file);
putc(2, file);
putc((uchar)(width >> 8), file);
putc((uchar)(width & 255), file);
/* put components separately */
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < width; j += count) { /* find next run */
for (beg = j; beg < width; beg += count) {
for (count = 1; (count < 127) && ((beg + count) < width) &&
(rgbe_scan[beg + count][i] == rgbe_scan[beg][i]);
count++) {
/* pass */
}
if (count >= MINRUN) {
break; /* long enough */
}
}
if (((beg - j) > 1) && ((beg - j) < MINRUN)) {
c2 = j + 1;
while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) {
if (c2 == beg) { /* short run */
putc((uchar)(128 + beg - j), file);
putc((uchar)(rgbe_scan[j][i]), file);
j = beg;
break;
}
}
}
while (j < beg) { /* write out non-run */
if ((c2 = beg - j) > 128) {
c2 = 128;
}
putc((uchar)(c2), file);
while (c2--) {
putc(rgbe_scan[j++][i], file);
}
}
if (count >= MINRUN) { /* write out run */
putc((uchar)(128 + count), file);
putc(rgbe_scan[beg][i], file);
}
else {
count = 0;
}
}
}
MEM_freeN(rgbe_scan);
return (ferror(file) ? -1 : 0);
}
static void writeHeader(FILE *file, int width, int height)
{
fprintf(file, "#?RADIANCE");
fputc(10, file);
fprintf(file, "# %s", "Created with Blender");
fputc(10, file);
fprintf(file, "EXPOSURE=%25.13f", 1.0);
fputc(10, file);
fprintf(file, "FORMAT=32-bit_rle_rgbe");
fputc(10, file);
fputc(10, file);
fprintf(file, "-Y %d +X %d", height, width);
fputc(10, file);
}
bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
{
FILE *file = BLI_fopen(filepath, "wb");
float *fp = NULL;
size_t width = ibuf->x, height = ibuf->y;
uchar *cp = NULL;
(void)flags; /* unused */
if (file == NULL) {
return 0;
}
writeHeader(file, width, height);
if (ibuf->rect) {
cp = (uchar *)ibuf->rect + ibuf->channels * (height - 1) * width;
}
if (ibuf->rect_float) {
fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
}
for (size_t y = 0; y < height; y++) {
if (fwritecolrs(file, width, ibuf->channels, cp, fp) < 0) {
fclose(file);
printf("HDR write error\n");
return 0;
}
if (cp) {
cp -= ibuf->channels * width;
}
if (fp) {
fp -= ibuf->channels * width;
}
}
fclose(file);
return 1;
}

View File

@ -1,791 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup imbuf
*/
#ifdef WIN32
# include <io.h>
#endif
#include "BLI_fileops.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
#include "imbuf.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_filetype.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
/* this one is only def-ed once, strangely... related to GS? */
#define GSS(x) (((uchar *)(x))[1] << 8 | ((uchar *)(x))[0])
/***/
typedef struct TARGA {
uchar numid;
uchar maptyp;
uchar imgtyp;
short maporig;
short mapsize;
uchar mapbits;
short xorig;
short yorig;
short xsize;
short ysize;
uchar pixsize;
uchar imgdes;
} TARGA;
/**
* On-disk header size.
*
* \note In theory it's possible padding would make the struct and on-disk size differ,
* so use a constant instead of `sizeof(TARGA)`.
*/
#define TARGA_HEADER_SIZE 18
/***/
static int tga_out1(uint data, FILE *file)
{
uchar *p;
p = (uchar *)&data;
if (putc(p[0], file) == EOF) {
return EOF;
}
return ~EOF;
}
static int tga_out2(uint data, FILE *file)
{
uchar *p;
p = (uchar *)&data;
if (putc(p[0], file) == EOF) {
return EOF;
}
if (putc(p[1], file) == EOF) {
return EOF;
}
return ~EOF;
}
static int tga_out3(uint data, FILE *file)
{
uchar *p;
p = (uchar *)&data;
if (putc(p[2], file) == EOF) {
return EOF;
}
if (putc(p[1], file) == EOF) {
return EOF;
}
if (putc(p[0], file) == EOF) {
return EOF;
}
return ~EOF;
}
static int tga_out4(uint data, FILE *file)
{
uchar *p;
p = (uchar *)&data;
/* Order = BGRA. */
if (putc(p[2], file) == EOF) {
return EOF;
}
if (putc(p[1], file) == EOF) {
return EOF;
}
if (putc(p[0], file) == EOF) {
return EOF;
}
if (putc(p[3], file) == EOF) {
return EOF;
}
return ~EOF;
}
static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(uint, FILE *))
{
int last, this;
int copy, bytes;
uint *rect, *rectstart, *temp;
int y;
for (y = 0; y < ibuf->y; y++) {
bytes = ibuf->x - 1;
rectstart = rect = ibuf->rect + (y * ibuf->x);
last = *rect++;
this = *rect++;
copy = last ^ this;
while (bytes > 0) {
if (copy) {
do {
last = this;
this = *rect++;
if (last == this) {
if (this == rect[-3]) { /* three the same? */
bytes--; /* set bytes */
break;
}
}
} while (--bytes != 0);
copy = rect - rectstart;
copy--;
if (bytes) {
copy -= 2;
}
temp = rect;
rect = rectstart;
while (copy) {
last = copy;
if (copy >= 128) {
last = 128;
}
copy -= last;
if (fputc(last - 1, file) == EOF) {
return 0;
}
do {
if (out(*rect++, file) == EOF) {
return 0;
}
} while (--last != 0);
}
rectstart = rect;
rect = temp;
last = this;
copy = 0;
}
else {
while (*rect++ == this) { /* seek for first different byte */
if (--bytes == 0) {
break; /* Or end of line. */
}
}
rect--;
copy = rect - rectstart;
rectstart = rect;
bytes--;
this = *rect++;
while (copy) {
if (copy > 128) {
if (fputc(255, file) == EOF) {
return 0;
}
copy -= 128;
}
else {
if (copy == 1) {
if (fputc(0, file) == EOF) {
return 0;
}
}
else if (fputc(127 + copy, file) == EOF) {
return 0;
}
copy = 0;
}
if (out(last, file) == EOF) {
return 0;
}
}
copy = 1;
}
}
}
return 1;
}
static bool dumptarga(struct ImBuf *ibuf, FILE *file)
{
int size;
uchar *rect;
if (ibuf == NULL) {
return 0;
}
if (ibuf->rect == NULL) {
return 0;
}
size = ibuf->x * ibuf->y;
rect = (uchar *)ibuf->rect;
if (ibuf->planes <= 8) {
while (size > 0) {
if (putc(*rect, file) == EOF) {
return 0;
}
size--;
rect += 4;
}
}
else if (ibuf->planes <= 16) {
while (size > 0) {
putc(rect[0], file);
if (putc(rect[1], file) == EOF) {
return 0;
}
size--;
rect += 4;
}
}
else if (ibuf->planes <= 24) {
while (size > 0) {
putc(rect[2], file);
putc(rect[1], file);
if (putc(rect[0], file) == EOF) {
return 0;
}
size--;
rect += 4;
}
}
else if (ibuf->planes <= 32) {
while (size > 0) {
putc(rect[2], file);
putc(rect[1], file);
putc(rect[0], file);
if (putc(rect[3], file) == EOF) {
return 0;
}
size--;
rect += 4;
}
}
else {
return 0;
}
return 1;
}
bool imb_savetarga(struct ImBuf *ibuf, const char *filepath, int UNUSED(flags))
{
char buf[TARGA_HEADER_SIZE] = {0};
FILE *fildes;
bool ok = false;
buf[16] = (ibuf->planes + 0x7) & ~0x7;
if (ibuf->planes > 8) {
buf[2] = 10;
}
else {
buf[2] = 11;
}
if (ibuf->foptions.flag & RAWTGA) {
buf[2] &= ~8;
}
buf[8] = 0;
buf[9] = 0;
buf[10] = 0;
buf[11] = 0;
buf[12] = ibuf->x & 0xff;
buf[13] = ibuf->x >> 8;
buf[14] = ibuf->y & 0xff;
buf[15] = ibuf->y >> 8;
/* Don't forget to indicate that your 32 bit
* targa uses 8 bits for the alpha channel! */
if (ibuf->planes == 32) {
buf[17] |= 0x08;
}
fildes = BLI_fopen(filepath, "wb");
if (!fildes) {
return 0;
}
if (fwrite(buf, 1, TARGA_HEADER_SIZE, fildes) != TARGA_HEADER_SIZE) {
fclose(fildes);
return 0;
}
if (ibuf->foptions.flag & RAWTGA) {
ok = dumptarga(ibuf, fildes);
}
else {
switch ((ibuf->planes + 7) >> 3) {
case 1:
ok = makebody_tga(ibuf, fildes, tga_out1);
break;
case 2:
ok = makebody_tga(ibuf, fildes, tga_out2);
break;
case 3:
ok = makebody_tga(ibuf, fildes, tga_out3);
break;
case 4:
ok = makebody_tga(ibuf, fildes, tga_out4);
break;
}
}
fclose(fildes);
return ok;
}
static bool checktarga(TARGA *tga, const uchar *mem, const size_t size)
{
if (size < TARGA_HEADER_SIZE) {
return false;
}
tga->numid = mem[0];
tga->maptyp = mem[1];
tga->imgtyp = mem[2];
tga->maporig = GSS(mem + 3);
tga->mapsize = GSS(mem + 5);
tga->mapbits = mem[7];
tga->xorig = GSS(mem + 8);
tga->yorig = GSS(mem + 10);
tga->xsize = GSS(mem + 12);
tga->ysize = GSS(mem + 14);
tga->pixsize = mem[16];
tga->imgdes = mem[17];
if (tga->maptyp > 1) {
return false;
}
switch (tga->imgtyp) {
case 1: /* raw cmap */
case 2: /* raw rgb */
case 3: /* raw b&w */
case 9: /* cmap */
case 10: /* rgb */
case 11: /* b&w */
break;
default:
return false;
}
if (tga->mapsize && tga->mapbits > 32) {
return false;
}
if (tga->xsize <= 0) {
return false;
}
if (tga->ysize <= 0) {
return false;
}
if (tga->pixsize > 32) {
return false;
}
if (tga->pixsize == 0) {
return false;
}
return true;
}
bool imb_is_a_targa(const uchar *buf, size_t size)
{
TARGA tga;
return checktarga(&tga, buf, size);
}
static void complete_partial_load(struct ImBuf *ibuf, uint *rect)
{
int size = (ibuf->x * ibuf->y) - (rect - ibuf->rect);
if (size) {
printf("decodetarga: incomplete file, %.1f%% missing\n",
100 * ((float)size / (ibuf->x * ibuf->y)));
/* Not essential but makes displaying partially rendered TGA's less ugly. */
memset(rect, 0, size);
}
else {
/* shouldn't happen */
printf("decodetarga: incomplete file, all pixels written\n");
}
}
static void decodetarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize)
{
const uchar *mem_end = mem + mem_size;
int count, col, size;
uint *rect;
uchar *cp = (uchar *)&col;
if (ibuf == NULL) {
return;
}
if (ibuf->rect == NULL) {
return;
}
size = ibuf->x * ibuf->y;
rect = ibuf->rect;
/* set alpha */
cp[0] = 0xff;
cp[1] = cp[2] = 0;
while (size > 0) {
count = *mem++;
if (mem > mem_end) {
goto partial_load;
}
if (count >= 128) {
// if (count == 128) printf("TARGA: 128 in file !\n");
count -= 127;
if (psize & 2) {
if (psize & 1) {
/* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = (mem[3] << 24) + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 4;
}
else {
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = 0xff000000 + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 3;
}
}
else {
if (psize & 1) {
cp[0] = mem[0];
cp[1] = mem[1];
mem += 2;
}
else {
col = *mem++;
}
}
size -= count;
if (size >= 0) {
while (count > 0) {
*rect++ = col;
count--;
}
}
}
else {
count++;
size -= count;
if (size >= 0) {
while (count > 0) {
if (psize & 2) {
if (psize & 1) {
/* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = (mem[3] << 24) + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 4;
}
else {
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = 0xff000000 + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 3;
}
}
else {
if (psize & 1) {
cp[0] = mem[0];
cp[1] = mem[1];
mem += 2;
}
else {
col = *mem++;
}
}
*rect++ = col;
count--;
if (mem > mem_end) {
goto partial_load;
}
}
if (mem > mem_end) {
goto partial_load;
}
}
}
}
if (size) {
printf("decodetarga: count would overwrite %d pixels\n", -size);
}
return;
partial_load:
complete_partial_load(ibuf, rect);
}
static void ldtarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize)
{
const uchar *mem_end = mem + mem_size;
int col, size;
uint *rect;
uchar *cp = (uchar *)&col;
if (ibuf == NULL) {
return;
}
if (ibuf->rect == NULL) {
return;
}
size = ibuf->x * ibuf->y;
rect = ibuf->rect;
/* set alpha */
cp[0] = 0xff;
cp[1] = cp[2] = 0;
while (size > 0) {
if (mem > mem_end) {
goto partial_load;
}
if (psize & 2) {
if (psize & 1) {
/* Order = BGRA. */
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = (mem[3] << 24) + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 4;
}
else {
/* set alpha for 24 bits colors */
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
// col = 0xff000000 + (mem[0] << 16) + (mem[1] << 8) + mem[2];
mem += 3;
}
}
else {
if (psize & 1) {
cp[0] = mem[0];
cp[1] = mem[1];
mem += 2;
}
else {
col = *mem++;
}
}
*rect++ = col;
size--;
}
return;
partial_load:
complete_partial_load(ibuf, rect);
}
ImBuf *imb_loadtarga(const uchar *mem, size_t mem_size, int flags, char colorspace[IM_MAX_SPACE])
{
TARGA tga;
struct ImBuf *ibuf;
int count, size;
uint *rect, *cmap = NULL /*, mincol = 0*/, cmap_max = 0;
int32_t cp_data;
uchar *cp = (uchar *)&cp_data;
if (checktarga(&tga, mem, mem_size) == 0) {
return NULL;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
if (flags & IB_test) {
ibuf = IMB_allocImBuf(tga.xsize, tga.ysize, tga.pixsize, 0);
}
else {
ibuf = IMB_allocImBuf(tga.xsize, tga.ysize, (tga.pixsize + 0x7) & ~0x7, IB_rect);
}
if (ibuf == NULL) {
return NULL;
}
ibuf->ftype = IMB_FTYPE_TGA;
if (tga.imgtyp < 4) {
ibuf->foptions.flag |= RAWTGA;
}
mem = mem + TARGA_HEADER_SIZE + tga.numid;
cp[0] = 0xff;
cp[1] = cp[2] = 0;
if (tga.mapsize) {
/* Load color map. */
// mincol = tga.maporig; /* UNUSED */
cmap_max = tga.mapsize;
cmap = MEM_callocN(sizeof(uint) * cmap_max, "targa cmap");
for (count = 0; count < cmap_max; count++) {
switch (tga.mapbits >> 3) {
case 4:
cp[0] = mem[3];
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
mem += 4;
break;
case 3:
cp[1] = mem[0];
cp[2] = mem[1];
cp[3] = mem[2];
mem += 3;
break;
case 2:
cp[1] = mem[1];
cp[0] = mem[0];
mem += 2;
break;
case 1:
cp_data = *mem++;
break;
}
cmap[count] = cp_data;
}
ibuf->planes = tga.mapbits;
if (tga.mapbits != 32) { /* Set alpha bits. */
cmap[0] &= BIG_LONG(0x00ffffffl);
}
}
if (flags & IB_test) {
if (cmap) {
MEM_freeN(cmap);
}
return ibuf;
}
if (!ELEM(tga.imgtyp, 1, 9)) { /* happens sometimes (ugh) */
if (cmap) {
MEM_freeN(cmap);
cmap = NULL;
}
}
switch (tga.imgtyp) {
case 1:
case 2:
case 3:
if (tga.pixsize <= 8) {
ldtarga(ibuf, mem, mem_size, 0);
}
else if (tga.pixsize <= 16) {
ldtarga(ibuf, mem, mem_size, 1);
}
else if (tga.pixsize <= 24) {
ldtarga(ibuf, mem, mem_size, 2);
}
else if (tga.pixsize <= 32) {
ldtarga(ibuf, mem, mem_size, 3);
}
break;
case 9:
case 10:
case 11:
if (tga.pixsize <= 8) {
decodetarga(ibuf, mem, mem_size, 0);
}
else if (tga.pixsize <= 16) {
decodetarga(ibuf, mem, mem_size, 1);
}
else if (tga.pixsize <= 24) {
decodetarga(ibuf, mem, mem_size, 2);
}
else if (tga.pixsize <= 32) {
decodetarga(ibuf, mem, mem_size, 3);
}
break;
}
if (cmap) {
/* apply color map */
rect = ibuf->rect;
for (size = ibuf->x * ibuf->y; size > 0; size--, rect++) {
int cmap_index = *rect;
if (cmap_index >= 0 && cmap_index < cmap_max) {
*rect = cmap[cmap_index];
}
}
MEM_freeN(cmap);
}
if (tga.pixsize == 16) {
uint col;
rect = ibuf->rect;
for (size = ibuf->x * ibuf->y; size > 0; size--, rect++) {
col = *rect;
cp = (uchar *)rect;
mem = (uchar *)&col;
cp[3] = ((mem[1] << 1) & 0xf8);
cp[2] = ((mem[0] & 0xe0) >> 2) + ((mem[1] & 0x03) << 6);
cp[1] = ((mem[0] << 3) & 0xf8);
cp[1] += cp[1] >> 5;
cp[2] += cp[2] >> 5;
cp[3] += cp[3] >> 5;
cp[0] = 0xff;
}
ibuf->planes = 24;
}
if (ELEM(tga.imgtyp, 3, 11)) {
uchar *crect;
uint *lrect, col;
crect = (uchar *)ibuf->rect;
lrect = (uint *)ibuf->rect;
for (size = ibuf->x * ibuf->y; size > 0; size--) {
col = *lrect++;
crect[0] = 255;
crect[1] = crect[2] = crect[3] = col;
crect += 4;
}
}
if (tga.imgdes & 0x20) {
IMB_flipy(ibuf);
}
if (ibuf->rect) {
IMB_convert_rgba_to_abgr(ibuf);
}
return ibuf;
}

View File

@ -1,832 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*/
/**
* Provides TIFF file loading and saving for Blender, via libtiff.
*
* The task of loading is complicated somewhat by the fact that Blender has
* already loaded the file into a memory buffer. libtiff is not well
* configured to handle files in memory, so a client wrapper is written to
* surround the memory and turn it into a virtual file. Currently, reading
* of TIFF files is done using libtiff's RGBAImage support. This is a
* high-level routine that loads all images as 32-bit RGBA, handling all the
* required conversions between many different TIFF types internally.
*
* Saving supports RGB, RGBA and BW (gray-scale) images correctly, with
* 8 bits per channel in all cases. The "deflate" compression algorithm is
* used to compress images.
*/
#include <string.h>
#include "imbuf.h"
#include "BLI_endian_defines.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_global.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_filetype.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
#include <tiffio.h>
#ifdef WIN32
# include "utfconv.h"
#endif
/* -------------------------------------------------------------------- */
/** \name Local Declarations
* \{ */
/* Reading and writing of an in-memory TIFF file. */
static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n);
static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n);
static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence);
static int imb_tiff_CloseProc(thandle_t handle);
static toff_t imb_tiff_SizeProc(thandle_t handle);
static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize);
static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size);
/** Structure for in-memory TIFF file. */
typedef struct ImbTIFFMemFile {
/** Location of first byte of TIFF file. */
const uchar *mem;
/** Current offset within the file. */
toff_t offset;
/** Size of the TIFF file. */
tsize_t size;
} ImbTIFFMemFile;
#define IMB_TIFF_GET_MEMFILE(x) ((ImbTIFFMemFile *)(x))
/** \} */
/* -------------------------------------------------------------------- */
/** \name Function Implementations
* \{ */
static void imb_tiff_DummyUnmapProc(
thandle_t fd,
tdata_t base,
/* Cannot be const, because this function implements #TIFFUnmapFileProc.
* NOLINTNEXTLINE: readability-non-const-parameter. */
toff_t size)
{
(void)fd;
(void)base;
(void)size;
}
static int imb_tiff_DummyMapProc(
thandle_t fd,
tdata_t *pbase,
/* Cannot be const, because this function implements #TIFFMapFileProc.
* NOLINTNEXTLINE: readability-non-const-parameter. */
toff_t *psize)
{
(void)fd;
(void)pbase;
(void)psize;
return 0;
}
/**
* Reads data from an in-memory TIFF file.
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
* \param data: Buffer to contain data (treat as (void *)).
* \param n: Number of bytes to read.
*
* \return Number of bytes actually read.
* 0 = EOF.
*/
static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n)
{
tsize_t nRemaining, nCopy;
ImbTIFFMemFile *mfile;
void *srcAddr;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_ReadProc: !mfile || !mfile->mem!\n");
return 0;
}
/* find the actual number of bytes to read (copy) */
nCopy = n;
if ((tsize_t)mfile->offset >= mfile->size) {
nRemaining = 0;
}
else {
nRemaining = mfile->size - mfile->offset;
}
if (nCopy > nRemaining) {
nCopy = nRemaining;
}
/* on EOF, return immediately and read (copy) nothing */
if (nCopy <= 0) {
return 0;
}
/* all set -> do the read (copy) */
srcAddr = (void *)&(mfile->mem[mfile->offset]);
memcpy((void *)data, srcAddr, nCopy);
mfile->offset += nCopy; /* advance file ptr by copied bytes */
return nCopy;
}
/**
* Writes data to an in-memory TIFF file.
*
* NOTE: The current Blender implementation should not need this function.
* It is simply a stub.
*/
static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n)
{
(void)handle;
(void)data;
(void)n;
printf("imb_tiff_WriteProc: this function should not be called.\n");
return (-1);
}
/**
* Seeks to a new location in an in-memory TIFF file.
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
* \param ofs: Offset value (interpreted according to whence below).
* \param whence: This can be one of three values:
* SEEK_SET - The offset is set to ofs bytes.
* SEEK_CUR - The offset is set to its current location plus ofs bytes.
* SEEK_END - (This is unsupported and will return -1, indicating an
* error).
*
* \return Resulting offset location within the file, measured in bytes from
* the beginning of the file. (-1) indicates an error.
*/
static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence)
{
ImbTIFFMemFile *mfile;
toff_t new_offset;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_SeekProc: !mfile || !mfile->mem!\n");
return (-1);
}
/* find the location we plan to seek to */
switch (whence) {
case SEEK_SET:
new_offset = ofs;
break;
case SEEK_CUR:
new_offset = mfile->offset + ofs;
break;
default:
/* no other types are supported - return an error */
fprintf(stderr,
"imb_tiff_SeekProc: "
"Unsupported TIFF SEEK type.\n");
return (-1);
}
/* set the new location */
mfile->offset = new_offset;
return mfile->offset;
}
/**
* Closes (virtually) an in-memory TIFF file.
*
* NOTE: All this function actually does is sets the data pointer within the
* TIFF file to NULL. That should trigger assertion errors if attempts
* are made to access the file after that point. However, no such
* attempts should ever be made (in theory).
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
*
* \return 0
*/
static int imb_tiff_CloseProc(thandle_t handle)
{
ImbTIFFMemFile *mfile;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_CloseProc: !mfile || !mfile->mem!\n");
return 0;
}
/* virtually close the file */
mfile->mem = NULL;
mfile->offset = 0;
mfile->size = 0;
return 0;
}
/**
* Returns the size of an in-memory TIFF file in bytes.
*
* \return Size of file (in bytes).
*/
static toff_t imb_tiff_SizeProc(thandle_t handle)
{
ImbTIFFMemFile *mfile;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_SizeProc: !mfile || !mfile->mem!\n");
return 0;
}
/* return the size */
return (toff_t)(mfile->size);
}
static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const uchar *mem, size_t size)
{
/* open the TIFF client layer interface to the in-memory file */
memFile->mem = mem;
memFile->offset = 0;
memFile->size = size;
return TIFFClientOpen("(Blender TIFF Interface Layer)",
"r",
(thandle_t)(memFile),
imb_tiff_ReadProc,
imb_tiff_WriteProc,
imb_tiff_SeekProc,
imb_tiff_CloseProc,
imb_tiff_SizeProc,
imb_tiff_DummyMapProc,
imb_tiff_DummyUnmapProc);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Load TIFF
* \{ */
/**
* Checks whether a given memory buffer contains a TIFF file.
*
* This method uses the format identifiers from:
* http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-9.html
* The first four bytes of big-endian and little-endian TIFF files
* respectively are (hex):
* 4d 4d 00 2a
* 49 49 2a 00
* Note that TIFF files on *any* platform can be either big- or little-endian;
* it's not platform-specific.
*
* AFAICT, libtiff doesn't provide a method to do this automatically, and
* hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005.
*/
#define IMB_TIFF_NCB 4 /* number of comparison bytes used */
bool imb_is_a_tiff(const uchar *buf, size_t size)
{
const char big_endian[IMB_TIFF_NCB] = {0x4d, 0x4d, 0x00, 0x2a};
const char lil_endian[IMB_TIFF_NCB] = {0x49, 0x49, 0x2a, 0x00};
if (size < IMB_TIFF_NCB) {
return false;
}
return ((memcmp(big_endian, buf, IMB_TIFF_NCB) == 0) ||
(memcmp(lil_endian, buf, IMB_TIFF_NCB) == 0));
}
static void scanline_contig_16bit(float *rectf, const ushort *sbuf, int scanline_w, int spp)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + 0] = sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 1] = (spp >= 3) ? sbuf[i * spp + 1] / 65535.0 : sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 2] = (spp >= 3) ? sbuf[i * spp + 2] / 65535.0 : sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 3] = (spp == 4) ? (sbuf[i * spp + 3] / 65535.0) : 1.0;
}
}
static void scanline_contig_32bit(float *rectf, const float *fbuf, int scanline_w, int spp)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + 0] = fbuf[i * spp + 0];
rectf[i * 4 + 1] = (spp >= 3) ? fbuf[i * spp + 1] : fbuf[i * spp + 0];
rectf[i * 4 + 2] = (spp >= 3) ? fbuf[i * spp + 2] : fbuf[i * spp + 0];
rectf[i * 4 + 3] = (spp == 4) ? fbuf[i * spp + 3] : 1.0f;
}
}
static void scanline_separate_16bit(float *rectf, const ushort *sbuf, int scanline_w, int chan)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + chan] = sbuf[i] / 65535.0;
}
}
static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanline_w, int chan)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + chan] = fbuf[i];
}
}
static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image)
{
uint16_t unit;
float xres;
float yres;
TIFFGetFieldDefaulted(image, TIFFTAG_RESOLUTIONUNIT, &unit);
TIFFGetFieldDefaulted(image, TIFFTAG_XRESOLUTION, &xres);
TIFFGetFieldDefaulted(image, TIFFTAG_YRESOLUTION, &yres);
if (unit == RESUNIT_CENTIMETER) {
ibuf->ppm[0] = (double)xres * 100.0;
ibuf->ppm[1] = (double)yres * 100.0;
}
else {
ibuf->ppm[0] = (double)xres / 0.0254;
ibuf->ppm[1] = (double)yres / 0.0254;
}
}
/*
* Use the libTIFF scanline API to read a TIFF image.
* This method is most flexible and can handle multiple different bit depths
* and RGB channel orderings.
*/
static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
{
ImBuf *tmpibuf = NULL;
int success = 0;
short bitspersample, spp, config;
size_t scanline;
int ib_flag = 0, row, chan;
float *fbuf = NULL;
ushort *sbuf = NULL;
TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp); /* number of 'channels' */
TIFFGetField(image, TIFFTAG_PLANARCONFIG, &config);
if (spp == 4) {
/* HACK: this is really tricky hack, which is only needed to force libtiff
* do not touch RGB channels when there's alpha channel present
* The thing is: libtiff will premul RGB if alpha mode is set to
* unassociated, which really conflicts with blender's assumptions
*
* Alternative would be to unpremul after load, but it'll be really
* lossy and unwanted behavior
*
* So let's keep this thing here for until proper solution is found (sergey)
*/
ushort extraSampleTypes[1];
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
}
imb_read_tiff_resolution(ibuf, image);
scanline = TIFFScanlineSize(image);
if (bitspersample == 32) {
ib_flag = IB_rectfloat;
fbuf = (float *)_TIFFmalloc(scanline);
if (!fbuf) {
goto cleanup;
}
}
else if (bitspersample == 16) {
ib_flag = IB_rectfloat;
sbuf = (ushort *)_TIFFmalloc(scanline);
if (!sbuf) {
goto cleanup;
}
}
else {
ib_flag = IB_rect;
}
tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ib_flag);
if (!tmpibuf) {
goto cleanup;
}
/* simple RGBA image */
if (!ELEM(bitspersample, 32, 16)) {
success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0);
}
/* contiguous channels: RGBRGBRGB */
else if (config == PLANARCONFIG_CONTIG) {
for (row = 0; row < ibuf->y; row++) {
size_t ib_offset = (size_t)ibuf->x * 4 * ((size_t)ibuf->y - ((size_t)row + 1));
if (bitspersample == 32) {
success |= TIFFReadScanline(image, fbuf, row, 0);
scanline_contig_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, spp);
}
else if (bitspersample == 16) {
success |= TIFFReadScanline(image, sbuf, row, 0);
scanline_contig_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, spp);
}
}
/* Separate channels: RRRGGGBBB. */
}
else if (config == PLANARCONFIG_SEPARATE) {
/* imbufs always have 4 channels of data, so we iterate over all of them
* but only fill in from the TIFF scanline where necessary. */
for (chan = 0; chan < 4; chan++) {
for (row = 0; row < ibuf->y; row++) {
size_t ib_offset = (size_t)ibuf->x * 4 * ((size_t)ibuf->y - ((size_t)row + 1));
if (bitspersample == 32) {
if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */
copy_vn_fl(fbuf, ibuf->x, 1.0f);
}
else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */
success |= TIFFReadScanline(image, fbuf, row, 0);
}
else {
success |= TIFFReadScanline(image, fbuf, row, chan);
}
scanline_separate_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, chan);
}
else if (bitspersample == 16) {
if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */
copy_vn_ushort(sbuf, ibuf->x, 65535);
}
else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */
success |= TIFFReadScanline(image, fbuf, row, 0);
}
else {
success |= TIFFReadScanline(image, sbuf, row, chan);
}
scanline_separate_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, chan);
}
}
}
}
if (success) {
/* Code seems to be not needed for 16 bits TIFF, on PPC G5 OSX (ton) */
if (bitspersample < 16) {
if (ENDIAN_ORDER == B_ENDIAN) {
IMB_convert_rgba_to_abgr(tmpibuf);
}
}
/* assign rect last */
if (tmpibuf->rect_float) {
ibuf->rect_float = tmpibuf->rect_float;
}
else {
ibuf->rect = tmpibuf->rect;
}
ibuf->mall |= ib_flag;
ibuf->flags |= ib_flag;
tmpibuf->mall &= ~ib_flag;
}
cleanup:
if (bitspersample == 32) {
_TIFFfree(fbuf);
}
else if (bitspersample == 16) {
_TIFFfree(sbuf);
}
IMB_freeImBuf(tmpibuf);
return success;
}
void imb_inittiff(void)
{
if (!(G.debug & G_DEBUG)) {
TIFFSetErrorHandler(NULL);
}
}
ImBuf *imb_loadtiff(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
TIFF *image = NULL;
ImBuf *ibuf = NULL;
ImbTIFFMemFile memFile;
uint32_t width, height;
short spp;
int ib_depth;
/* Check whether or not we have a TIFF file. */
if (imb_is_a_tiff(mem, size) == 0) {
return NULL;
}
/* both 8 and 16 bit PNGs are default to standard byte colorspace */
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
image = imb_tiff_client_open(&memFile, mem, size);
if (image == NULL) {
printf("imb_loadtiff: could not open TIFF IO layer.\n");
return NULL;
}
/* allocate the image buffer */
TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);
ib_depth = spp * 8;
ibuf = IMB_allocImBuf(width, height, ib_depth, 0);
if (ibuf) {
ibuf->ftype = IMB_FTYPE_TIF;
}
else {
fprintf(stderr,
"imb_loadtiff: could not allocate memory for TIFF "
"image.\n");
TIFFClose(image);
return NULL;
}
/* get alpha mode from file header */
if (flags & IB_alphamode_detect) {
if (spp == 4) {
ushort extra, *extraSampleTypes;
const int found = TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extra, &extraSampleTypes);
if (found && (extraSampleTypes[0] == EXTRASAMPLE_ASSOCALPHA)) {
ibuf->flags |= IB_alphamode_premul;
}
}
}
/* if testing, we're done */
if (flags & IB_test) {
TIFFClose(image);
return ibuf;
}
/* read pixels */
if (!imb_read_tiff_pixels(ibuf, image)) {
fprintf(stderr, "imb_loadtiff: Failed to read tiff image.\n");
TIFFClose(image);
return NULL;
}
/* close the client layer interface to the in-memory file */
TIFFClose(image);
/* return successfully */
return ibuf;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Save TIFF
* \{ */
bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
{
TIFF *image = NULL;
uint16_t samplesperpixel, bitspersample;
size_t npixels;
uchar *pixels = NULL;
uchar *from = NULL, *to = NULL;
ushort *pixels16 = NULL, *to16 = NULL;
float *fromf = NULL;
float xres, yres;
int x, y, from_i, to_i, i;
int compress_mode = COMPRESSION_NONE;
/* check for a valid number of bytes per pixel. Like the PNG writer,
* the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
* to gray, RGB, RGBA respectively. */
samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3);
if ((samplesperpixel > 4) || (samplesperpixel == 2)) {
fprintf(stderr,
"imb_savetiff: unsupported number of bytes per "
"pixel: %d\n",
samplesperpixel);
return 0;
}
if ((ibuf->foptions.flag & TIF_16BIT) && ibuf->rect_float) {
bitspersample = 16;
}
else {
bitspersample = 8;
}
if (ibuf->foptions.flag & TIF_COMPRESS_DEFLATE) {
compress_mode = COMPRESSION_DEFLATE;
}
else if (ibuf->foptions.flag & TIF_COMPRESS_LZW) {
compress_mode = COMPRESSION_LZW;
}
else if (ibuf->foptions.flag & TIF_COMPRESS_PACKBITS) {
compress_mode = COMPRESSION_PACKBITS;
}
/* open TIFF file for writing */
if (flags & IB_mem) {
/* Failed to allocate TIFF in memory. */
fprintf(stderr,
"imb_savetiff: creation of in-memory TIFF files is "
"not yet supported.\n");
return 0;
}
/* create image as a file */
#ifdef WIN32
wchar_t *wname = alloc_utf16_from_8(filepath, 0);
image = TIFFOpenW(wname, "w");
free(wname);
#else
image = TIFFOpen(filepath, "w");
#endif
if (image == NULL) {
fprintf(stderr, "imb_savetiff: could not open TIFF for writing.\n");
return 0;
}
/* allocate array for pixel data */
npixels = ibuf->x * ibuf->y;
if (bitspersample == 16) {
pixels16 = (ushort *)_TIFFmalloc(npixels * samplesperpixel * sizeof(ushort));
}
else {
pixels = (uchar *)_TIFFmalloc(npixels * samplesperpixel * sizeof(uchar));
}
if (pixels == NULL && pixels16 == NULL) {
fprintf(stderr, "imb_savetiff: could not allocate pixels array.\n");
TIFFClose(image);
return 0;
}
/* setup pointers */
if (bitspersample == 16) {
fromf = ibuf->rect_float;
to16 = pixels16;
}
else {
from = (uchar *)ibuf->rect;
to = pixels;
}
/* setup samples per pixel */
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, bitspersample);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
if (samplesperpixel == 4) {
ushort extraSampleTypes[1];
if (bitspersample == 16) {
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
}
else {
extraSampleTypes[0] = EXTRASAMPLE_UNASSALPHA;
}
/* RGBA images */
TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
}
else if (samplesperpixel == 3) {
/* RGB images */
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
}
else if (samplesperpixel == 1) {
/* Gray-scale images, 1 channel */
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
}
/* copy pixel data. While copying, we flip the image vertically. */
const int channels_in_float = ibuf->channels ? ibuf->channels : 4;
for (x = 0; x < ibuf->x; x++) {
for (y = 0; y < ibuf->y; y++) {
from_i = ((size_t)channels_in_float) * (y * ibuf->x + x);
to_i = samplesperpixel * ((ibuf->y - y - 1) * ibuf->x + x);
if (pixels16) {
/* convert from float source */
float rgb[4];
if (ELEM(channels_in_float, 3, 4)) {
if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
/* Float buffer was managed already, no need in color
* space conversion.
*/
copy_v3_v3(rgb, &fromf[from_i]);
}
else {
/* Standard linear-to-SRGB conversion if float buffer wasn't managed. */
linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]);
}
if (channels_in_float == 4) {
rgb[3] = fromf[from_i + 3];
}
else {
rgb[3] = 1.0f;
}
}
else {
if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
rgb[0] = fromf[from_i];
}
else {
rgb[0] = linearrgb_to_srgb(fromf[from_i]);
}
rgb[1] = rgb[2] = rgb[0];
rgb[3] = 1.0f;
}
for (i = 0; i < samplesperpixel; i++, to_i++) {
to16[to_i] = unit_float_to_ushort_clamp(rgb[i]);
}
}
else {
for (i = 0; i < samplesperpixel; i++, to_i++, from_i++) {
to[to_i] = from[from_i];
}
}
}
}
/* write the actual TIFF file */
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, ibuf->x);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, ibuf->y);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, ibuf->y);
TIFFSetField(image, TIFFTAG_COMPRESSION, compress_mode);
TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
xres = (float)(ibuf->ppm[0] * 0.0254);
yres = (float)(ibuf->ppm[1] * 0.0254);
}
else {
xres = yres = IMB_DPI_DEFAULT;
}
TIFFSetField(image, TIFFTAG_XRESOLUTION, xres);
TIFFSetField(image, TIFFTAG_YRESOLUTION, yres);
TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
if (TIFFWriteEncodedStrip(image,
0,
(bitspersample == 16) ? (uchar *)pixels16 : pixels,
(size_t)ibuf->x * ibuf->y * samplesperpixel * bitspersample / 8) ==
-1) {
fprintf(stderr, "imb_savetiff: Could not write encoded TIFF.\n");
TIFFClose(image);
if (pixels) {
_TIFFfree(pixels);
}
if (pixels16) {
_TIFFfree(pixels16);
}
return 1;
}
/* close the TIFF file */
TIFFClose(image);
if (pixels) {
_TIFFfree(pixels);
}
if (pixels16) {
_TIFFfree(pixels16);
}
return 1;
}
/** \} */

View File

@ -41,26 +41,18 @@
#define UTIL_DEBUG 0
const char *imb_ext_image[] = {
".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba",
#ifdef WITH_TIFF
".tif", ".tiff", ".tx",
#endif
".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb", ".rgba", ".tif", ".tiff", ".tx",
#ifdef WITH_OPENJPEG
".jp2", ".j2c",
#endif
#ifdef WITH_HDR
".hdr",
#endif
#ifdef WITH_DDS
".dds",
#endif
".hdr", ".dds",
#ifdef WITH_CINEON
".dpx", ".cin",
#endif
#ifdef WITH_OPENEXR
".exr",
#endif
".psd", ".pdd", ".psb",
".psd", ".pdd", ".psb",
#ifdef WITH_WEBP
".webp",
#endif

View File

@ -80,7 +80,6 @@ static const char *imb_gpu_get_swizzle(const ImBuf *ibuf)
}
/* Return false if no suitable format was found. */
#ifdef WITH_DDS
static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
{
/* For DDS we only support data, scene linear and sRGB. Converting to
@ -102,7 +101,6 @@ static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *
}
return true;
}
#endif
/**
* Apply colormanagement and scale buffer if needed.
@ -326,7 +324,6 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
}
}
#ifdef WITH_DDS
if (ibuf->ftype == IMB_FTYPE_DDS) {
eGPUTextureFormat compressed_format;
if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) {
@ -356,7 +353,6 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
/* Fallback to uncompressed texture. */
fprintf(stderr, " falling back to uncompressed.\n");
}
#endif
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;

View File

@ -70,6 +70,11 @@ if(WITH_HARU)
)
list(APPEND LIB
${HARU_LIBRARIES}
# Haru needs `TIFFFaxBlackCodes` & `TIFFFaxWhiteCodes` symbols from TIFF.
# Can be removed with Haru 2.4.0. They should be shipping with their own
# Fax codes defined by default from that version onwards.
${TIFF_LIBRARY}
)
add_definitions(-DWITH_HARU)
endif()

View File

@ -245,26 +245,14 @@ if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
if(WITH_IMAGE_TIFF)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_IMAGE_OPENJPEG)
add_definitions(-DWITH_OPENJPEG)
endif()
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
if(WITH_IMAGE_WEBP)
add_definitions(-DWITH_WEBP)
endif()

View File

@ -280,12 +280,8 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = {
"Output image in uncompressed Targa format"},
#if 0 /* UNUSED (so far) */
# ifdef WITH_DDS
# define R_IMF_ENUM_DDS \
{R_IMF_IMTYPE_DDS, "DDS", ICON_FILE_IMAGE, "DDS", "Output image in DDS format"},
# else
# define R_IMF_ENUM_DDS
# endif
# define R_IMF_ENUM_DDS \
{R_IMF_IMTYPE_DDS, "DDS", ICON_FILE_IMAGE, "DDS", "Output image in DDS format"},
#endif
#ifdef WITH_OPENJPEG
@ -327,23 +323,15 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = {
# define R_IMF_ENUM_EXR
#endif
#ifdef WITH_HDR
# define R_IMF_ENUM_HDR \
{R_IMF_IMTYPE_RADHDR, \
"HDR", \
ICON_FILE_IMAGE, \
"Radiance HDR", \
"Output image in Radiance HDR format"},
#else
# define R_IMF_ENUM_HDR
#endif
#define R_IMF_ENUM_HDR \
{R_IMF_IMTYPE_RADHDR, \
"HDR", \
ICON_FILE_IMAGE, \
"Radiance HDR", \
"Output image in Radiance HDR format"},
#ifdef WITH_TIFF
# define R_IMF_ENUM_TIFF \
{R_IMF_IMTYPE_TIFF, "TIFF", ICON_FILE_IMAGE, "TIFF", "Output image in TIFF format"},
#else
# define R_IMF_ENUM_TIFF
#endif
#define R_IMF_ENUM_TIFF \
{R_IMF_IMTYPE_TIFF, "TIFF", ICON_FILE_IMAGE, "TIFF", "Output image in TIFF format"},
#ifdef WITH_WEBP
# define R_IMF_ENUM_WEBP \
@ -5726,7 +5714,6 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
};
# endif
# ifdef WITH_TIFF
static const EnumPropertyItem tiff_codec_items[] = {
{R_IMF_TIFF_CODEC_NONE, "NONE", 0, "None", ""},
{R_IMF_TIFF_CODEC_DEFLATE, "DEFLATE", 0, "Deflate", ""},
@ -5734,7 +5721,6 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
{R_IMF_TIFF_CODEC_PACKBITS, "PACKBITS", 0, "Pack Bits", ""},
{0, NULL, 0, NULL, NULL},
};
# endif
static const EnumPropertyItem color_management_items[] = {
{R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE, "FOLLOW_SCENE", 0, "Follow Scene", ""},
@ -5851,14 +5837,12 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
# endif
# ifdef WITH_TIFF
/* TIFF */
prop = RNA_def_property(srna, "tiff_codec", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "tiff_codec");
RNA_def_property_enum_items(prop, tiff_codec_items);
RNA_def_property_ui_text(prop, "Compression", "Compression mode for TIFF");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
# endif
/* Cineon and DPX */

View File

@ -208,14 +208,6 @@ if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
@ -224,10 +216,6 @@ if(WITH_IMAGE_OPENJPEG)
add_definitions(-DWITH_OPENJPEG)
endif()
if(WITH_IMAGE_TIFF)
add_definitions(-DWITH_TIFF)
endif()
if(WITH_WEBP)
add_definitions(-DWITH_WEBP)
endif()

View File

@ -135,17 +135,11 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
#ifdef WITH_DDS
/* DDS */
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
#endif
#ifdef WITH_HDR
/* HDR */
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
#endif
#ifdef WITH_OPENEXR
SetObjIncref(Py_True);
@ -159,11 +153,8 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
#ifdef WITH_TIFF
/* TIFF */
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
#endif
#ifdef WITH_INPUT_NDOF
SetObjIncref(Py_True);

View File

@ -946,18 +946,12 @@ else()
if(WITH_IMAGE_CINEON)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} CINEON")
endif()
if(WITH_IMAGE_HDR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} HDR")
endif()
if(WITH_IMAGE_OPENEXR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENEXR")
endif()
if(WITH_IMAGE_OPENJPEG)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENJPEG")
endif()
if(WITH_IMAGE_TIFF)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} TIFF")
endif()
if(WITH_IMAGE_WEBP)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} WEBP")
endif()

View File

@ -119,16 +119,12 @@ class ImBufLoadTest(ImBufTest):
self.check("*.exr")
def test_load_hdr(self):
self.skip_if_format_missing("HDR")
self.check("*.hdr")
def test_load_targa(self):
self.check("*.tga")
def test_load_tiff(self):
self.skip_if_format_missing("TIFF")
self.check("*.tif")
def test_load_jpeg(self):
@ -141,8 +137,6 @@ class ImBufLoadTest(ImBufTest):
self.check("*.j2c")
def test_load_dpx(self):
self.skip_if_format_missing("CINEON")
self.check("*.dpx")
def test_load_cineon(self):

View File

@ -130,8 +130,6 @@ class ImBufSaveTest(ImBufTest):
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"})
def test_save_hdr(self):
self.skip_if_format_missing("HDR")
self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "BW"})
self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "RGB"})
@ -157,8 +155,6 @@ class ImBufSaveTest(ImBufTest):
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGBA"})
def test_save_tiff(self):
self.skip_if_format_missing("TIFF")
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "8", "tiff_codec": "DEFLATE"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "8", "tiff_codec": "LZW"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "8", "tiff_codec": "PACKBITS"})
@ -216,8 +212,6 @@ class ImBufSaveTest(ImBufTest):
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": False, "use_jpeg2k_ycc": True, "quality": 70})
def test_save_dpx(self):
self.skip_if_format_missing("CINEON")
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "8", "use_cineon_log": False})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "12", "use_cineon_log": False})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "16", "use_cineon_log": False})