/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup imbuf */ #ifdef _WIN32 # include # include # include #endif #include #include "BLI_fileops.h" #include "BLI_mmap.h" #include "BLI_path_utils.hh" /* For assertions. */ #include "BLI_string.h" #include "BLI_string_utf8.h" #include "CLG_log.h" #include "IMB_filetype.hh" #include "IMB_imbuf.hh" #include "IMB_imbuf_types.hh" #include "IMB_metadata.hh" #include "IMB_thumbs.hh" #include "imbuf.hh" #include "IMB_colormanagement.hh" #include "IMB_colormanagement_intern.hh" namespace blender { static CLG_LogRef LOG = {"image.read"}; static void imb_handle_colorspace_and_alpha(ImBuf *ibuf, const ImBufFlags flags, const char *filepath, const ImFileColorSpace &file_colorspace, char r_colorspace[IM_MAX_SPACE]) { /* Determine file colorspace. */ char new_colorspace[IM_MAX_SPACE]; if (r_colorspace && r_colorspace[0]) { /* Existing configured colorspace has priority. */ STRNCPY_UTF8(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_UTF8(new_colorspace, file_colorspace.metadata_colorspace); } else { /* The color-space from the file-path (not a file-path). */ const char *filepath_colorspace = (filepath) ? IMB_colormanagement_space_from_filepath_rules(filepath) : nullptr; if (filepath_colorspace) { /* Use colorspace from OpenColorIO file rules. */ STRNCPY_UTF8(new_colorspace, filepath_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_UTF8(new_colorspace, role_colorspace); } } if (r_colorspace) { BLI_strncpy_utf8(r_colorspace, new_colorspace, IM_MAX_SPACE); } if (r_colorspace) { if (ibuf->byte_data() != nullptr && ibuf->float_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(new_colorspace); } } bool is_data = (r_colorspace && IMB_colormanagement_space_name_is_data(new_colorspace)); ImBufFlags alpha_flags = flag_is_set(flags, ImBufFlags::AlphaDetect) ? ibuf->flags : flags; if (is_data || flag_is_set(flags, ImBufFlags::AlphaChannelPacked)) { /* Don't touch alpha. */ ibuf->flags |= ImBufFlags::AlphaChannelPacked; } else if (flag_is_set(flags, ImBufFlags::AlphaIgnore)) { /* Make opaque. */ IMB_rectfill_alpha(ibuf, 1.0f); ibuf->flags |= ImBufFlags::AlphaIgnore; } else { if (flag_is_set(alpha_flags, ImBufFlags::AlphaPremul)) { if (ibuf->byte_data()) { IMB_unpremultiply_alpha(ibuf); } else { /* pass, floats are expected to be premul */ } } else { if (ibuf->float_data()) { IMB_premultiply_alpha(ibuf); } else { /* pass, bytes are expected to be straight */ } } } if (flag_is_set(flags, ImBufFlags::NoColorspaceConvert)) { if (ibuf->float_data() != nullptr) { ibuf->float_buffer.colorspace = colormanage_colorspace_get_named(new_colorspace); } } else { colormanage_imbuf_make_linear(ibuf, new_colorspace, ColorManagedFileOutput::Image); } } ImBuf *IMB_load_image_from_memory(const uchar *mem, const size_t size, const ImBufFlags flags, const char *descr, const char *filepath, char r_colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; const ImFileType *type; if (mem == nullptr) { CLOG_ERROR(&LOG, "%s: nullptr pointer", __func__); return nullptr; } ImFileColorSpace file_colorspace; for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { if (type->load) { ibuf = type->load(mem, size, flags, file_colorspace); if (ibuf) { imb_handle_colorspace_and_alpha(ibuf, flags, filepath, file_colorspace, r_colorspace); return ibuf; } } } if (!flag_is_set(flags, ImBufFlags::Test)) { CLOG_ERROR(&LOG, "%s: unknown file-format (%s)", __func__, descr); } return nullptr; } ImBuf *IMB_load_image_from_file_descriptor(const int file, const ImBufFlags flags, const char *filepath, char r_colorspace[IM_MAX_SPACE]) { ImBuf *ibuf = nullptr; if (file == -1) { return nullptr; } BLI_mmap_file *mmap_file = BLI_mmap_open(file); if (mmap_file == nullptr) { CLOG_ERROR(&LOG, "%s: couldn't get mapping for \"%s\"", __func__, filepath); return nullptr; } const uchar *mem = static_cast(BLI_mmap_get_pointer(mmap_file)); const size_t size = BLI_mmap_get_length(mmap_file); ibuf = IMB_load_image_from_memory(mem, size, flags, filepath, filepath, r_colorspace); /* If we got an image but mmap encountered an error, * free the image and return nullptr as it could be corrupted. */ if (ibuf != nullptr && BLI_mmap_any_io_error(mmap_file)) { IMB_freeImBuf(ibuf); ibuf = nullptr; } BLI_mmap_free(mmap_file); return ibuf; } ImBuf *IMB_load_image_from_filepath(const char *filepath, const ImBufFlags flags, char r_colorspace[IM_MAX_SPACE]) { ImBuf *ibuf; int file; BLI_assert(!BLI_path_is_rel(filepath)); file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); if (file == -1) { return nullptr; } ibuf = IMB_load_image_from_file_descriptor(file, flags, filepath, r_colorspace); if (ibuf) { ibuf->filepath = filepath; } close(file); return ibuf; } ImBuf *IMB_thumb_load_image(const char *filepath, const size_t max_thumb_size, char r_colorspace[IM_MAX_SPACE], const IMBThumbLoadFlags load_flags) { const ImFileType *type = IMB_file_type_from_ftype(IMB_test_image_type(filepath)); if (type == nullptr) { return nullptr; } ImBuf *ibuf = nullptr; ImBufFlags flags = ImBufFlags::ByteData | ImBufFlags::Metadata; /* Size of the original image. */ size_t width = 0; size_t height = 0; if (type->load_filepath_thumbnail) { ImFileColorSpace file_colorspace; ibuf = type->load_filepath_thumbnail( filepath, flags, max_thumb_size, file_colorspace, &width, &height); if (ibuf) { imb_handle_colorspace_and_alpha(ibuf, flags, filepath, file_colorspace, r_colorspace); } } else { /* Skip images of other types if over 100MB. */ if (!flag_is_set(load_flags, IMBThumbLoadFlags::LoadLargeFiles)) { const size_t file_size = BLI_file_size(filepath); if (file_size != size_t(-1) && file_size > THUMB_SIZE_MAX) { return nullptr; } } ibuf = IMB_load_image_from_filepath(filepath, flags, r_colorspace); if (ibuf) { width = ibuf->x; height = ibuf->y; } } if (ibuf) { if (width > 0 && height > 0) { /* Save dimensions of original image into the thumbnail metadata. */ char cwidth[40]; char cheight[40]; SNPRINTF_UTF8(cwidth, "%zu", width); SNPRINTF_UTF8(cheight, "%zu", height); IMB_metadata_ensure(&ibuf->metadata); IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth); IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight); } } return ibuf; } } // namespace blender