/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * SPDX-FileCopyrightText: 2025 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup imbuf */ #pragma once #include "../gpu/GPU_texture.hh" #include "BLI_enum_flags.hh" #include "BLI_math_matrix_types.hh" #include "BLI_span.hh" #include "BLI_vector.hh" #include "IMB_imbuf_types.hh" namespace blender { struct ImBuf; struct rctf; struct rcti; struct ImageFormatData; struct Stereo3dFormat; /** * Module init/exit. */ void IMB_init(); void IMB_exit(); /** * Module GPU context management. */ void IMB_ensure_gpu_context(); void IMB_activate_gpu_context(); void IMB_deactivate_gpu_context(); /** * Load image. */ ImBuf *IMB_load_image_from_memory(const unsigned char *mem, size_t size, ImBufFlags flags, const char *descr, const char *filepath = nullptr, char r_colorspace[IM_MAX_SPACE] = nullptr); ImBuf *IMB_load_image_from_file_descriptor(int file, ImBufFlags flags, const char *filepath = nullptr, char r_colorspace[IM_MAX_SPACE] = nullptr); ImBuf *IMB_load_image_from_filepath(const char *filepath, ImBufFlags flags, char r_colorspace[IM_MAX_SPACE] = nullptr); /** * Save image. */ bool IMB_save_image(ImBuf *ibuf, const char *filepath, ImBufFlags flags); Vector IMB_save_image_to_buffer(ImBuf *ibuf, ImBufFlags flags); /** * Test image file. */ bool IMB_test_image(const char *filepath); bool IMB_test_image_type_matches(const char *filepath, eImbFileType filetype); eImbFileType IMB_test_image_type_from_memory(const unsigned char *buf, size_t buf_size); eImbFileType IMB_test_image_type(const char *filepath); /** * Return true if the file type is supported (compiled in). */ bool IMB_ftype_is_supported(eImbFileType ftype); /** * Return the string identifier for a file type, or nullptr if not found. */ const char *IMB_ftype_to_id(eImbFileType ftype); /** * Return the file type enum value for a string identifier, or #IMB_FTYPE_NONE if not found. */ eImbFileType IMB_ftype_from_id(const char *id); /** * Return the null-terminated list of extensions for a file type, or nullptr if not found. */ const char **IMB_ftype_file_extensions(eImbFileType ftype); /** * Return the read capability flags for a file type. */ eImFileTypeCapability IMB_ftype_capability_read(eImbFileType ftype); /** * Return the write capability flags for a file type. */ eImFileTypeCapability IMB_ftype_capability_write(eImbFileType ftype); /** * Load thumbnail image. */ enum class IMBThumbLoadFlags { Zero = 0, /** Normally files larger than 100MB are not loaded for thumbnails, except when this flag is set. */ LoadLargeFiles = (1 << 0), }; ENUM_OPERATORS(IMBThumbLoadFlags); ImBuf *IMB_thumb_load_image(const char *filepath, size_t max_thumb_size, char colorspace[IM_MAX_SPACE], IMBThumbLoadFlags load_flags = IMBThumbLoadFlags::Zero); /** * Allocate and free image buffer. */ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, ImBufFlags flags); void IMB_freeImBuf(ImBuf *ibuf); /** * Initialize given ImBuf. * * Use in cases when temporary image buffer is allocated on stack. */ bool IMB_initImBuf(ImBuf *ibuf, unsigned int x, unsigned int y, ImBufFlags flags); /** * Create a copy of a pixel buffer and wrap it to a new ImBuf * (transferring ownership to the in imbuf). */ ImBuf *IMB_allocFromBufferOwn(uint8_t *byte_buffer, float *float_buffer, unsigned int w, unsigned int h, unsigned int channels); /** * Create a copy of a pixel buffer and wrap it to a new ImBuf */ ImBuf *IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, unsigned int w, unsigned int h, unsigned int channels); /** * Assign the GPU texture of the buffer to the given texture. The current GPU texture is released. * * \note Does not modify the topology (width, height, number of channels). */ void IMB_assign_gpu_texture(ImBuf *ibuf, gpu::Texture *texture); /** * Reads the GPU data texture of the image buffer if it exists and assigns the data to the float * buffer. This is only done if the buffer has the IB_HOST_BUFFER_INVALID flag is set, which is * then reset after the function executes. * * \warning Not thread-safe, so callee should worry about thread locks. */ void IMB_ensure_host_buffer(ImBuf *ibuf); /** * Increase reference count to imbuf * (to delete an imbuf you have to call freeImBuf as many times as it * is referenced) */ void IMB_refImBuf(ImBuf *ibuf); ImBuf *IMB_makeSingleUser(ImBuf *ibuf); ImBuf *IMB_dupImBuf(const ImBuf *ibuf1); /** * Approximate size of ImBuf in memory */ size_t IMB_get_size_in_memory(const ImBuf *ibuf); /** * \brief Get the length of the data of the given image buffer in pixels. * * This is the width * the height of the image buffer. * This function is preferred over `ibuf->x * ibuf->y` due to 32 bit int overflow * issues when working with very large resolution images. */ size_t IMB_get_pixel_count(const ImBuf *ibuf); enum IMB_BlendMode { IMB_BLEND_MIX = 0, IMB_BLEND_ADD = 1, IMB_BLEND_SUB = 2, IMB_BLEND_MUL = 3, IMB_BLEND_LIGHTEN = 4, IMB_BLEND_DARKEN = 5, IMB_BLEND_ERASE_ALPHA = 6, IMB_BLEND_ADD_ALPHA = 7, IMB_BLEND_OVERLAY = 8, IMB_BLEND_HARDLIGHT = 9, IMB_BLEND_COLORBURN = 10, IMB_BLEND_LINEARBURN = 11, IMB_BLEND_COLORDODGE = 12, IMB_BLEND_SCREEN = 13, IMB_BLEND_SOFTLIGHT = 14, IMB_BLEND_PINLIGHT = 15, IMB_BLEND_VIVIDLIGHT = 16, IMB_BLEND_LINEARLIGHT = 17, IMB_BLEND_DIFFERENCE = 18, IMB_BLEND_EXCLUSION = 19, IMB_BLEND_HUE = 20, IMB_BLEND_SATURATION = 21, IMB_BLEND_LUMINOSITY = 22, IMB_BLEND_COLOR = 23, IMB_BLEND_INTERPOLATE = 24, IMB_BLEND_COPY_RGB = 1001, IMB_BLEND_COPY_ALPHA = 1002, }; void IMB_blend_color_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], IMB_BlendMode mode); void IMB_blend_color_float(float dst[4], const float src1[4], const float src2[4], IMB_BlendMode mode); void IMB_blend_color_float(MutableSpan dst, Span src1, Span src2, IMB_BlendMode mode); /** * Copy a rectangle of pixel data from one image buffer to another. The source and destination * buffers are described by the pointers and corresponding 2D sizes. They must not reference the * same memory. */ void IMB_copy_rect(float *dst, const int2 &dst_size, const float *src, const int2 &src_size, int channels, const int2 &src_rect_pos, const int2 &dst_rect_pos, const int2 &rect_size); void IMB_copy_rect(uchar *dst, const int2 &dst_size, const uchar *src, const int2 &src_size, const int2 &src_rect_pos, const int2 &dst_rect_pos, const int2 &rect_size); /** * In-place image crop. `rect` is *inclusive*. */ void IMB_crop(ImBuf *ibuf, const int2 &rect_pos, const int2 &rect_size); /** * Copy a rectangle of pixel data from one image buffer to another. Data outside of the destination * rectangle is not written to. */ void IMB_copy_rect(ImBuf *dst, const ImBuf *src, const int2 &src_rect_pos, const int2 &dst_rect_pos, const int2 &rect_size); /** * In-place size setting (caller must fill in buffer contents). */ void IMB_rect_size_set(ImBuf *ibuf, const uint size[2]); void IMB_rectclip(ImBuf *dbuf, const ImBuf *sbuf, int *destx, int *desty, int *srcx, int *srcy, int *width, int *height); void IMB_rectblend(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, unsigned short *dmask, const unsigned short *curvemask, const unsigned short *texmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, IMB_BlendMode mode, bool accumulate); void IMB_rectblend_threaded(ImBuf *dbuf, const ImBuf *obuf, const ImBuf *sbuf, unsigned short *dmask, const unsigned short *curvemask, const unsigned short *texmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, IMB_BlendMode mode, bool accumulate); enum eIMBInterpolationFilterMode { IMB_FILTER_NEAREST, IMB_FILTER_BILINEAR, IMB_FILTER_CUBIC_BSPLINE, IMB_FILTER_CUBIC_MITCHELL, IMB_FILTER_BOX, }; #define FILTER_MASK_NULL 0 #define FILTER_MASK_MARGIN 1 #define FILTER_MASK_USED 2 void IMB_mask_filter_extend(char *mask, int width, int height); void IMB_mask_clear(ImBuf *ibuf, const char *mask, int val); /** * If alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0 * When a mask is given, the mask will be used instead of the alpha channel, where only * pixels with a mask value of 0 will be written to, and only pixels with a mask value of 1 * will be used for the average. The mask will be set to one for the pixels which were written. */ void IMB_filter_extend(ImBuf *ibuf, char *mask, int filter); void IMB_filtery(ImBuf *ibuf); /** Interpolation filter used by `IMB_scale`. */ enum class IMBScaleFilter { /** No filtering (point sampling). This is fastest but lowest quality. */ Nearest, /** * Bilinear filter: each pixel in result image interpolates between 2x2 pixels of source image. */ Bilinear, /** * Box filter. Behaves exactly like Bilinear when scaling up, * better results when scaling down by more than 2x. */ Box, }; void IMB_scale_box(const float *src_buffer, int2 src_size, int channels, float *dst_buffer, int2 dst_size, bool threaded); void IMB_scale_box(const uchar *src_buffer, int2 src_size, int channels, uchar *dst_buffer, int2 dst_size, bool threaded); /** * Scale/resize image to new dimensions. * Return true if \a ibuf is modified. */ bool IMB_scale(ImBuf *ibuf, int2 new_size, IMBScaleFilter filter, bool threaded = true); inline bool IMB_scale( ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded = true) { return IMB_scale(ibuf, int2(newx, newy), filter, threaded); } /** * Scale/resize image to new dimensions, into a newly created result image. * Metadata of input image (if any) is copied into the result image. */ ImBuf *IMB_scale_into_new(const ImBuf *ibuf, int2 new_size, IMBScaleFilter filter, bool threaded = true); inline ImBuf *IMB_scale_into_new(const ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded = true) { return IMB_scale_into_new(ibuf, int2(newx, newy), filter, threaded); } /** * Test if color-space conversions of pixels in buffer need to take into account alpha. */ bool IMB_alpha_affects_rgb(const ImBuf *ibuf); /** * Create char buffer, color corrected if necessary, for ImBufs that lack one. */ void IMB_byte_from_float(ImBuf *ibuf); void IMB_float_from_byte_ex(ImBuf *dst, const ImBuf *src, const rcti *region_to_update); void IMB_float_from_byte(ImBuf *ibuf); void IMB_color_to_bw(ImBuf *ibuf); void IMB_saturation(ImBuf *ibuf, float sat); /** * Convert float pixels to byte pixels. * \param dest Destination, always 4 channel RGBA, non-premultiplied. * \param src Source. * \param src_channels Source channels (1, 3, 4). * \param dither Amount of dithering to apply to destination. * \param predivide Is source alpha premultiplied. * \param width Width in pixels. * \param height Height in pixels. * \param stride Row stride in pixels. */ void IMB_buffer_byte_from_float(unsigned char *dest, const float *src, int src_channels, float dither, bool predivide, int width, int height, int stride, int start_y = 0); /** * Same as #IMB_buffer_byte_from_float, but only writes destination * pixels where corresponding mask value is #FILTER_MASK_USED. */ void IMB_buffer_byte_from_float_mask(unsigned char *dest, const float *src, int src_channels, float dither, int width, int height, const char *mask); /** * Convert byte pixels to float pixels. * \param dest Destination, always 4 channel RGBA, non-premultiplied. * \param src Source, always 4 channel RGBA, non-premultiplied. * \param width Width in pixels. * \param height Height in pixels. * \param dest_stride Destination row stride in pixels. * \param src_stride Source row stride in pixels. */ void IMB_buffer_float_from_byte( float *dest, const unsigned char *src, int width, int height, int dest_stride, int src_stride); /** * Convert 1/3/4 channel float pixels to 4 channel (RGBA) float pixels. * \param dest Destination, always 4 channel. * \param src Source. * \param src_channels Source channels (1, 3, 4). * \param width Width in pixels. * \param height Height in pixels. */ void IMB_buffer_float_rgba_from_float( float *dest, const float *src, int src_channels, int width, int height); /** * Same as #IMB_buffer_float_rgba_from_float, but only writes destination * pixels where corresponding mask value is #FILTER_MASK_USED. */ void IMB_buffer_float_rgba_from_float_mask( float *dest, const float *src, int src_channels, int width, int height, const char *mask); void IMB_buffer_float_rgba_srgb_to_linear(float *buffer, int width, int height); void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]); void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]); void IMB_flipx(ImBuf *ibuf); void IMB_flipy(ImBuf *ibuf); /** Rotate by 90 degree increments. Returns true if the ImBuf is altered. */ bool IMB_rotate_orthogonal(ImBuf *ibuf, int degrees); /* Pre-multiply alpha. */ void IMB_premultiply_alpha(ImBuf *ibuf); void IMB_unpremultiply_alpha(ImBuf *ibuf); /** * Replace pixels of entire image with solid color. * \param drect: An image to be filled with color. It must be 4 channel image. * \param col: RGBA color, which is assigned directly to both byte (via scaling) and float buffers. */ void IMB_rectfill(ImBuf *drect, const float col[4]); /** * Blend pixels of image area with solid color. * * \param ibuf: an image to be filled with color. It must be 4 channel image. * \param scene_linear_color: RGBA color in scene linear colorspace. For byte buffers, this is * converted to the byte buffer colorspace. * \param x1, y1, x2, y2: (x1, y1) defines starting point * of the rectangular area to be filled, (x2, y2) is the end point. Note that values are allowed to * be loosely ordered, which means that x2 is allowed to be lower than x1, as well as y2 is allowed * to be lower than y1. No matter the order the area between x1 and x2, and y1 and y2 is filled. */ void IMB_rectfill_area( ImBuf *ibuf, const float scene_linear_color[4], int x1, int y1, int x2, int y2); void IMB_rectfill_alpha(ImBuf *ibuf, float value); /** * Exported for image tools in blender, to quickly allocate 32 bits rect. */ void *imb_alloc_pixels(unsigned int x, unsigned int y, unsigned int channels, size_t typesize, bool initialize_pixels, const char *alloc_name); /** * Allocate storage for byte type pixels. * If the image already contains byte data storage, it is freed first. */ bool IMB_alloc_byte_pixels(ImBuf *ibuf, bool initialize_pixels = true); /** * Deallocate image byte storage. */ void IMB_free_byte_pixels(ImBuf *ibuf); /** * Allocate storage for float type pixels. * If the image already contains float data storage, it is freed first. */ bool IMB_alloc_float_pixels(ImBuf *ibuf, unsigned int channels, bool initialize_pixels = true); /** * Deallocate image float storage. */ void IMB_free_float_pixels(ImBuf *ibuf); /** Deallocate all CPU side data storage (byte, float). */ void IMB_free_all_data(ImBuf *ibuf); /** * Free the GPU textures of the given image buffer, leaving the CPU buffers unchanged. * The ibuf can be nullptr, in which case the function does nothing. */ void IMB_free_gpu_textures(ImBuf *ibuf); /** * \brief Transform modes to use for IMB_transform function. * * These are not flags as the combination of cropping and repeat can lead to different expectation. */ enum eIMBTransformMode { /** \brief Do not crop or repeat. */ IMB_TRANSFORM_MODE_REGULAR = 0, /** \brief Crop the source buffer. */ IMB_TRANSFORM_MODE_CROP_SRC = 1, /** \brief Wrap repeat the source buffer. Only supported in with nearest filtering. */ IMB_TRANSFORM_MODE_WRAP_REPEAT = 2, }; /** * \brief Transform source image buffer onto destination image buffer using a transform matrix. * * \param src: Image buffer to read from. * \param dst: Image buffer to write to. rect or rect_float must already be initialized. * - dst buffer must be a 4 channel buffers. * - Only one data type buffer will be used (rect_float has priority over rect) * \param mode: Cropping/Wrap repeat effect to apply during transformation. * \param filter: Interpolation to use during sampling. * \param transform_matrix: Transformation matrix to use. * The given matrix should transform between dst pixel space to src pixel space. * One unit is one pixel. * \param src_crop: Cropping region how to crop the source buffer. Should only be passed when mode * is set to #IMB_TRANSFORM_MODE_CROP_SRC. For any other mode this should be empty. * * During transformation no data/color conversion will happens. * When transforming between float images the number of channels of the source buffer may be * between 1 and 4. When source buffer has one channel the data will be read as a gray scale value. */ void IMB_transform(const ImBuf *src, ImBuf *dst, eIMBTransformMode mode, eIMBInterpolationFilterMode filter, const float3x3 &transform_matrix, const rctf *src_crop); /* Creates a GPU texture from the given image buffer and name. If use_high_bitdepth is true, float * image buffers will be stored in full float textures, otherwise, they will be stored in half * float textures. If use_premult is true, the image buffer data will be stored premultiplied. If * limit_size is true, the texture will be scaled down to match the maximum size allowed by the * U.glreslimit user preferences setting. */ gpu::Texture *IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, bool use_premult, const bool limit_size); gpu::TextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, bool high_bitdepth, bool use_grayscale); bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, gpu::TextureFormat *r_texture_format); /** * Ensures that values stored in the float rect can safely loaded into half float gpu textures. * * Does nothing when given image_buffer doesn't contain a float rect. */ void IMB_gpu_clamp_half_float(ImBuf *image_buffer); /** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). */ gpu::Texture *IMB_touch_gpu_texture(const char *name, ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth, bool use_grayscale); /** * Will update a #gpu::Texture using the content of the #ImBuf. Only one layer will be * updated. Will resize the ibuf if needed. Z is the layer to update. Unused if the texture is 2D. */ void IMB_update_gpu_texture_sub(gpu::Texture *tex, ImBuf *ibuf, int x, int y, int z, int w, int h, bool use_high_bitdepth, bool use_grayscale, bool use_premult); void IMB_stereo3d_write_dimensions( char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height); void IMB_stereo3d_read_dimensions( char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height); /** * Left/right are always float. */ ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right); /** * Reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right). */ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, ImBuf **r_ibuf_left, ImBuf **r_ibuf_right); } // namespace blender