From ccd7bc20786b1e95553395140695fbf02c72757f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 25 Mar 2025 14:52:31 +0100 Subject: [PATCH] Refactor: Modify colorspace handling for image buffer reading The file formats now fill in ImColorSpaceInfo with the metadata colorspace and a boolean saying if the pixels have HDR colors. And then the actual colorspace is decided in imb_handle_colorspace_and_alpha. This centralizes the logic in one place to make it possible to add OpenColorIO file rules. Pull Request: https://projects.blender.org/blender/blender/pulls/136516 --- .../intern/IMB_colormanagement_intern.hh | 2 - source/blender/imbuf/intern/IMB_filetype.hh | 53 +++++++++------- .../blender/imbuf/intern/cineon/cineon_dpx.cc | 10 +-- .../blender/imbuf/intern/colormanagement.cc | 11 ---- source/blender/imbuf/intern/format_bmp.cc | 4 +- source/blender/imbuf/intern/format_dds.cc | 4 +- source/blender/imbuf/intern/format_dpx.cc | 8 +-- source/blender/imbuf/intern/format_hdr.cc | 4 +- source/blender/imbuf/intern/format_png.cc | 10 +-- source/blender/imbuf/intern/format_psd.cc | 6 +- source/blender/imbuf/intern/format_svg.cc | 4 +- source/blender/imbuf/intern/format_targa.cc | 4 +- source/blender/imbuf/intern/format_tiff.cc | 10 +-- source/blender/imbuf/intern/iris.cc | 5 +- source/blender/imbuf/intern/jp2.cc | 15 ++--- source/blender/imbuf/intern/jpeg.cc | 13 ++-- .../imbuf/intern/oiio/openimageio_support.cc | 38 +++--------- .../imbuf/intern/oiio/openimageio_support.hh | 9 ++- .../imbuf/intern/openexr/openexr_api.cpp | 26 +++----- .../imbuf/intern/openexr/openexr_api.h | 13 ++-- source/blender/imbuf/intern/readimage.cc | 62 ++++++++++++------- source/blender/imbuf/intern/webp.cc | 7 +-- .../blender/imbuf/movie/intern/movie_read.cc | 11 ++-- 23 files changed, 152 insertions(+), 177 deletions(-) diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.hh b/source/blender/imbuf/intern/IMB_colormanagement_intern.hh index 14f5e8ec284..46f397cb1f1 100644 --- a/source/blender/imbuf/intern/IMB_colormanagement_intern.hh +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.hh @@ -107,7 +107,5 @@ ColorManagedLook *colormanage_look_add(const char *name, const char *process_spa ColorManagedLook *colormanage_look_get_named(const char *name); ColorManagedLook *colormanage_look_get_indexed(int index); -void colorspace_set_default_role(char *colorspace, int size, int role); - void colormanage_imbuf_set_default_spaces(ImBuf *ibuf); void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace); diff --git a/source/blender/imbuf/intern/IMB_filetype.hh b/source/blender/imbuf/intern/IMB_filetype.hh index 8f5162461e7..124c5e3b194 100644 --- a/source/blender/imbuf/intern/IMB_filetype.hh +++ b/source/blender/imbuf/intern/IMB_filetype.hh @@ -10,12 +10,13 @@ #include "IMB_imbuf.hh" +struct ImBuf; +struct ImFileColorSpace; + /* -------------------------------------------------------------------- */ /** \name Generic File Type * \{ */ -struct ImBuf; - #define IM_FTYPE_FLOAT 1 struct ImFileType { @@ -32,9 +33,9 @@ struct ImFileType { bool (*is_a)(const unsigned char *buf, size_t size); /** Load an image from memory. */ - ImBuf *(*load)(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); + ImBuf *(*load)(const unsigned char *mem, size_t size, int flags, ImFileColorSpace &r_colorspace); /** Load an image from a file. */ - ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); + ImBuf *(*load_filepath)(const char *filepath, int flags, ImFileColorSpace &r_colorspace); /** * Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either * dimension, so can return less on either or both. Should, if possible and performant, return @@ -43,7 +44,7 @@ struct ImFileType { ImBuf *(*load_filepath_thumbnail)(const char *filepath, int flags, size_t max_thumb_size, - char colorspace[IM_MAX_SPACE], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height); /** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */ @@ -57,6 +58,14 @@ struct ImFileType { int default_save_role; }; +/* Color space information provided by the file. */ +struct ImFileColorSpace { + /* Color space from metadata. */ + char metadata_colorspace[IM_MAX_SPACE] = ""; + /* Is image HDR with range potentially outside 0..1? */ + bool is_hdr_float = false; +}; + extern const ImFileType IMB_FILE_TYPES[]; extern const ImFileType *IMB_FILE_TYPES_LAST; @@ -78,7 +87,7 @@ bool imb_is_a_png(const unsigned char *mem, size_t size); ImBuf *imb_load_png(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); bool imb_save_png(ImBuf *ibuf, const char *filepath, int flags); /** \} */ @@ -91,7 +100,7 @@ bool imb_is_a_tga(const unsigned char *mem, size_t size); ImBuf *imb_load_tga(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); bool imb_save_tga(ImBuf *ibuf, const char *filepath, int flags); /** \} */ @@ -107,7 +116,7 @@ bool imb_is_a_iris(const unsigned char *mem, size_t size); ImBuf *imb_loadiris(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); bool imb_saveiris(ImBuf *ibuf, const char *filepath, int flags); /** \} */ @@ -120,8 +129,8 @@ bool imb_is_a_jp2(const unsigned char *buf, size_t size); ImBuf *imb_load_jp2(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); -ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); +ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, ImFileColorSpace &r_colorspace); bool imb_save_jp2(ImBuf *ibuf, const char *filepath, int flags); /** \} */ @@ -135,11 +144,11 @@ bool imb_savejpeg(ImBuf *ibuf, const char *filepath, int flags); ImBuf *imb_load_jpeg(const unsigned char *buffer, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); ImBuf *imb_thumbnail_jpeg(const char *filepath, int flags, size_t max_thumb_size, - char colorspace[IM_MAX_SPACE], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height); @@ -153,7 +162,7 @@ bool imb_is_a_bmp(const unsigned char *mem, size_t size); ImBuf *imb_load_bmp(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */ bool imb_save_bmp(ImBuf *ibuf, const char *filepath, int flags); @@ -168,7 +177,7 @@ bool imb_save_cineon(ImBuf *buf, const char *filepath, int flags); ImBuf *imb_load_cineon(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /** \} */ @@ -181,7 +190,7 @@ bool imb_save_dpx(ImBuf *ibuf, const char *filepath, int flags); ImBuf *imb_load_dpx(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /** \} */ @@ -193,7 +202,7 @@ bool imb_is_a_hdr(const unsigned char *mem, size_t size); ImBuf *imb_load_hdr(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); bool imb_save_hdr(ImBuf *ibuf, const char *filepath, int flags); /** \} */ @@ -215,7 +224,7 @@ bool imb_is_a_tiff(const unsigned char *mem, size_t size); ImBuf *imb_load_tiff(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /** * Saves a TIFF file. * @@ -242,11 +251,11 @@ bool imb_is_a_webp(const unsigned char *mem, size_t size); ImBuf *imb_loadwebp(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath, const int flags, const size_t max_thumb_size, - char colorspace[], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height); bool imb_savewebp(ImBuf *ibuf, const char *filepath, int flags); @@ -264,7 +273,7 @@ bool imb_is_a_dds(const unsigned char *mem, size_t size); ImBuf *imb_load_dds(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /** \} */ @@ -277,7 +286,7 @@ bool imb_is_a_psd(const unsigned char *mem, size_t size); ImBuf *imb_load_psd(const unsigned char *mem, size_t size, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); /** \} */ @@ -288,7 +297,7 @@ ImBuf *imb_load_psd(const unsigned char *mem, ImBuf *imb_load_filepath_thumbnail_svg(const char *filepath, const int flags, const size_t max_thumb_size, - char colorspace[], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height); diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.cc b/source/blender/imbuf/intern/cineon/cineon_dpx.cc index bfa4060d628..09c3ff495ce 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.cc +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.cc @@ -20,14 +20,12 @@ #include "MEM_guardedalloc.h" static ImBuf *imb_load_dpx_cineon( - const uchar *mem, size_t size, int use_cineon, int flags, char colorspace[IM_MAX_SPACE]) + const uchar *mem, size_t size, int use_cineon, int flags, ImFileColorSpace &r_colorspace) { ImBuf *ibuf; LogImageFile *image; int width, height, depth; - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); - logImageSetVerbose((G.debug & G_DEBUG) ? 1 : 0); image = logImageOpenFromMemory(mem, size); @@ -61,6 +59,8 @@ static ImBuf *imb_load_dpx_cineon( ibuf->flags |= IB_alphamode_premul; } + r_colorspace.is_hdr_float = true; + return ibuf; } @@ -173,10 +173,10 @@ bool imb_is_a_cineon(const uchar *mem, size_t size) return logImageIsCineon(mem, size); } -ImBuf *imb_load_cineon(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_cineon(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { if (!imb_is_a_cineon(mem, size)) { return nullptr; } - return imb_load_dpx_cineon(mem, size, 1, flags, colorspace); + return imb_load_dpx_cineon(mem, size, 1, flags, r_colorspace); } diff --git a/source/blender/imbuf/intern/colormanagement.cc b/source/blender/imbuf/intern/colormanagement.cc index 6995daefb01..3af1182eead 100644 --- a/source/blender/imbuf/intern/colormanagement.cc +++ b/source/blender/imbuf/intern/colormanagement.cc @@ -1110,17 +1110,6 @@ static void curve_mapping_apply_pixel(const CurveMapping *curve_mapping, } } -void colorspace_set_default_role(char *colorspace, int size, int role) -{ - if (colorspace && colorspace[0] == '\0') { - const char *role_colorspace; - - role_colorspace = IMB_colormanagement_role_colorspace_name_get(role); - - BLI_strncpy(colorspace, role_colorspace, size); - } -} - void colormanage_imbuf_set_default_spaces(ImBuf *ibuf) { ibuf->byte_buffer.colorspace = colormanage_colorspace_get_named(global_role_default_byte); diff --git a/source/blender/imbuf/intern/format_bmp.cc b/source/blender/imbuf/intern/format_bmp.cc index 66dbf0cbe13..95e01638368 100644 --- a/source/blender/imbuf/intern/format_bmp.cc +++ b/source/blender/imbuf/intern/format_bmp.cc @@ -19,7 +19,7 @@ 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]) +ImBuf *imb_load_bmp(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImageSpec config, spec; @@ -27,7 +27,7 @@ ImBuf *imb_load_bmp(const uchar *mem, size_t size, int flags, char colorspace[IM config.attribute("bmp:monochrome_detect", 0); ReadContext ctx{mem, size, "bmp", IMB_FTYPE_BMP, flags}; - return imb_oiio_read(ctx, config, colorspace, spec); + return imb_oiio_read(ctx, config, r_colorspace, spec); } bool imb_save_bmp(ImBuf *ibuf, const char *filepath, int flags) diff --git a/source/blender/imbuf/intern/format_dds.cc b/source/blender/imbuf/intern/format_dds.cc index 0e0cc4bfd18..37e3fc0b75e 100644 --- a/source/blender/imbuf/intern/format_dds.cc +++ b/source/blender/imbuf/intern/format_dds.cc @@ -48,12 +48,12 @@ bool imb_is_a_dds(const uchar *mem, size_t size) return imb_oiio_check(mem, size, "dds"); } -ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImageSpec config, spec; ReadContext ctx{mem, size, "dds", IMB_FTYPE_DDS, flags}; - ImBuf *ibuf = imb_oiio_read(ctx, config, colorspace, spec); + ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec); /* Load compressed DDS information if available. */ if (ibuf && (flags & IB_test) == 0) { diff --git a/source/blender/imbuf/intern/format_dpx.cc b/source/blender/imbuf/intern/format_dpx.cc index ff110a5c06d..b809aac2b5b 100644 --- a/source/blender/imbuf/intern/format_dpx.cc +++ b/source/blender/imbuf/intern/format_dpx.cc @@ -20,21 +20,21 @@ 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]) +ImBuf *imb_load_dpx(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { 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); + ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec); if (ibuf) { if (flags & IB_alphamode_detect) { ibuf->flags |= IB_alphamode_premul; } } + r_colorspace.is_hdr_float = true; + return ibuf; } diff --git a/source/blender/imbuf/intern/format_hdr.cc b/source/blender/imbuf/intern/format_hdr.cc index 48edaffa092..283b8c96a74 100644 --- a/source/blender/imbuf/intern/format_hdr.cc +++ b/source/blender/imbuf/intern/format_hdr.cc @@ -19,7 +19,7 @@ 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]) +ImBuf *imb_load_hdr(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImageSpec config, spec; @@ -28,7 +28,7 @@ ImBuf *imb_load_hdr(const uchar *mem, size_t size, int flags, char colorspace[IM /* 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); + ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec); if (ibuf) { if (flags & IB_alphamode_detect) { ibuf->flags |= IB_alphamode_premul; diff --git a/source/blender/imbuf/intern/format_png.cc b/source/blender/imbuf/intern/format_png.cc index a59f3721a51..ce0df6ce110 100644 --- a/source/blender/imbuf/intern/format_png.cc +++ b/source/blender/imbuf/intern/format_png.cc @@ -20,23 +20,23 @@ 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]) +ImBuf *imb_load_png(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { 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); + ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec); if (ibuf) { if (spec.format == TypeDesc::UINT16) { ibuf->flags |= PNG_16BIT; } } + /* Both 8 and 16 bit PNGs should be in default byte colorspace. */ + r_colorspace.is_hdr_float = false; + return ibuf; } diff --git a/source/blender/imbuf/intern/format_psd.cc b/source/blender/imbuf/intern/format_psd.cc index cd6dafba284..1458ea5e122 100644 --- a/source/blender/imbuf/intern/format_psd.cc +++ b/source/blender/imbuf/intern/format_psd.cc @@ -20,7 +20,7 @@ bool imb_is_a_psd(const uchar *mem, size_t size) return imb_oiio_check(mem, size, "psd"); } -ImBuf *imb_load_psd(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_psd(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImageSpec config, spec; config.attribute("oiio:UnassociatedAlpha", 1); @@ -28,7 +28,7 @@ ImBuf *imb_load_psd(const uchar *mem, size_t size, int flags, char colorspace[IM ReadContext ctx{mem, size, "psd", IMB_FTYPE_PSD, flags}; /* PSD should obey color space information embedded in the file. */ - ctx.use_embedded_colorspace = true; + ctx.use_metadata_colorspace = true; - return imb_oiio_read(ctx, config, colorspace, spec); + return imb_oiio_read(ctx, config, r_colorspace, spec); } diff --git a/source/blender/imbuf/intern/format_svg.cc b/source/blender/imbuf/intern/format_svg.cc index 35d83d1090a..b8c62e087a5 100644 --- a/source/blender/imbuf/intern/format_svg.cc +++ b/source/blender/imbuf/intern/format_svg.cc @@ -19,7 +19,7 @@ ImBuf *imb_load_filepath_thumbnail_svg(const char *filepath, const int /*flags*/, const size_t max_thumb_size, - char colorspace[], + ImFileColorSpace & /*r_colorspace*/, size_t *r_width, size_t *r_height) { @@ -47,8 +47,6 @@ ImBuf *imb_load_filepath_thumbnail_svg(const char *filepath, return nullptr; } - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - const float scale = float(max_thumb_size) / std::max(w, h); const int dest_w = std::max(int(w * scale), 1); const int dest_h = std::max(int(h * scale), 1); diff --git a/source/blender/imbuf/intern/format_targa.cc b/source/blender/imbuf/intern/format_targa.cc index ce42692ca43..7ebca13719a 100644 --- a/source/blender/imbuf/intern/format_targa.cc +++ b/source/blender/imbuf/intern/format_targa.cc @@ -19,13 +19,13 @@ 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]) +ImBuf *imb_load_tga(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { 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); + return imb_oiio_read(ctx, config, r_colorspace, spec); } bool imb_save_tga(ImBuf *ibuf, const char *filepath, int flags) diff --git a/source/blender/imbuf/intern/format_tiff.cc b/source/blender/imbuf/intern/format_tiff.cc index 37c45473dc2..b702b28bfa2 100644 --- a/source/blender/imbuf/intern/format_tiff.cc +++ b/source/blender/imbuf/intern/format_tiff.cc @@ -20,17 +20,14 @@ 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]) +ImBuf *imb_load_tiff(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { 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); + ImBuf *ibuf = imb_oiio_read(ctx, config, r_colorspace, spec); if (ibuf) { if (flags & IB_alphamode_detect) { if (spec.nchannels == 4 && spec.format == TypeDesc::UINT16) { @@ -39,6 +36,9 @@ ImBuf *imb_load_tiff(const uchar *mem, size_t size, int flags, char colorspace[I } } + /* All TIFFs should be in default byte colorspace. */ + r_colorspace.is_hdr_float = false; + return ibuf; } diff --git a/source/blender/imbuf/intern/iris.cc b/source/blender/imbuf/intern/iris.cc index 5bba5ab991b..9791d6759a2 100644 --- a/source/blender/imbuf/intern/iris.cc +++ b/source/blender/imbuf/intern/iris.cc @@ -213,7 +213,7 @@ bool imb_is_a_iris(const uchar *mem, size_t size) return ((GS(mem) == IMAGIC) || (GSS(mem) == IMAGIC)); } -ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, ImFileColorSpace & /*r_colorspace*/) { uint *base, *lptr = nullptr; float *fbase, *fptr = nullptr; @@ -235,9 +235,6 @@ ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colorspace[IM return nullptr; } - /* OCIO_TODO: only tested with 1 byte per pixel, not sure how to test with other settings */ - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - readheader(inf, &image); if (image.imagic != IMAGIC) { fprintf(stderr, "longimagedata: bad magic number in image file\n"); diff --git a/source/blender/imbuf/intern/jp2.cc b/source/blender/imbuf/intern/jp2.cc index bc600a21440..ce336194e4f 100644 --- a/source/blender/imbuf/intern/jp2.cc +++ b/source/blender/imbuf/intern/jp2.cc @@ -298,9 +298,9 @@ static opj_stream_t *opj_stream_create_from_file(const char *filepath, static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, OPJ_CODEC_FORMAT p_format, int flags, - char colorspace[IM_MAX_SPACE]); + ImFileColorSpace &r_colorspace); -ImBuf *imb_load_jp2(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_jp2(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { const OPJ_CODEC_FORMAT format = (size > JP2_FILEHEADER_SIZE) ? format_from_header(mem, size) : OPJ_CODEC_UNKNOWN; @@ -310,12 +310,12 @@ ImBuf *imb_load_jp2(const uchar *mem, size_t size, int flags, char colorspace[IM buf_wrapper.len = OPJ_OFF_T(size); opj_stream_t *stream = opj_stream_create_from_buffer( &buf_wrapper, OPJ_J2K_STREAM_CHUNK_SIZE, true); - ImBuf *ibuf = imb_load_jp2_stream(stream, format, flags, colorspace); + ImBuf *ibuf = imb_load_jp2_stream(stream, format, flags, r_colorspace); opj_stream_destroy(stream); return ibuf; } -ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, ImFileColorSpace &r_colorspace) { FILE *p_file = nullptr; uchar mem[JP2_FILEHEADER_SIZE]; @@ -333,7 +333,7 @@ ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM fseek(p_file, 0, SEEK_SET); const OPJ_CODEC_FORMAT format = format_from_header(mem, sizeof(mem)); - ImBuf *ibuf = imb_load_jp2_stream(stream, format, flags, colorspace); + ImBuf *ibuf = imb_load_jp2_stream(stream, format, flags, r_colorspace); opj_stream_destroy(stream); return ibuf; } @@ -341,7 +341,7 @@ ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, const OPJ_CODEC_FORMAT format, int flags, - char colorspace[IM_MAX_SPACE]) + ImFileColorSpace & /*r_colorspace*/) { if (format == OPJ_CODEC_UNKNOWN) { return nullptr; @@ -363,9 +363,6 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, opj_image_t *image = nullptr; opj_codec_t *codec = nullptr; /* handle to a decompressor */ - /* both 8, 12 and 16 bit JP2Ks are default to standard byte colorspace */ - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - /* set decoding parameters to default values */ opj_set_default_decoder_parameters(¶meters); diff --git a/source/blender/imbuf/intern/jpeg.cc b/source/blender/imbuf/intern/jpeg.cc index 73809328f1b..b895526f423 100644 --- a/source/blender/imbuf/intern/jpeg.cc +++ b/source/blender/imbuf/intern/jpeg.cc @@ -438,7 +438,10 @@ static ImBuf *ibJpegImageFromCinfo( return ibuf; } -ImBuf *imb_load_jpeg(const uchar *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_jpeg(const uchar *buffer, + size_t size, + int flags, + ImFileColorSpace & /*r_colorspace*/) { jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; my_error_mgr jerr; @@ -448,8 +451,6 @@ ImBuf *imb_load_jpeg(const uchar *buffer, size_t size, int flags, char colorspac return nullptr; } - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = jpeg_error; @@ -479,7 +480,7 @@ ImBuf *imb_load_jpeg(const uchar *buffer, size_t size, int flags, char colorspac ImBuf *imb_thumbnail_jpeg(const char *filepath, const int flags, const size_t max_thumb_size, - char colorspace[IM_MAX_SPACE], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height) { @@ -487,8 +488,6 @@ ImBuf *imb_thumbnail_jpeg(const char *filepath, my_error_mgr jerr; FILE *infile = nullptr; - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = jpeg_error; @@ -526,7 +525,7 @@ ImBuf *imb_thumbnail_jpeg(const char *filepath, buffer[0] = JPEG_MARKER_MSB; buffer[1] = JPEG_MARKER_SOI; if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) { - ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace); + ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, r_colorspace); } MEM_SAFE_FREE(buffer); if (ibuf) { diff --git a/source/blender/imbuf/intern/oiio/openimageio_support.cc b/source/blender/imbuf/intern/oiio/openimageio_support.cc index e4970338f62..5b9324bbf5c 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_support.cc +++ b/source/blender/imbuf/intern/oiio/openimageio_support.cc @@ -16,6 +16,7 @@ #include "DNA_ID.h" #include "IMB_allocimbuf.hh" #include "IMB_colormanagement.hh" +#include "IMB_filetype.hh" #include "IMB_metadata.hh" OIIO_NAMESPACE_USING @@ -139,44 +140,25 @@ static ImBuf *load_pixels( return ibuf; } -static void set_colorspace_name(char colorspace[IM_MAX_SPACE], +static void set_file_colorspace(ImFileColorSpace &r_colorspace, const ReadContext &ctx, const ImageSpec &spec, bool is_float) { - const bool is_colorspace_set = (colorspace[0] != '\0'); - if (is_colorspace_set) { - return; - } - - /* Use a default role unless otherwise specified. */ - if (ctx.use_colorspace_role >= 0) { - colorspace_set_default_role(colorspace, IM_MAX_SPACE, ctx.use_colorspace_role); - } - else if (is_float) { - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); - } - else { - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - } + /* Guess float data types means HDR colors. File formats can override this later. */ + r_colorspace.is_hdr_float = is_float; /* Override if necessary. */ - if (ctx.use_embedded_colorspace) { + if (ctx.use_metadata_colorspace) { string ics = spec.get_string_attribute("oiio:ColorSpace"); - char file_colorspace[IM_MAX_SPACE]; - STRNCPY(file_colorspace, ics.c_str()); - - /* Only use color-spaces that exist. */ - if (colormanage_colorspace_get_named(file_colorspace)) { - BLI_strncpy(colorspace, file_colorspace, IM_MAX_SPACE); - } + STRNCPY(r_colorspace.metadata_colorspace, ics.c_str()); } } /** * Get an #ImBuf filled in with pixel data and associated metadata using the provided ImageInput. */ -static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorspace[IM_MAX_SPACE]) +static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, ImFileColorSpace &r_colorspace) { const ImageSpec &spec = in->spec(); const int width = spec.width; @@ -205,7 +187,7 @@ static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorsp ibuf->ftype = ctx.file_type; ibuf->foptions.flag |= (spec.format == TypeDesc::HALF) ? OPENEXR_HALF : 0; - set_colorspace_name(colorspace, ctx, spec, is_float); + set_file_colorspace(r_colorspace, ctx, spec, is_float); float x_res = spec.get_float_attribute("XResolution", 0.0f); float y_res = spec.get_float_attribute("YResolution", 0.0f); @@ -277,7 +259,7 @@ bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format) ImBuf *imb_oiio_read(const ReadContext &ctx, const ImageSpec &config, - char colorspace[IM_MAX_SPACE], + ImFileColorSpace &r_colorspace, ImageSpec &r_newspec) { /* This memory proxy must remain alive for the full duration of the read. */ @@ -287,7 +269,7 @@ ImBuf *imb_oiio_read(const ReadContext &ctx, return nullptr; } - return get_oiio_ibuf(in.get(), ctx, colorspace); + return get_oiio_ibuf(in.get(), ctx, r_colorspace); } bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSpec &file_spec) diff --git a/source/blender/imbuf/intern/oiio/openimageio_support.hh b/source/blender/imbuf/intern/oiio/openimageio_support.hh index 8933825a32b..e802b5254a8 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_support.hh +++ b/source/blender/imbuf/intern/oiio/openimageio_support.hh @@ -15,6 +15,8 @@ #include "IMB_imbuf.hh" #include "IMB_imbuf_types.hh" +struct ImFileColorSpace; + namespace blender::imbuf { /** @@ -27,14 +29,11 @@ struct ReadContext { const eImbFileType file_type; const int flags; - /** Override the automatic color-role choice with the value specified here. */ - int use_colorspace_role = -1; - /** Allocate and use all #ImBuf image planes even if the image has fewer. */ bool use_all_planes = false; /** Use the `colorspace` provided in the image metadata when available. */ - bool use_embedded_colorspace = false; + bool use_metadata_colorspace = false; }; /** @@ -67,7 +66,7 @@ bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format); */ ImBuf *imb_oiio_read(const ReadContext &ctx, const OIIO::ImageSpec &config, - char colorspace[IM_MAX_SPACE], + ImFileColorSpace &r_colorspace, OIIO::ImageSpec &r_newspec); /** diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 4e451fe64f2..8f05525f784 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -6,6 +6,7 @@ * \ingroup openexr */ +#include "IMB_filetype.hh" #include #include #include @@ -2174,11 +2175,9 @@ static bool imb_check_chromaticity_matches(const Imf::Chromaticities &a, imb_check_chromaticity_val(a.white.y, b.white.y); } -static void imb_exr_set_known_colorspace(const Header &header, char colorspace[IMA_MAX_SPACE]) +static void imb_exr_set_known_colorspace(const Header &header, ImFileColorSpace &r_colorspace) { - if (colorspace == nullptr || colorspace[0] != '\0') { - return; - } + r_colorspace.is_hdr_float = true; /* Read ACES container format metadata. */ const IntAttribute *header_aces_container = header.findTypedAttribute( @@ -2193,25 +2192,18 @@ static void imb_exr_set_known_colorspace(const Header &header, char colorspace[I const char *known_colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_ACES_INTERCHANGE); if (known_colorspace) { - BLI_strncpy(colorspace, known_colorspace, IMA_MAX_SPACE); - return; + STRNCPY(r_colorspace.metadata_colorspace, known_colorspace); } } else if (header_chromaticities && (imb_check_chromaticity_matches(header_chromaticities->value(), CHROMATICITIES_XYZ_E))) { /* Only works for the Blender default configuration due to fixed name. */ - const char *known_colorspace = "Linear CIE-XYZ E"; - if (IMB_colormanagement_space_get_named(known_colorspace)) { - BLI_strncpy(colorspace, known_colorspace, IMA_MAX_SPACE); - return; - } + STRNCPY(r_colorspace.metadata_colorspace, "Linear CIE-XYZ E"); } - - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); } -ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace) { ImBuf *ibuf = nullptr; IMemStream *membuf = nullptr; @@ -2258,7 +2250,7 @@ ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, char colorspac ibuf->ppm[1] = ibuf->ppm[0] * double(file_header.pixelAspectRatio()); } - imb_exr_set_known_colorspace(file_header, colorspace); + imb_exr_set_known_colorspace(file_header, r_colorspace); ibuf->ftype = IMB_FTYPE_OPENEXR; @@ -2416,7 +2408,7 @@ ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, char colorspac ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, const int /*flags*/, const size_t max_thumb_size, - char colorspace[], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height) { @@ -2465,7 +2457,7 @@ ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, } /* No effect yet for thumbnails, but will work once it is supported. */ - imb_exr_set_known_colorspace(file_header, colorspace); + imb_exr_set_known_colorspace(file_header, r_colorspace); /* Create a new thumbnail. */ float scale_factor = std::min(float(max_thumb_size) / float(source_w), diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h index 2fb3bbf280e..c36252258e3 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.h +++ b/source/blender/imbuf/intern/openexr/openexr_api.h @@ -10,8 +10,10 @@ #include -void imb_initopenexr(void); -void imb_exitopenexr(void); +struct ImFileColorSpace; + +void imb_initopenexr(); +void imb_exitopenexr(); /** * Test presence of OpenEXR file. @@ -21,11 +23,14 @@ bool imb_is_a_openexr(const unsigned char *mem, size_t size); bool imb_save_openexr(struct ImBuf *ibuf, const char *filepath, int flags); -struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags, char *colorspace); +struct ImBuf *imb_load_openexr(const unsigned char *mem, + size_t size, + int flags, + ImFileColorSpace &r_colorspace); struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, int flags, size_t max_thumb_size, - char colorspace[], + ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height); diff --git a/source/blender/imbuf/intern/readimage.cc b/source/blender/imbuf/intern/readimage.cc index 7ac69583ce8..4b47671d97f 100644 --- a/source/blender/imbuf/intern/readimage.cc +++ b/source/blender/imbuf/intern/readimage.cc @@ -29,23 +29,40 @@ #include "IMB_colormanagement.hh" #include "IMB_colormanagement_intern.hh" -static void imb_handle_alpha(ImBuf *ibuf, - int flags, - char colorspace[IM_MAX_SPACE], - const char effective_colorspace[IM_MAX_SPACE]) +static void imb_handle_colorspace_and_alpha(ImBuf *ibuf, + int flags, + const ImFileColorSpace &file_colorspace, + char r_colorspace[IM_MAX_SPACE]) { - if (colorspace) { + char new_colorspace[IM_MAX_SPACE]; + + if (r_colorspace && r_colorspace[0]) { + /* Existing configured colorspace has priority. */ + STRNCPY(new_colorspace, r_colorspace); + } + else if (file_colorspace.metadata_colorspace[0] && + colormanage_colorspace_get_named(file_colorspace.metadata_colorspace)) + { + /* Use colorspace from file metadata if provided. */ + STRNCPY(new_colorspace, file_colorspace.metadata_colorspace); + } + else { + /* Use float colorspace if the image may contain HDR colors, byte otherwise. */ + const char *role_colorspace = IMB_colormanagement_role_colorspace_name_get( + file_colorspace.is_hdr_float ? COLOR_ROLE_DEFAULT_FLOAT : COLOR_ROLE_DEFAULT_BYTE); + STRNCPY(new_colorspace, role_colorspace); + } + + if (r_colorspace) { if (ibuf->byte_buffer.data != nullptr && ibuf->float_buffer.data == nullptr) { /* byte buffer is never internally converted to some standard space, * store pointer to its color space descriptor instead */ - ibuf->byte_buffer.colorspace = colormanage_colorspace_get_named(effective_colorspace); + ibuf->byte_buffer.colorspace = colormanage_colorspace_get_named(new_colorspace); } - - BLI_strncpy(colorspace, effective_colorspace, IM_MAX_SPACE); } - bool is_data = (colorspace && IMB_colormanagement_space_name_is_data(colorspace)); + bool is_data = (r_colorspace && IMB_colormanagement_space_name_is_data(new_colorspace)); int alpha_flags = (flags & IB_alphamode_detect) ? ibuf->flags : flags; if (is_data || (flags & IB_alphamode_channel_packed)) { @@ -76,7 +93,10 @@ static void imb_handle_alpha(ImBuf *ibuf, } } - colormanage_imbuf_make_linear(ibuf, effective_colorspace); + colormanage_imbuf_make_linear(ibuf, new_colorspace); + if (r_colorspace) { + BLI_strncpy(r_colorspace, new_colorspace, IM_MAX_SPACE); + } } ImBuf *IMB_ibImageFromMemory( @@ -84,22 +104,19 @@ ImBuf *IMB_ibImageFromMemory( { ImBuf *ibuf; const ImFileType *type; - char effective_colorspace[IM_MAX_SPACE] = ""; if (mem == nullptr) { fprintf(stderr, "%s: nullptr pointer\n", __func__); return nullptr; } - if (colorspace) { - STRNCPY(effective_colorspace, colorspace); - } + ImFileColorSpace file_colorspace; for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { if (type->load) { - ibuf = type->load(mem, size, flags, effective_colorspace); + ibuf = type->load(mem, size, flags, file_colorspace); if (ibuf) { - imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace); + imb_handle_colorspace_and_alpha(ibuf, flags, file_colorspace, colorspace); return ibuf; } } @@ -179,14 +196,13 @@ ImBuf *IMB_thumb_load_image(const char *filepath, size_t width = 0; size_t height = 0; - char effective_colorspace[IM_MAX_SPACE] = ""; - if (colorspace) { - STRNCPY(effective_colorspace, colorspace); - } - if (type->load_filepath_thumbnail) { + ImFileColorSpace file_colorspace; ibuf = type->load_filepath_thumbnail( - filepath, flags, max_thumb_size, colorspace, &width, &height); + filepath, flags, max_thumb_size, file_colorspace, &width, &height); + if (ibuf) { + imb_handle_colorspace_and_alpha(ibuf, flags, file_colorspace, colorspace); + } } else { /* Skip images of other types if over 100MB. */ @@ -204,8 +220,6 @@ ImBuf *IMB_thumb_load_image(const char *filepath, } if (ibuf) { - imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace); - if (width > 0 && height > 0) { /* Save dimensions of original image into the thumbnail metadata. */ char cwidth[40]; diff --git a/source/blender/imbuf/intern/webp.cc b/source/blender/imbuf/intern/webp.cc index 937b47af68b..c9583b7add6 100644 --- a/source/blender/imbuf/intern/webp.cc +++ b/source/blender/imbuf/intern/webp.cc @@ -37,14 +37,12 @@ bool imb_is_a_webp(const uchar *mem, size_t size) return false; } -ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) +ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, ImFileColorSpace & /*r_colorspace*/) { if (!imb_is_a_webp(mem, size)) { return nullptr; } - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - WebPBitstreamFeatures features; if (WebPGetFeatures(mem, size, &features) != VP8_STATUS_OK) { fprintf(stderr, "WebP: Failed to parse features\n"); @@ -77,7 +75,7 @@ ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, char colorspace[IM ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath, const int /*flags*/, const size_t max_thumb_size, - char colorspace[], + ImFileColorSpace & /*r_colorspace*/, size_t *r_width, size_t *r_height) { @@ -116,7 +114,6 @@ ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath, const int dest_w = std::max(int(config.input.width * scale), 1); const int dest_h = std::max(int(config.input.height * scale), 1); - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_byte_data); if (ibuf == nullptr) { fprintf(stderr, "WebP: Failed to allocate image memory\n"); diff --git a/source/blender/imbuf/movie/intern/movie_read.cc b/source/blender/imbuf/movie/intern/movie_read.cc index 4525e9b52dd..f737f8e8dcf 100644 --- a/source/blender/imbuf/movie/intern/movie_read.cc +++ b/source/blender/imbuf/movie/intern/movie_read.cc @@ -108,13 +108,12 @@ MovieReader *MOV_open_file(const char *filepath, anim = MEM_new("anim struct"); if (anim != nullptr) { + const char *byte_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_DEFAULT_BYTE); + STRNCPY(anim->colorspace, byte_colorspace); + if (colorspace) { - colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - STRNCPY(anim->colorspace, colorspace); - } - else { - colorspace_set_default_role( - anim->colorspace, sizeof(anim->colorspace), COLOR_ROLE_DEFAULT_BYTE); + BLI_strncpy(colorspace, anim->colorspace, IM_MAX_SPACE); } STRNCPY(anim->filepath, filepath);