diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-01-26 23:57:44 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-01-27 00:03:58 +0300 |
commit | 4226c484bdbe7336f1221094916fcdfb12850034 (patch) | |
tree | 33428e72be40105c222ca77935ee1554b702facc /source/blender/imbuf | |
parent | 55a6a8900aec81e94f4d82401d6051e3b5507c0e (diff) | |
parent | af87b6d8cb75d9d625378dee25d726a0d55f75c6 (diff) |
Merge branch 'draw-viewport-data' into eevee-rewrite
# Conflicts:
# release/scripts/startup/bl_ui/properties_data_camera.py
# source/blender/blenkernel/BKE_camera.h
# source/blender/blenkernel/BKE_node.h
# source/blender/blenkernel/intern/camera.c
# source/blender/blenlib/BLI_float2.hh
# source/blender/blenlib/BLI_float3.hh
# source/blender/blenlib/BLI_float4.hh
# source/blender/blenlib/BLI_math_geom.h
# source/blender/blenlib/intern/math_geom.c
# source/blender/draw/CMakeLists.txt
# source/blender/draw/engines/basic/basic_engine.c
# source/blender/draw/engines/eevee/eevee_cryptomatte.c
# source/blender/draw/engines/eevee/eevee_effects.c
# source/blender/draw/engines/eevee/eevee_engine.c
# source/blender/draw/engines/eevee/eevee_lightcache.c
# source/blender/draw/engines/eevee/eevee_lightcache.h
# source/blender/draw/engines/eevee/eevee_lightprobes.c
# source/blender/draw/engines/eevee/eevee_lights.c
# source/blender/draw/engines/eevee/eevee_materials.c
# source/blender/draw/engines/eevee/eevee_motion_blur.c
# source/blender/draw/engines/eevee/eevee_occlusion.c
# source/blender/draw/engines/eevee/eevee_private.h
# source/blender/draw/engines/eevee/eevee_render.c
# source/blender/draw/engines/eevee/eevee_renderpasses.c
# source/blender/draw/engines/eevee/eevee_sampling.c
# source/blender/draw/engines/eevee/eevee_screen_raytrace.c
# source/blender/draw/engines/eevee/eevee_shaders.c
# source/blender/draw/engines/eevee/eevee_shadows.c
# source/blender/draw/engines/eevee/eevee_shadows_cube.c
# source/blender/draw/engines/eevee/eevee_temporal_sampling.c
# source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
# source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
# source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
# source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl
# source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
# source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl
# source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl
# source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
# source/blender/draw/engines/eevee/shaders/random_lib.glsl
# source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
# source/blender/draw/engines/eevee/shaders/surface_lib.glsl
# source/blender/draw/engines/eevee/shaders/surface_vert.glsl
# source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
# source/blender/draw/engines/external/external_engine.c
# source/blender/draw/engines/gpencil/gpencil_engine.c
# source/blender/draw/engines/image/image_engine.c
# source/blender/draw/engines/overlay/overlay_engine.c
# source/blender/draw/engines/select/select_engine.c
# source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
# source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl
# source/blender/draw/engines/workbench/workbench_engine.c
# source/blender/draw/engines/workbench/workbench_shader.c
# source/blender/draw/intern/DRW_render.h
# source/blender/draw/intern/draw_debug.h
# source/blender/draw/intern/draw_manager_data.c
# source/blender/draw/intern/draw_manager_exec.c
# source/blender/draw/intern/draw_view_data.h
# source/blender/gpu/CMakeLists.txt
# source/blender/gpu/GPU_material.h
# source/blender/gpu/GPU_shader.h
# source/blender/gpu/GPU_state.h
# source/blender/gpu/GPU_vertex_buffer.h
# source/blender/gpu/intern/gpu_codegen.c
# source/blender/gpu/intern/gpu_material.c
# source/blender/gpu/intern/gpu_material_library.h
# source/blender/gpu/intern/gpu_node_graph.c
# source/blender/gpu/intern/gpu_texture_private.hh
# source/blender/gpu/intern/gpu_vertex_buffer.cc
# source/blender/gpu/opengl/gl_shader.cc
# source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl
# source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
# source/blender/nodes/shader/node_shader_tree.cc
# source/blender/nodes/shader/nodes/node_shader_background.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c
# source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c
# source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
# source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
# source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
# source/blender/nodes/shader/nodes/node_shader_emission.cc
# source/blender/nodes/shader/nodes/node_shader_holdout.cc
# source/blender/nodes/shader/nodes/node_shader_output_material.cc
# source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c
# source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
# source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
# source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc
# source/blender/nodes/shader/nodes/node_shader_volume_principled.cc
# source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc
# source/blender/render/RE_pipeline.h
# source/blender/render/intern/initrender.c
Diffstat (limited to 'source/blender/imbuf')
53 files changed, 1693 insertions, 808 deletions
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index be0e364c85f..479f4f7e82f 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/thumbs.c intern/thumbs_blend.c intern/thumbs_font.c + intern/transform.cc intern/util.c intern/util_gpu.c intern/writeimage.c diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 53b0e295385..30f2f24447f 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -45,7 +45,9 @@ struct bContext; struct ColorManagedDisplay; struct ColorSpace; -/* ** Generic functions ** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Functions + * \{ */ void IMB_colormanagement_check_file_config(struct Main *bmain); @@ -67,13 +69,35 @@ bool IMB_colormanagement_space_is_scene_linear(struct ColorSpace *colorspace); bool IMB_colormanagement_space_is_srgb(struct ColorSpace *colorspace); bool IMB_colormanagement_space_name_is_data(const char *name); +/** + * Convert a float RGB triplet to the correct luminance weighted average. + * + * Gray-scale, or Luma is a distillation of RGB data values down to a weighted average + * based on the luminance positions of the red, green, and blue primaries. + * Given that the internal reference space may be arbitrarily set, any + * effort to glean the luminance coefficients must be aware of the reference + * space primaries. + * + * See http://wiki.blender.org/index.php/User:Nazg-gul/ColorManagement#Luminance + */ BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]); +/** + * Byte equivalent of #IMB_colormanagement_get_luminance(). + */ BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]); BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]); BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]); const float *IMB_colormanagement_get_xyz_to_rgb(void); -/* ** Color space transformation functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Space Transformation Functions + * \{ */ + +/** + * Convert the whole buffer from specified by name color space to another. + */ void IMB_colormanagement_transform(float *buffer, int width, int height, @@ -81,6 +105,10 @@ void IMB_colormanagement_transform(float *buffer, const char *from_colorspace, const char *to_colorspace, bool predivide); +/** + * Convert the whole buffer from specified by name color space to another + * will do threaded conversion. + */ void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, @@ -88,6 +116,9 @@ void IMB_colormanagement_transform_threaded(float *buffer, const char *from_colorspace, const char *to_colorspace, bool predivide); +/** + * Similar to #IMB_colormanagement_transform_threaded, but operates on byte buffer. + */ void IMB_colormanagement_transform_byte(unsigned char *buffer, int width, int height, @@ -100,6 +131,9 @@ void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer, int channels, const char *from_colorspace, const char *to_colorspace); +/** + * Similar to #IMB_colormanagement_transform_byte_threaded, but gets float buffer from display one. + */ void IMB_colormanagement_transform_from_byte(float *float_buffer, unsigned char *byte_buffer, int width, @@ -118,12 +152,20 @@ void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspace, const char *to_colorspace); +/** + * Convert pixel from specified by descriptor color space to scene linear + * used by performance-critical areas such as renderer and baker. + */ void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], struct ColorSpace *colorspace); void IMB_colormanagement_colorspace_to_scene_linear_v4(float pixel[4], bool predivide, struct ColorSpace *colorspace); +/** + * Same as #IMB_colormanagement_colorspace_to_scene_linear_v4, + * but converts colors in opposite direction. + */ void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], struct ColorSpace *colorspace); @@ -135,29 +177,51 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, bool predivide); void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, - const int x, - const int y, - const int width, - const int height, + int x, + int y, + int width, + int height, const struct ImBuf *ibuf, - const bool compress_as_srgb, - const bool store_premultiplied); + bool compress_as_srgb, + bool store_premultiplied); void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, - const int offset_x, - const int offset_y, - const int width, - const int height, + int offset_x, + int offset_y, + int width, + int height, const struct ImBuf *ibuf, - const bool store_premultiplied); - + bool store_premultiplied); + +/** + * Conversion between color picking role. Typically we would expect such a + * requirements: + * - It is approximately perceptually linear, so that the HSV numbers and + * the HSV cube/circle have an intuitive distribution. + * - It has the same gamut as the scene linear color space. + * - Color picking values 0..1 map to scene linear values in the 0..1 range, + * so that picked albedo values are energy conserving. + */ void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]); void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]); +/** + * Conversion between sRGB, for rare cases like hex color or copy/pasting + * between UI theme and scene linear colors. + */ void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3]); void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3]); +/** + * Convert pixel from scene linear to display space using default view + * used by performance-critical areas such as color-related widgets where we want to reduce + * amount of per-widget allocations. + */ void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], struct ColorManagedDisplay *display); +/** + * Same as #IMB_colormanagement_scene_linear_to_display_v3, + * but converts color in opposite direction. + */ void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], struct ColorManagedDisplay *display); @@ -178,6 +242,18 @@ void IMB_colormanagement_imbuf_make_display_space( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); +/** + * Prepare image buffer to be saved on disk, applying color management if needed + * color management would be applied if image is saving as render result and if + * file format is not expecting float buffer to be in linear space (currently + * JPEG2000 and TIFF are such formats -- they're storing image as float but + * file itself stores applied color space). + * + * Both byte and float buffers would contain applied color space, and result's + * float_colorspace would be set to display color space. This should be checked + * in image format write callback and if float_colorspace is not NULL, no color + * space transformation should be applied on this buffer. + */ struct ImBuf *IMB_colormanagement_imbuf_for_write( struct ImBuf *ibuf, bool save_as_render, @@ -196,7 +272,11 @@ void IMB_colormanagement_buffer_make_display_space( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); -/* ** Public display buffers interfaces ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Display Buffers Interfaces + * \{ */ void IMB_colormanagement_display_settings_from_ctx( const struct bContext *C, @@ -207,11 +287,17 @@ const char *IMB_colormanagement_get_display_colorspace_name( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); +/** + * Acquire display buffer for given image buffer using specified view and display settings. + */ unsigned char *IMB_display_buffer_acquire( struct ImBuf *ibuf, const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, void **cache_handle); +/** + * Same as #IMB_display_buffer_acquire but gets view and display settings from context. + */ unsigned char *IMB_display_buffer_acquire_ctx(const struct bContext *C, struct ImBuf *ibuf, void **cache_handle); @@ -227,24 +313,47 @@ void IMB_display_buffer_transform_apply(unsigned char *display_buffer, void IMB_display_buffer_release(void *cache_handle); -/* ** Display functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display Functions + * \{ */ + int IMB_colormanagement_display_get_named_index(const char *name); const char *IMB_colormanagement_display_get_indexed_name(int index); const char *IMB_colormanagement_display_get_default_name(void); +/** + * Used by performance-critical pixel processing areas, such as color widgets. + */ struct ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name); const char *IMB_colormanagement_display_get_none_name(void); const char *IMB_colormanagement_display_get_default_view_transform_name( struct ColorManagedDisplay *display); -/* ** View functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Functions + * \{ */ + int IMB_colormanagement_view_get_named_index(const char *name); const char *IMB_colormanagement_view_get_indexed_name(int index); -/* ** Look functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Look Functions + * \{ */ + int IMB_colormanagement_look_get_named_index(const char *name); const char *IMB_colormanagement_look_get_indexed_name(int index); -/* ** Color space functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Space Functions + * \{ */ + int IMB_colormanagement_colorspace_get_named_index(const char *name); const char *IMB_colormanagement_colorspace_get_indexed_name(int index); const char *IMB_colormanagement_view_get_default_name(const char *display_name); @@ -252,7 +361,12 @@ const char *IMB_colormanagement_view_get_default_name(const char *display_name); void IMB_colormanagement_colorspace_from_ibuf_ftype( struct ColorManagedColorspaceSettings *colorspace_settings, struct ImBuf *ibuf); -/* ** RNA helper functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name RNA Helper Functions + * \{ */ + void IMB_colormanagement_display_items_add(struct EnumPropertyItem **items, int *totitem); void IMB_colormanagement_view_items_add(struct EnumPropertyItem **items, int *totitem, @@ -262,7 +376,12 @@ void IMB_colormanagement_look_items_add(struct EnumPropertyItem **items, const char *view_name); void IMB_colormanagement_colorspace_items_add(struct EnumPropertyItem **items, int *totitem); -/* ** Tile-based buffer management ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tile-based Buffer Management + * \{ */ + void IMB_partial_display_buffer_update(struct ImBuf *ibuf, const float *linear_buffer, const unsigned char *byte_buffer, @@ -293,7 +412,12 @@ void IMB_partial_display_buffer_update_threaded( void IMB_partial_display_buffer_update_delayed( struct ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax); -/* ** Pixel processor functions ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pixel Processor Functions + * \{ */ + struct ColormanageProcessor *IMB_colormanagement_display_processor_new( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); @@ -321,17 +445,40 @@ void IMB_colormanagement_processor_apply_byte(struct ColormanageProcessor *cm_pr int channels); void IMB_colormanagement_processor_free(struct ColormanageProcessor *cm_processor); -/* ** OpenGL drawing routines using GLSL for color space transform ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name OpenGL Drawing Routines Using GLSL for Color Space Transform + * \{ */ -/* Test if GLSL drawing is supported for combination of graphics card and this configuration */ +/** + * Test if GLSL drawing is supported for combination of graphics card and this configuration. + */ bool IMB_colormanagement_support_glsl_draw(const struct ColorManagedViewSettings *view_settings); -/* Configures GLSL shader for conversion from scene linear to display space */ +/** + * Configures GLSL shader for conversion from scene linear to display space. + */ bool IMB_colormanagement_setup_glsl_draw( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, float dither, bool predivide); -/* Same as above, but display space conversion happens from a specified space */ +/** + * \note Same as IMB_colormanagement_setup_glsl_draw, + * but display space conversion happens from a specified space. + * + * Configures GLSL shader for conversion from specified to + * display color space + * + * Will create appropriate OCIO processor and setup GLSL shader, + * so further 2D texture usage will use this conversion. + * + * When there's no need to apply transform on 2D textures, use + * IMB_colormanagement_finish_glsl_draw(). + * + * This is low-level function, use ED_draw_imbuf_ctx if you + * only need to display given image buffer + */ bool IMB_colormanagement_setup_glsl_draw_from_space( const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, @@ -339,20 +486,31 @@ bool IMB_colormanagement_setup_glsl_draw_from_space( float dither, bool predivide, bool do_overlay_merge); -/* Same as setup_glsl_draw, but color management settings are guessing from a given context */ +/** + * Same as setup_glsl_draw, but color management settings are guessing from a given context. + */ bool IMB_colormanagement_setup_glsl_draw_ctx(const struct bContext *C, float dither, bool predivide); -/* Same as setup_glsl_draw_from_space, - * but color management settings are guessing from a given context. */ +/** + * Same as `setup_glsl_draw_from_space`, + * but color management settings are guessing from a given context. + */ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const struct bContext *C, struct ColorSpace *colorspace, float dither, bool predivide); -/* Finish GLSL-based display space conversion */ +/** + * Finish GLSL-based display space conversion. + */ void IMB_colormanagement_finish_glsl_draw(void); -/* ** View transform ** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Transform + * \{ */ + void IMB_colormanagement_init_default_view_settings( struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); @@ -368,6 +526,8 @@ enum { COLOR_ROLE_DATA, }; +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index dd8e6549e24..a557d7dc6d1 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -199,6 +199,17 @@ bool addzbuffloatImBuf(struct ImBuf *ibuf); size_t IMB_get_size_in_memory(struct ImBuf *ibuf); /** + * \brief Get the length of the rect of the given image buffer in terms of pixels. + * + * This is the width * the height of the image buffer. + * This function is preferred over `ibuf->x * ibuf->y` due to overflow issues when + * working with large resolution images (30kx30k). + * + * \attention Defined in allocimbuf.c + */ +size_t IMB_get_rect_len(const struct ImBuf *ibuf); + +/** * * \attention Defined in rectop.c */ @@ -244,8 +255,14 @@ void IMB_blend_color_float(float dst[4], const float src2[4], IMB_BlendMode mode); +/** + * In-place image crop. + */ void IMB_rect_crop(struct ImBuf *ibuf, const struct rcti *crop); +/** + * In-place size setting (caller must fill in buffer contents). + */ void IMB_rect_size_set(struct ImBuf *ibuf, const uint size[2]); void IMB_rectclip(struct ImBuf *dbuf, @@ -342,7 +359,9 @@ typedef enum eIMBInterpolationFilterMode { IMB_FILTER_BILINEAR, } eIMBInterpolationFilterMode; -/* Defaults to BL_proxy within the directory of the animation. */ +/** + * Defaults to BL_proxy within the directory of the animation. + */ void IMB_anim_set_index_dir(struct anim *anim, const char *dir); void IMB_anim_get_fname(struct anim *anim, char *file, int size); @@ -352,21 +371,28 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim); struct IndexBuildContext; -/* Prepare context for proxies/time-codes builder. */ +/** + * Prepare context for proxies/time-codes builder + */ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality, const bool overwrite, - struct GSet *file_list); + struct GSet *file_list, + bool build_only_on_bad_performance); -/* Will rebuild all used indices and proxies at once. */ +/** + * Will rebuild all used indices and proxies at once. + */ void IMB_anim_index_rebuild(struct IndexBuildContext *context, short *stop, short *do_update, float *progress); -/* Finish rebuilding proxies/time-codes and free temporary contexts used. */ +/** + * Finish rebuilding proxies/time-codes and free temporary contexts used. + */ void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop); /** @@ -406,6 +432,7 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); int ismovie(const char *filepath); int IMB_anim_get_image_width(struct anim *anim); int IMB_anim_get_image_height(struct anim *anim); +bool IMB_get_gop_decode_time(struct anim *anim); /** * @@ -442,8 +469,20 @@ void IMB_free_anim(struct anim *anim); void IMB_filter(struct ImBuf *ibuf); void IMB_mask_filter_extend(char *mask, int width, int height); void IMB_mask_clear(struct 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(struct ImBuf *ibuf, char *mask, int filter); +/** + * Frees too (if there) and recreates new data. + */ void IMB_makemipmap(struct ImBuf *ibuf, int use_filter); +/** + * Thread-safe version, only recreates existing maps. + */ void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter); struct ImBuf *IMB_getmipmap(struct ImBuf *ibuf, int level); @@ -452,6 +491,9 @@ struct ImBuf *IMB_getmipmap(struct ImBuf *ibuf, int level); * \attention Defined in cache.c */ +/** + * Presumed to be called when no threads are running. + */ void IMB_tile_cache_params(int totthread, int maxmem); unsigned int *IMB_gettile(struct ImBuf *ibuf, int tx, int ty, int thread); void IMB_tiles_to_rect(struct ImBuf *ibuf); @@ -471,6 +513,8 @@ struct ImBuf *IMB_onehalf(struct ImBuf *ibuf1); /** * * \attention Defined in scaling.c + * + * Return true if \a ibuf is modified. */ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy); @@ -478,6 +522,9 @@ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy); * * \attention Defined in scaling.c */ +/** + * Return true if \a ibuf is modified. + */ bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy); /** @@ -491,7 +538,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); -bool IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf); +bool IMB_prepare_write_ImBuf(bool isfloat, struct ImBuf *ibuf); /** * @@ -499,7 +546,7 @@ bool IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf); */ bool IMB_ispic(const char *filepath); bool IMB_ispic_type_matches(const char *filepath, int filetype); -int IMB_ispic_type_from_memory(const unsigned char *buf, const size_t buf_size); +int IMB_ispic_type_from_memory(const unsigned char *buf, size_t buf_size); int IMB_ispic_type(const char *filepath); /** @@ -520,16 +567,27 @@ int imb_get_anim_type(const char *filepath); */ bool IMB_isfloat(const struct ImBuf *ibuf); -/* Do byte/float and colorspace conversions need to take alpha into account? */ +/** + * Test if color-space conversions of pixels in buffer need to take into account alpha. + */ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf); -/* create char buffer, color corrected if necessary, for ImBufs that lack one */ +/** + * Create char buffer, color corrected if necessary, for ImBufs that lack one. + */ void IMB_rect_from_float(struct ImBuf *ibuf); void IMB_float_from_rect(struct ImBuf *ibuf); +/** + * No profile conversion. + */ void IMB_color_to_bw(struct ImBuf *ibuf); void IMB_saturation(struct ImBuf *ibuf, float sat); -/* converting pixel buffers */ +/* Converting pixel buffers. */ + +/** + * Float to byte pixels, output 4-channel RGBA. + */ void IMB_buffer_byte_from_float(unsigned char *rect_to, const float *rect_from, int channels_from, @@ -541,6 +599,9 @@ void IMB_buffer_byte_from_float(unsigned char *rect_to, int height, int stride_to, int stride_from); +/** + * Float to byte pixels, output 4-channel RGBA. + */ void IMB_buffer_byte_from_float_mask(unsigned char *rect_to, const float *rect_from, int channels_from, @@ -551,6 +612,9 @@ void IMB_buffer_byte_from_float_mask(unsigned char *rect_to, int stride_to, int stride_from, char *mask); +/** + * Byte to float pixels, input and output 4-channel RGBA. + */ void IMB_buffer_float_from_byte(float *rect_to, const unsigned char *rect_from, int profile_to, @@ -560,6 +624,9 @@ void IMB_buffer_float_from_byte(float *rect_to, int height, int stride_to, int stride_from); +/** + * Float to float pixels, output 4-channel RGBA. + */ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, int channels_from, @@ -580,6 +647,9 @@ void IMB_buffer_float_from_float_threaded(float *rect_to, int height, int stride_to, int stride_from); +/** + * Float to float pixels, output 4-channel RGBA. + */ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from, @@ -588,6 +658,9 @@ void IMB_buffer_float_from_float_mask(float *rect_to, int stride_to, int stride_from, char *mask); +/** + * Byte to byte pixels, input and output 4-channel RGBA. + */ void IMB_buffer_byte_from_byte(unsigned char *rect_to, const unsigned char *rect_from, int profile_to, @@ -605,6 +678,8 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height); * rgba to abgr. size * 4 color bytes are reordered. * * \attention Defined in imageprocess.c + * + * Only this one is used liberally here, and in imbuf. */ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf); @@ -612,27 +687,50 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf); * * \attention defined in imageprocess.c */ + void bicubic_interpolation( - struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); + const struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); void nearest_interpolation( - struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); + const struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); void bilinear_interpolation( - struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); + const struct ImBuf *in, struct ImBuf *out, float u, float v, int xout, int yout); +typedef void (*InterpolationColorFunction)( + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); void bicubic_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + +/* Functions assumes out to be zeroed, only does RGBA. */ + +void nearest_interpolation_color_char( + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +void nearest_interpolation_color_fl( + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); void nearest_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); void nearest_interpolation_color_wrap( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); void bilinear_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +void bilinear_interpolation_color_char( + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +void bilinear_interpolation_color_fl( + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +/** + * Note about wrapping, the u/v still needs to be within the image bounds, + * just the interpolation is wrapped. + * This the same as bilinear_interpolation_color except it wraps + * rather than using empty and emptyI. + */ void bilinear_interpolation_color_wrap( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); 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]); +/** + * Sample pixel of image using NEAREST method. + */ void IMB_sampleImageAtLocation( struct ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]); @@ -686,7 +784,7 @@ struct ImBuf *IMB_double_y(struct ImBuf *ibuf1); void IMB_flipx(struct ImBuf *ibuf); void IMB_flipy(struct ImBuf *ibuf); -/* Premultiply alpha */ +/* Pre-multiply alpha. */ void IMB_premultiply_alpha(struct ImBuf *ibuf); void IMB_unpremultiply_alpha(struct ImBuf *ibuf); @@ -702,7 +800,27 @@ void IMB_freezbuffloatImBuf(struct ImBuf *ibuf); * * \attention Defined in rectop.c */ +/** + * 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(struct ImBuf *drect, const float col[4]); +/** + * Blend pixels of image area with solid color. + * + * For images with `uchar` buffer use color matching image color-space. + * For images with float buffer use color display color-space. + * If display color-space can not be referenced, use color in SRGB color-space. + * + * \param ibuf: an image to be filled with color. It must be 4 channel image. + * \param col: RGBA color. + * \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. + * \param display: color-space reference for display space. + */ void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, @@ -710,12 +828,23 @@ void IMB_rectfill_area(struct ImBuf *ibuf, int x2, int y2, struct ColorManagedDisplay *display); +/** + * Replace pixels of image area with solid color. + * \param ibuf: 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. + * \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_replace( const struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2); -void IMB_rectfill_alpha(struct ImBuf *ibuf, const float value); +void IMB_rectfill_alpha(struct ImBuf *ibuf, float value); -/* This should not be here, really, - * we needed it for operating on render data, IMB_rectfill_area calls it. */ +/** + * This should not be here, really, + * we needed it for operating on render data, #IMB_rectfill_area calls it. + */ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, @@ -727,23 +856,34 @@ void buf_rectfill_area(unsigned char *rect, int x2, int y2); -/* exported for image tools in blender, to quickly allocate 32 bits rect */ +/** + * 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, const char *name); bool imb_addrectImBuf(struct ImBuf *ibuf); +/** + * Any free `ibuf->rect` frees mipmaps to be sure, creation is in render on first request. + */ void imb_freerectImBuf(struct ImBuf *ibuf); bool imb_addrectfloatImBuf(struct ImBuf *ibuf); +/** + * Any free `ibuf->rect` frees mipmaps to be sure, creation is in render on first request. + */ void imb_freerectfloatImBuf(struct ImBuf *ibuf); void imb_freemipmapImBuf(struct ImBuf *ibuf); bool imb_addtilesImBuf(struct ImBuf *ibuf); void imb_freetilesImBuf(struct ImBuf *ibuf); +/** Free all pixel data (associated with image size). */ void imb_freerectImbuf_all(struct ImBuf *ibuf); -/* threaded processors */ +/** + * Threaded processors. + */ void IMB_processor_apply_threaded( int buffer_lines, int handle_size, @@ -756,13 +896,48 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, ScanlineThreadFunc do_thread, void *custom_data); -void IMB_transform(struct ImBuf *src, +/** + * \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. + */ +typedef 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, +} eIMBTransformMode; + +/** + * \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 struct ImBuf *src, struct ImBuf *dst, - float transform_matrix[3][3], - struct rctf *src_crop, - const eIMBInterpolationFilterMode filter); + eIMBTransformMode mode, + eIMBInterpolationFilterMode filter, + const float transform_matrix[4][4], + const struct rctf *src_crop); + +/* FFMPEG */ -/* ffmpeg */ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); @@ -775,8 +950,17 @@ struct GPUTexture *IMB_create_gpu_texture(const char *name, bool use_high_bitdepth, bool use_premult, bool limit_gl_texture_size); +/** + * 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(). + */ struct GPUTexture *IMB_touch_gpu_texture( const char *name, struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth); +/** + * Will update a #GPUTexture 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(struct GPUTexture *tex, struct ImBuf *ibuf, int x, @@ -788,36 +972,33 @@ void IMB_update_gpu_texture_sub(struct GPUTexture *tex, bool use_premult); /** - * * \attention defined in stereoimbuf.c */ -void IMB_stereo3d_write_dimensions(const char mode, - const bool is_squeezed, - const size_t width, - const size_t height, - size_t *r_width, - size_t *r_height); -void IMB_stereo3d_read_dimensions(const char mode, - const bool is_squeezed, - const size_t width, - const size_t height, - size_t *r_width, - size_t *r_height); +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); int *IMB_stereo3d_from_rect(struct ImageFormatData *im_format, - const size_t x, - const size_t y, - const size_t channels, + size_t x, + size_t y, + size_t channels, int *rect_left, int *rect_right); float *IMB_stereo3d_from_rectf(struct ImageFormatData *im_format, - const size_t x, - const size_t y, - const size_t channels, + size_t x, + size_t y, + size_t channels, float *rectf_left, float *rectf_right); +/** + * Left/right are always float. + */ struct ImBuf *IMB_stereo3d_ImBuf(struct ImageFormatData *im_format, struct ImBuf *ibuf_left, struct ImBuf *ibuf_right); +/** + * Reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right). + */ void IMB_ImBufFromStereo3d(struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo, struct ImBuf **r_ibuf_left, diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 9697064b445..58148743b7e 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -143,10 +143,9 @@ typedef struct ImbFormatOptions { char quality; } ImbFormatOptions; -/** - * \name Imbuf Component flags +/* -------------------------------------------------------------------- */ +/** \name Imbuf Component flags * \brief These flags determine the components of an ImBuf struct. - * * \{ */ typedef enum eImBufFlags { @@ -175,6 +174,11 @@ typedef enum eImBufFlags { } eImBufFlags; /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Buffer + * \{ */ + typedef struct ImBuf { struct ImBuf *next, *prev; /** < allow lists of #ImBufs, for caches or flip-books. */ @@ -301,11 +305,14 @@ enum { IB_PERSISTENT = (1 << 5), }; -/** - * \name Imbuf preset profile tags - * \brief Some predefined color space profiles that 8 bit imbufs can represent +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Imbuf Preset Profile Tags * + * \brief Some predefined color space profiles that 8 bit imbufs can represent. * \{ */ + #define IB_PROFILE_NONE 0 #define IB_PROFILE_LINEAR_RGB 1 #define IB_PROFILE_SRGB 2 @@ -322,7 +329,7 @@ enum { # endif /* DDS_MAKEFOURCC */ /* - * FOURCC codes for DX compressed-texture pixel formats + * FOURCC codes for DX compressed-texture pixel formats. */ # define FOURCC_DDS (DDS_MAKEFOURCC('D', 'D', 'S', ' ')) @@ -337,13 +344,13 @@ extern const char *imb_ext_image[]; extern const char *imb_ext_movie[]; extern const char *imb_ext_audio[]; -/* image formats that can only be loaded via filepath */ +/** Image formats that can only be loaded via filepath. */ extern const char *imb_ext_image_filepath_only[]; -/** - * \name Imbuf Color Management Flag - * \brief Used with #ImBuf.colormanage_flag +/* -------------------------------------------------------------------- */ +/** \name Imbuf Color Management Flag * + * \brief Used with #ImBuf.colormanage_flag * \{ */ enum { diff --git a/source/blender/imbuf/IMB_metadata.h b/source/blender/imbuf/IMB_metadata.h index 652ce913ee5..50982d08c3e 100644 --- a/source/blender/imbuf/IMB_metadata.h +++ b/source/blender/imbuf/IMB_metadata.h @@ -58,10 +58,7 @@ void IMB_metadata_free(struct IDProperty *metadata); * \param len: length of value buffer allocated by user. * \return 1 (true) if metadata is present and value for the key found, 0 (false) otherwise. */ -bool IMB_metadata_get_field(struct IDProperty *metadata, - const char *key, - char *value, - const size_t len); +bool IMB_metadata_get_field(struct IDProperty *metadata, const char *key, char *value, size_t len); /** * Set user data in the metadata. diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index 8e5f15a64a0..156a060c621 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -59,7 +59,7 @@ void IMB_moviecache_set_priority_callback(struct MovieCache *cache, void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); bool IMB_moviecache_put_if_possible(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); -struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey); +struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey, bool *r_is_cached_empty); void IMB_moviecache_remove(struct MovieCache *cache, void *userkey); bool IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); @@ -70,6 +70,9 @@ void IMB_moviecache_cleanup(struct MovieCache *cache, void *userdata), void *userdata); +/** + * Get segments of cached frames. Useful for debugging cache policies. + */ void IMB_moviecache_get_cache_segments( struct MovieCache *cache, int proxy, int render_flags, int *r_totseg, int **r_points); diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index e1a315a0bd2..6a739002fb3 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -48,47 +48,66 @@ typedef enum ThumbSource { THB_SOURCE_FONT, } ThumbSource; -/* don't generate thumbs for images bigger than this (100mb) */ +/** + * Don't generate thumbs for images bigger than this (100mb). + */ #define THUMB_SIZE_MAX (100 * 1024 * 1024) #define PREVIEW_RENDER_DEFAULT_HEIGHT 128 #define PREVIEW_RENDER_LARGE_HEIGHT 256 -/* Note this can also be used as versioning system, +/** + * Note this can also be used as versioning system, * to force refreshing all thumbnails if e.g. we change some thumb generating code or so. - * Only used by fonts so far. */ + * Only used by fonts so far. + */ #define THUMB_DEFAULT_HASH "00000000000000000000000000000000" -/* create thumbnail for file and returns new imbuf for thumbnail */ +/** + * Create thumbnail for file and returns new imbuf for thumbnail. + */ struct ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, struct ImBuf *img); -/* read thumbnail for file and returns new imbuf for thumbnail */ +/** + * Read thumbnail for file and returns new imbuf for thumbnail. + */ struct ImBuf *IMB_thumb_read(const char *path, ThumbSize size); -/* delete all thumbs for the file */ +/** + * Delete all thumbs for the file. + */ void IMB_thumb_delete(const char *path, ThumbSize size); -/* return the state of the thumb, needed to determine how to manage the thumb */ +/** + * Create the thumb if necessary and manage failed and old thumbs. + */ struct ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source); -/* create the necessary dirs to store the thumbnails */ +/** + * Create the necessary dirs to store the thumbnails. + */ void IMB_thumb_makedirs(void); -/* special function for loading a thumbnail embedded into a blend file */ +/** + * Special function for loading a thumbnail embedded into a blend file. + */ struct ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const char *blen_id); -/* special function for previewing fonts */ +/** + * Special function for previewing fonts. + */ struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y); bool IMB_thumb_load_font_get_hash(char *r_hash); void IMB_thumb_clear_translations(void); void IMB_thumb_ensure_translations(void); /* Threading */ + void IMB_thumb_locks_acquire(void); void IMB_thumb_locks_release(void); void IMB_thumb_path_lock(const char *path); diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index c8f6135f330..bf6aef3ecd3 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -22,7 +22,9 @@ #include "IMB_imbuf.h" -/* Generic File Type */ +/* -------------------------------------------------------------------- */ +/** \name Generic File Type + * \{ */ struct ImBuf; @@ -39,7 +41,7 @@ typedef struct ImFileType { * \note that this may only read in a small part of the files header, * see: #IMB_ispic_type for details. */ - bool (*is_a)(const unsigned char *buf, const size_t size); + bool (*is_a)(const unsigned char *buf, size_t size); /** Load an image from memory. */ struct ImBuf *(*load)(const unsigned char *mem, @@ -78,36 +80,62 @@ void imb_tile_cache_init(void); void imb_tile_cache_exit(void); void imb_loadtile(struct ImBuf *ibuf, int tx, int ty, unsigned int *rect); +/** + * External free. + */ void imb_tile_cache_tile_free(struct ImBuf *ibuf, int tx, int ty); +/** \} */ + /* Type Specific Functions */ -/* png */ -bool imb_is_a_png(const unsigned char *mem, const size_t size); +/* -------------------------------------------------------------------- */ +/** \name Format: PNG (#IMB_FTYPE_PNG) + * \{ */ + +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); -/* targa */ -bool imb_is_a_targa(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \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); -/* iris */ -bool imb_is_a_iris(const unsigned char *mem, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: IRIS (#IMB_FTYPE_IMAGIC) + * \{ */ + +bool imb_is_a_iris(const unsigned char *mem, size_t size); +/** + * Read in a B/W RGB or RGBA iris image file and return an image buffer. + */ struct ImBuf *imb_loadiris(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); bool imb_saveiris(struct ImBuf *ibuf, const char *filepath, int flags); -/* jp2 */ -bool imb_is_a_jp2(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: JP2 (#IMB_FTYPE_JP2) + * \{ */ + +bool imb_is_a_jp2(const unsigned char *buf, size_t size); struct ImBuf *imb_load_jp2(const unsigned char *mem, size_t size, int flags, @@ -117,53 +145,110 @@ struct ImBuf *imb_load_jp2_filepath(const char *filepath, char colorspace[IM_MAX_SPACE]); bool imb_save_jp2(struct ImBuf *ibuf, const char *filepath, int flags); -/* jpeg */ -bool imb_is_a_jpeg(const unsigned char *mem, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: JPEG (#IMB_FTYPE_JPG) + * \{ */ + +bool imb_is_a_jpeg(const unsigned char *mem, size_t size); bool imb_savejpeg(struct ImBuf *ibuf, const char *filepath, int flags); struct ImBuf *imb_load_jpeg(const unsigned char *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]); -/* bmp */ -bool imb_is_a_bmp(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: BMP (#IMB_FTYPE_BMP) + * \{ */ + +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]); +/* 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); -/* cineon */ -bool imb_is_a_cineon(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: CINEON (#IMB_FTYPE_CINEON) + * \{ */ + +bool imb_is_a_cineon(const unsigned char *buf, size_t size); bool imb_save_cineon(struct ImBuf *buf, const char *filepath, int flags); struct ImBuf *imb_load_cineon(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); -/* dpx */ -bool imb_is_a_dpx(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: DPX (#IMB_FTYPE_DPX) + * \{ */ + +bool imb_is_a_dpx(const unsigned char *buf, size_t size); bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags); struct ImBuf *imb_load_dpx(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]); -/* hdr */ -bool imb_is_a_hdr(const unsigned char *buf, const size_t size); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: HDR (#IMB_FTYPE_RADHDR) + * \{ */ + +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); -/* tiff */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Format: TIFF (#IMB_FTYPE_TIF) + * \{ */ + void imb_inittiff(void); -bool imb_is_a_tiff(const unsigned char *buf, const size_t size); +bool imb_is_a_tiff(const unsigned char *buf, size_t size); +/** + * Loads a TIFF file. + * \param mem: Memory containing the TIFF file. + * \param size: Size of the mem buffer. + * \param flags: If flags has IB_test set then the file is not actually loaded, + * but all other operations take place. + * + * \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]); void imb_loadtiletiff( struct ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect); +/** + * Saves a TIFF file. + * + * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA + * respectively) are accepted, and interpreted correctly. Note that the TIFF + * convention is to use pre-multiplied alpha, which can be achieved within + * Blender by setting "Premul" alpha handling. Other alpha conventions are + * not strictly correct, but are permitted anyhow. + * + * \param ibuf: Image buffer. + * \param filepath: Name of the TIFF file to create. + * \param flags: Currently largely ignored. + * + * \return 1 if the function is successful, 0 on failure. + */ bool imb_savetiff(struct ImBuf *ibuf, const char *filepath, int flags); + +/** \} */ diff --git a/source/blender/imbuf/intern/IMB_filter.h b/source/blender/imbuf/intern/IMB_filter.h index 556362d78c1..434750e6736 100644 --- a/source/blender/imbuf/intern/IMB_filter.h +++ b/source/blender/imbuf/intern/IMB_filter.h @@ -34,4 +34,7 @@ void IMB_premultiply_rect_float(float *rect_float, int channels, int w, int h); void IMB_unpremultiply_rect(unsigned int *rect, char planes, int w, int h); void IMB_unpremultiply_rect_float(float *rect_float, int channels, int w, int h); +/** + * Result in ibuf2, scaling should be done correctly. + */ void imb_onehalf_no_alloc(struct ImBuf *ibuf2, struct ImBuf *ibuf1); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 1369f8cc0f8..197d7e6b1d5 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -93,7 +93,6 @@ void imb_freemipmapImBuf(ImBuf *ibuf) ibuf->miptot = 0; } -/* any free rect frees mipmaps to be sure, creation is in render on first request */ void imb_freerectfloatImBuf(ImBuf *ibuf) { if (ibuf == NULL) { @@ -111,7 +110,6 @@ void imb_freerectfloatImBuf(ImBuf *ibuf) ibuf->mall &= ~IB_rectfloat; } -/* any free rect frees mipmaps to be sure, creation is in render on first request */ void imb_freerectImBuf(ImBuf *ibuf) { if (ibuf == NULL) { @@ -197,7 +195,6 @@ void IMB_freezbuffloatImBuf(ImBuf *ibuf) ibuf->mall &= ~IB_zbuffloat; } -/** Free all pixel data (associated with image size). */ void imb_freerectImbuf_all(ImBuf *ibuf) { imb_freerectImBuf(ibuf); @@ -403,9 +400,10 @@ bool imb_addrectfloatImBuf(ImBuf *ibuf) return false; } -/* question; why also add zbuf? */ bool imb_addrectImBuf(ImBuf *ibuf) { + /* Question; why also add ZBUF (when `planes > 32`)? */ + if (ibuf == NULL) { return false; } @@ -430,9 +428,6 @@ bool imb_addrectImBuf(ImBuf *ibuf) return false; } -/** - * \param take_ownership: When true, the buffers become owned by the resulting image. - */ struct ImBuf *IMB_allocFromBufferOwn( unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels) { @@ -580,7 +575,6 @@ bool IMB_initImBuf( return true; } -/* does no zbuffers? */ ImBuf *IMB_dupImBuf(const ImBuf *ibuf1) { ImBuf *ibuf2, tbuf; @@ -669,6 +663,11 @@ ImBuf *IMB_dupImBuf(const ImBuf *ibuf1) return ibuf2; } +size_t IMB_get_rect_len(const ImBuf *ibuf) +{ + return (size_t)ibuf->x * (size_t)ibuf->y; +} + size_t IMB_get_size_in_memory(ImBuf *ibuf) { int a; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 13f9356751e..17e82fc79c9 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -58,6 +58,8 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "DNA_scene_types.h" + #include "MEM_guardedalloc.h" #ifdef WITH_AVI @@ -502,11 +504,6 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position) #ifdef WITH_FFMPEG -BLI_INLINE bool need_aligned_ffmpeg_buffer(struct anim *anim) -{ - return (anim->x & 31) != 0; -} - static int startffmpeg(struct anim *anim) { int i, video_stream_index; @@ -705,23 +702,20 @@ static int startffmpeg(struct anim *anim) anim->pFrameComplete = false; anim->pFrameDeinterlaced = av_frame_alloc(); anim->pFrameRGB = av_frame_alloc(); + anim->pFrameRGB->format = AV_PIX_FMT_RGBA; + anim->pFrameRGB->width = anim->x; + anim->pFrameRGB->height = anim->y; - if (need_aligned_ffmpeg_buffer(anim)) { - anim->pFrameRGB->format = AV_PIX_FMT_RGBA; - anim->pFrameRGB->width = anim->x; - anim->pFrameRGB->height = anim->y; - - if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) { - fprintf(stderr, "Could not allocate frame data.\n"); - avcodec_free_context(&anim->pCodecCtx); - avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->cur_packet); - av_frame_free(&anim->pFrameRGB); - av_frame_free(&anim->pFrameDeinterlaced); - av_frame_free(&anim->pFrame); - anim->pCodecCtx = NULL; - return -1; - } + if (av_frame_get_buffer(anim->pFrameRGB, 0) < 0) { + fprintf(stderr, "Could not allocate frame data.\n"); + avcodec_free_context(&anim->pCodecCtx); + avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->cur_packet); + av_frame_free(&anim->pFrameRGB); + av_frame_free(&anim->pFrameDeinterlaced); + av_frame_free(&anim->pFrame); + anim->pCodecCtx = NULL; + return -1; } if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) { @@ -849,104 +843,61 @@ static void ffmpeg_postprocess(struct anim *anim) } } - if (!need_aligned_ffmpeg_buffer(anim)) { - av_image_fill_arrays(anim->pFrameRGB->data, - anim->pFrameRGB->linesize, - (unsigned char *)ibuf->rect, - AV_PIX_FMT_RGBA, - anim->x, - anim->y, - 1); - } - -# if defined(__x86_64__) || defined(_M_X64) - /* Scale and flip image over Y axis in one go, using negative strides. - * This doesn't work with ARM/PowerPC though and may be misusing the API. - * Limit it x86_64 where it appears to work. - * http://trac.ffmpeg.org/ticket/9060 */ - int *dstStride = anim->pFrameRGB->linesize; - uint8_t **dst = anim->pFrameRGB->data; - const int dstStride2[4] = {-dstStride[0], 0, 0, 0}; - uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0}; - - sws_scale(anim->img_convert_ctx, - (const uint8_t *const *)input->data, - input->linesize, - 0, - anim->y, - dst2, - dstStride2); -# else - /* Scale with swscale then flip image over Y axis. */ - int *dstStride = anim->pFrameRGB->linesize; - uint8_t **dst = anim->pFrameRGB->data; - const int dstStride2[4] = {dstStride[0], 0, 0, 0}; - uint8_t *dst2[4] = {dst[0], 0, 0, 0}; - int x, y, h, w; - unsigned char *bottom; - unsigned char *top; - sws_scale(anim->img_convert_ctx, (const uint8_t *const *)input->data, input->linesize, 0, anim->y, - dst2, - dstStride2); - - bottom = (unsigned char *)ibuf->rect; - top = bottom + ibuf->x * (ibuf->y - 1) * 4; - - h = (ibuf->y + 1) / 2; - w = ibuf->x; - - for (y = 0; y < h; y++) { - unsigned char tmp[4]; - unsigned int *tmp_l = (unsigned int *)tmp; - - for (x = 0; x < w; x++) { - tmp[0] = bottom[0]; - tmp[1] = bottom[1]; - tmp[2] = bottom[2]; - tmp[3] = bottom[3]; - - bottom[0] = top[0]; - bottom[1] = top[1]; - bottom[2] = top[2]; - bottom[3] = top[3]; - - *(unsigned int *)top = *tmp_l; - - bottom += 4; - top += 4; - } - top -= 8 * w; + anim->pFrameRGB->data, + anim->pFrameRGB->linesize); + + /* Copy the valid bytes from the aligned buffer vertically flipped into ImBuf */ + int aligned_stride = anim->pFrameRGB->linesize[0]; + const uint8_t *const src[4] = { + anim->pFrameRGB->data[0] + (anim->y - 1) * aligned_stride, 0, 0, 0}; + /* NOTE: Negative linesize is used to copy and flip image at once with function + * `av_image_copy_to_buffer`. This could cause issues in future and image may need to be flipped + * explicitly. */ + const int src_linesize[4] = {-anim->pFrameRGB->linesize[0], 0, 0, 0}; + int dst_size = av_image_get_buffer_size( + anim->pFrameRGB->format, anim->pFrameRGB->width, anim->pFrameRGB->height, 1); + av_image_copy_to_buffer( + (uint8_t *)ibuf->rect, dst_size, src, src_linesize, AV_PIX_FMT_RGBA, anim->x, anim->y, 1); + if (filter_y) { + IMB_filtery(ibuf); } -# endif +} - if (need_aligned_ffmpeg_buffer(anim)) { - uint8_t *buf_src = anim->pFrameRGB->data[0]; - uint8_t *buf_dst = (uint8_t *)ibuf->rect; - for (int y = 0; y < anim->y; y++) { - memcpy(buf_dst, buf_src, anim->x * 4); - buf_dst += anim->x * 4; - buf_src += anim->pFrameRGB->linesize[0]; - } - } +static void ffmpeg_decode_store_frame_pts(struct anim *anim) +{ + anim->cur_pts = av_get_pts_from_frame(anim->pFrame); - if (filter_y) { - IMB_filtery(ibuf); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; } + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, + (int64_t)anim->cur_pts); } /* decode one video frame also considering the packet read into cur_packet */ - static int ffmpeg_decode_video_frame(struct anim *anim) { - int rval = 0; - av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); + /* Sometimes, decoder returns more than one frame per sent packet. Check if frames are available. + * This frames must be read, otherwise decoding will fail. See T91405. */ + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; + if (anim->pFrameComplete) { + av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE FROM CODEC BUFFER\n"); + ffmpeg_decode_store_frame_pts(anim); + return 1; + } + + int rval = 0; if (anim->cur_packet->stream_index == anim->videoStream) { av_packet_unref(anim->cur_packet); anim->cur_packet->stream_index = -1; @@ -963,22 +914,11 @@ static int ffmpeg_decode_video_frame(struct anim *anim) (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts, (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); if (anim->cur_packet->stream_index == anim->videoStream) { - anim->pFrameComplete = 0; - avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->cur_pts = av_get_pts_from_frame(anim->pFrame); - - if (anim->pFrame->key_frame) { - anim->cur_key_frame_pts = anim->cur_pts; - } - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", - (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->cur_pts); + ffmpeg_decode_store_frame_pts(anim); break; } } @@ -988,22 +928,11 @@ static int ffmpeg_decode_video_frame(struct anim *anim) if (rval == AVERROR_EOF) { /* Flush any remaining frames out of the decoder. */ - anim->pFrameComplete = 0; - avcodec_send_packet(anim->pCodecCtx, NULL); anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->cur_pts = av_get_pts_from_frame(anim->pFrame); - - if (anim->pFrame->key_frame) { - anim->cur_key_frame_pts = anim->cur_pts; - } - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", - (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->cur_pts); + ffmpeg_decode_store_frame_pts(anim); rval = 0; } } @@ -1443,7 +1372,15 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ * * The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker * and is fixed in the newer versions than 4.3.1. */ - anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0); + + const AVPixFmtDescriptor *pix_fmt_descriptor = av_pix_fmt_desc_get(anim->pCodecCtx->pix_fmt); + + int planes = R_IMF_PLANES_RGBA; + if ((pix_fmt_descriptor->flags & AV_PIX_FMT_FLAG_ALPHA) == 0) { + planes = R_IMF_PLANES_RGB; + } + + anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, planes, 0); anim->cur_frame_final->rect = MEM_mallocN_aligned( (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); anim->cur_frame_final->mall |= IB_rect; @@ -1471,20 +1408,6 @@ static void free_anim_ffmpeg(struct anim *anim) av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrame); - - if (!need_aligned_ffmpeg_buffer(anim)) { - /* If there's no need for own aligned buffer it means that FFmpeg's - * frame shares the same buffer as temporary ImBuf. In this case we - * should not free the buffer when freeing the FFmpeg buffer. - */ - av_image_fill_arrays(anim->pFrameRGB->data, - anim->pFrameRGB->linesize, - NULL, - AV_PIX_FMT_RGBA, - anim->x, - anim->y, - 1); - } av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); @@ -1496,16 +1419,16 @@ static void free_anim_ffmpeg(struct anim *anim) #endif -/* Try next picture to read */ -/* No picture, try to open next animation */ -/* Succeed, remove first image from animation */ - -static ImBuf *anim_getnew(struct anim *anim) +/** + * Try to initialize the #anim struct. + * Returns true on success. + */ +static bool anim_getnew(struct anim *anim) { - struct ImBuf *ibuf = NULL; - + BLI_assert(anim->curtype == ANIM_NONE); if (anim == NULL) { - return NULL; + /* Nothing to initialize. */ + return false; } free_anim_movie(anim); @@ -1518,44 +1441,43 @@ static ImBuf *anim_getnew(struct anim *anim) free_anim_ffmpeg(anim); #endif - if (anim->curtype != 0) { - return NULL; - } anim->curtype = imb_get_anim_type(anim->name); switch (anim->curtype) { - case ANIM_SEQUENCE: - ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); + case ANIM_SEQUENCE: { + ImBuf *ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); if (ibuf) { BLI_strncpy(anim->first, anim->name, sizeof(anim->first)); anim->duration_in_frames = 1; + IMB_freeImBuf(ibuf); + } + else { + return false; } break; + } case ANIM_MOVIE: if (startmovie(anim)) { - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); /* fake */ break; #ifdef WITH_AVI case ANIM_AVI: if (startavi(anim)) { printf("couldn't start avi\n"); - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif #ifdef WITH_FFMPEG case ANIM_FFMPEG: if (startffmpeg(anim)) { - return 0; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif } - return ibuf; + return true; } struct ImBuf *IMB_anim_previewframe(struct anim *anim) @@ -1589,14 +1511,10 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, filter_y = (anim->ib_flags & IB_animdeinterlace); if (preview_size == IMB_PROXY_NONE) { - if (anim->curtype == 0) { - ibuf = anim_getnew(anim); - if (ibuf == NULL) { + if (anim->curtype == ANIM_NONE) { + if (!anim_getnew(anim)) { return NULL; } - - IMB_freeImBuf(ibuf); /* ???? */ - ibuf = NULL; } if (position < 0) { diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 70bb70ec4fa..b44b12999bf 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -151,8 +151,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[ } /* Validate and cross-check offsets and sizes. */ - if (x < 1 || - !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) { + if (x < 1 || !(ELEM(depth, 1, 4, 8, 16, 24, 32))) { return NULL; } @@ -307,7 +306,6 @@ static int putShortLSB(ushort us, FILE *ofile) return putc((us >> 8) & 0xFF, ofile); } -/* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */ bool imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags)) { BMPINFOHEADER infoheader; diff --git a/source/blender/imbuf/intern/cache.c b/source/blender/imbuf/intern/cache.c index 02d1fe3710a..ce11e1c3f80 100644 --- a/source/blender/imbuf/intern/cache.c +++ b/source/blender/imbuf/intern/cache.c @@ -153,7 +153,6 @@ static void imb_global_cache_tile_unload(ImGlobalTile *gtile) GLOBAL_CACHE.totmem -= sizeof(unsigned int) * ibuf->tilex * ibuf->tiley; } -/* external free */ void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty) { ImGlobalTile *gtile, lookuptile; @@ -248,7 +247,6 @@ void imb_tile_cache_exit(void) } } -/* presumed to be called when no threads are running */ void IMB_tile_cache_params(int totthread, int maxmem) { int a; diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c index 1223033f535..758b21f8cda 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.c +++ b/source/blender/imbuf/intern/cineon/cineonlib.c @@ -239,13 +239,13 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t } if (cineon->depth == 1) { - /* Grayscale image */ + /* Gray-scale image. */ cineon->element[0].descriptor = descriptor_Luminance; cineon->element[0].transfer = transfer_Linear; cineon->element[0].depth = 1; } else if (cineon->depth == 3) { - /* RGB image */ + /* RGB image. */ if (cineon->numElements == 1) { cineon->element[0].descriptor = descriptor_RGB; cineon->element[0].transfer = transfer_PrintingDensity; diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 119ef3ff971..6e9e5dd3ddb 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -342,7 +342,7 @@ static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, *cache_handle = NULL; - cache_ibuf = IMB_moviecache_get(moviecache, key); + cache_ibuf = IMB_moviecache_get(moviecache, key, NULL); *cache_handle = cache_ibuf; @@ -1968,7 +1968,6 @@ static void colormanagement_transform_ex(unsigned char *byte_buffer, IMB_colormanagement_processor_free(cm_processor); } -/* convert the whole buffer from specified by name color space to another */ void IMB_colormanagement_transform(float *buffer, int width, int height, @@ -1981,9 +1980,6 @@ void IMB_colormanagement_transform(float *buffer, NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, false); } -/* convert the whole buffer from specified by name color space to another - * will do threaded conversion - */ void IMB_colormanagement_transform_threaded(float *buffer, int width, int height, @@ -1996,7 +1992,6 @@ void IMB_colormanagement_transform_threaded(float *buffer, NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true); } -/* Similar to functions above, but operates on byte buffer. */ void IMB_colormanagement_transform_byte(unsigned char *buffer, int width, int height, @@ -2018,7 +2013,6 @@ void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer, buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, true); } -/* Similar to above, but gets float buffer from display one. */ void IMB_colormanagement_transform_from_byte(float *float_buffer, unsigned char *byte_buffer, int width, @@ -2098,9 +2092,6 @@ void IMB_colormanagement_transform_v4(float pixel[4], IMB_colormanagement_processor_free(cm_processor); } -/* convert pixel from specified by descriptor color space to scene linear - * used by performance-critical areas such as renderer and baker - */ void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpace *colorspace) { OCIO_ConstCPUProcessorRcPtr *processor; @@ -2118,7 +2109,6 @@ void IMB_colormanagement_colorspace_to_scene_linear_v3(float pixel[3], ColorSpac } } -/* same as above, but converts colors in opposite direction */ void IMB_colormanagement_scene_linear_to_colorspace_v3(float pixel[3], ColorSpace *colorspace) { OCIO_ConstCPUProcessorRcPtr *processor; @@ -2315,14 +2305,6 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, } } -/* Conversion between color picking role. Typically we would expect such a - * requirements: - * - It is approximately perceptually linear, so that the HSV numbers and - * the HSV cube/circle have an intuitive distribution. - * - It has the same gamut as the scene linear color space. - * - Color picking values 0..1 map to scene linear values in the 0..1 range, - * so that picked albedo values are energy conserving. - */ void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]) { if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) { @@ -2377,8 +2359,6 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]) } } -/* Conversion between sRGB, for rare cases like hex color or copy/pasting - * between UI theme and scene linear colors. */ void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3]) { mul_m3_v3(imbuf_rgb_to_xyz, pixel); @@ -2393,10 +2373,6 @@ void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3]) mul_m3_v3(imbuf_xyz_to_rgb, pixel); } -/* convert pixel from scene linear to display space using default view - * used by performance-critical areas such as color-related widgets where we want to reduce - * amount of per-widget allocations - */ void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display) { OCIO_ConstCPUProcessorRcPtr *processor = display_from_scene_linear_processor(display); @@ -2406,7 +2382,6 @@ void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManaged } } -/* same as above, but converts color in opposite direction */ void IMB_colormanagement_display_to_scene_linear_v3(float pixel[3], ColorManagedDisplay *display) { OCIO_ConstCPUProcessorRcPtr *processor = display_to_scene_linear_processor(display); @@ -2468,17 +2443,6 @@ void IMB_colormanagement_imbuf_make_display_space( colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false); } -/* prepare image buffer to be saved on disk, applying color management if needed - * color management would be applied if image is saving as render result and if - * file format is not expecting float buffer to be in linear space (currently - * JPEG2000 and TIFF are such formats -- they're storing image as float but - * file itself stores applied color space). - * - * Both byte and float buffers would contain applied color space, and result's - * float_colorspace would be set to display color space. This should be checked - * in image format write callback and if float_colorspace is not NULL, no color - * space transformation should be applied on this buffer. - */ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, @@ -2640,7 +2604,6 @@ void IMB_colormanagement_buffer_make_display_space( /** \name Public Display Buffers Interfaces * \{ */ -/* acquire display buffer for given image buffer using specified view and display settings */ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, @@ -2738,7 +2701,6 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, return display_buffer; } -/* same as IMB_display_buffer_acquire but gets view and display settings from context */ unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle) { ColorManagedViewSettings *view_settings; @@ -2899,7 +2861,6 @@ const char *IMB_colormanagement_display_get_default_name(void) return display->name; } -/* used by performance-critical pixel processing areas, such as color widgets */ ColorManagedDisplay *IMB_colormanagement_display_get_named(const char *name) { return colormanage_display_get_named(name); @@ -4027,19 +3988,6 @@ bool IMB_colormanagement_support_glsl_draw(const ColorManagedViewSettings *UNUSE return OCIO_supportGPUShader(); } -/** - * Configures GLSL shader for conversion from specified to - * display color space - * - * Will create appropriate OCIO processor and setup GLSL shader, - * so further 2D texture usage will use this conversion. - * - * When there's no need to apply transform on 2D textures, use - * IMB_colormanagement_finish_glsl_draw(). - * - * This is low-level function, use ED_draw_imbuf_ctx if you - * only need to display given image buffer - */ bool IMB_colormanagement_setup_glsl_draw_from_space( const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, @@ -4097,7 +4045,6 @@ bool IMB_colormanagement_setup_glsl_draw_from_space( return global_gpu_state.gpu_shader_bound; } -/* Configures GLSL shader for conversion from scene linear to display space */ bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float dither, @@ -4107,10 +4054,6 @@ bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_se view_settings, display_settings, NULL, dither, predivide, false); } -/** - * Same as setup_glsl_draw_from_space, - * but color management settings are guessing from a given context. - */ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, struct ColorSpace *from_colorspace, float dither, @@ -4125,13 +4068,11 @@ bool IMB_colormanagement_setup_glsl_draw_from_space_ctx(const bContext *C, view_settings, display_settings, from_colorspace, dither, predivide, false); } -/* Same as setup_glsl_draw, but color management settings are guessing from a given context */ bool IMB_colormanagement_setup_glsl_draw_ctx(const bContext *C, float dither, bool predivide) { return IMB_colormanagement_setup_glsl_draw_from_space_ctx(C, NULL, dither, predivide); } -/* Finish GLSL-based display space conversion */ void IMB_colormanagement_finish_glsl_draw(void) { if (global_gpu_state.gpu_shader_bound) { diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index c304ad8d8e5..6e57a8cc1b2 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -27,23 +27,11 @@ #include "BLI_math_vector.h" #include "IMB_colormanagement_intern.h" -/* Convert a float RGB triplet to the correct luminance weighted average. - * - * Grayscale, or Luma is a distillation of RGB data values down to a weighted average - * based on the luminance positions of the red, green, and blue primaries. - * Given that the internal reference space may be arbitrarily set, any - * effort to glean the luminance coefficients must be aware of the reference - * space primaries. - * - * See http://wiki.blender.org/index.php/User:Nazg-gul/ColorManagement#Luminance - */ - float IMB_colormanagement_get_luminance(const float rgb[3]) { return dot_v3v3(imbuf_luma_coefficients, rgb); } -/* Byte equivalent of IMB_colormanagement_get_luminance(). */ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3]) { float rgbf[3]; diff --git a/source/blender/imbuf/intern/dds/BlockDXT.cpp b/source/blender/imbuf/intern/dds/BlockDXT.cpp index 4e4fca864a0..e471b6834ae 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.cpp +++ b/source/blender/imbuf/intern/dds/BlockDXT.cpp @@ -158,7 +158,6 @@ uint BlockDXT1::evaluatePaletteNV5x(Color32 color_array[4]) const return 3; } -/* Evaluate palette assuming 3 color block. */ void BlockDXT1::evaluatePalette3(Color32 color_array[4]) const { color_array[0].b = (col0.b << 3) | (col0.b >> 2); @@ -184,7 +183,6 @@ void BlockDXT1::evaluatePalette3(Color32 color_array[4]) const color_array[3].a = 0x00; } -/* Evaluate palette assuming 4 color block. */ void BlockDXT1::evaluatePalette4(Color32 color_array[4]) const { color_array[0].b = (col0.b << 3) | (col0.b >> 2); @@ -247,14 +245,12 @@ void BlockDXT1::setIndices(const int *idx) } } -/** Flip DXT1 block vertically. */ inline void BlockDXT1::flip4() { swap(row[0], row[3]); swap(row[1], row[2]); } -/** Flip half DXT1 block vertically. */ inline void BlockDXT1::flip2() { swap(row[0], row[1]); @@ -299,27 +295,23 @@ void AlphaBlockDXT3::decodeBlock(ColorBlock *block) const block->color(0xF).a = (alphaF << 4) | alphaF; } -/** Flip DXT3 alpha block vertically. */ void AlphaBlockDXT3::flip4() { swap(row[0], row[3]); swap(row[1], row[2]); } -/** Flip half DXT3 alpha block vertically. */ void AlphaBlockDXT3::flip2() { swap(row[0], row[1]); } -/** Flip DXT3 block vertically. */ void BlockDXT3::flip4() { alpha.flip4(); color.flip4(); } -/** Flip half DXT3 block vertically. */ void BlockDXT3::flip2() { alpha.flip2(); @@ -458,21 +450,18 @@ void BlockDXT5::decodeBlockNV5x(ColorBlock *block) const alpha.decodeBlock(block); } -/** Flip DXT5 block vertically. */ void BlockDXT5::flip4() { alpha.flip4(); color.flip4(); } -/** Flip half DXT5 block vertically. */ void BlockDXT5::flip2() { alpha.flip2(); color.flip2(); } -/** Decode ATI1 block. */ void BlockATI1::decodeBlock(ColorBlock *block) const { uint8 alpha_array[8]; @@ -488,19 +477,16 @@ void BlockATI1::decodeBlock(ColorBlock *block) const } } -/** Flip ATI1 block vertically. */ void BlockATI1::flip4() { alpha.flip4(); } -/** Flip half ATI1 block vertically. */ void BlockATI1::flip2() { alpha.flip2(); } -/** Decode ATI2 block. */ void BlockATI2::decodeBlock(ColorBlock *block) const { uint8 alpha_array[8]; @@ -525,14 +511,12 @@ void BlockATI2::decodeBlock(ColorBlock *block) const } } -/** Flip ATI2 block vertically. */ void BlockATI2::flip4() { x.flip4(); y.flip4(); } -/** Flip half ATI2 block vertically. */ void BlockATI2::flip2() { x.flip2(); @@ -586,14 +570,12 @@ void BlockCTX1::setIndices(const int *idx) } } -/** Flip CTX1 block vertically. */ inline void BlockCTX1::flip4() { swap(row[0], row[3]); swap(row[1], row[2]); } -/** Flip half CTX1 block vertically. */ inline void BlockCTX1::flip2() { swap(row[0], row[1]); diff --git a/source/blender/imbuf/intern/dds/BlockDXT.h b/source/blender/imbuf/intern/dds/BlockDXT.h index 1fefa7c739d..eb2d5f8726c 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.h +++ b/source/blender/imbuf/intern/dds/BlockDXT.h @@ -69,7 +69,9 @@ struct BlockDXT1 { uint evaluatePalette(Color32 color_array[4]) const; uint evaluatePaletteNV5x(Color32 color_array[4]) const; + /** Evaluate palette assuming 3 color block. */ void evaluatePalette3(Color32 color_array[4]) const; + /** Evaluate palette assuming 4 color block. */ void evaluatePalette4(Color32 color_array[4]) const; void decodeBlock(ColorBlock *block) const; @@ -77,7 +79,9 @@ struct BlockDXT1 { void setIndices(const int *idx); + /** Flip DXT1 block vertically. */ void flip4(); + /** Flip half DXT1 block vertically. */ void flip2(); }; @@ -113,7 +117,9 @@ struct AlphaBlockDXT3 { void decodeBlock(ColorBlock *block) const; + /** Flip DXT3 alpha block vertically. */ void flip4(); + /** Flip half DXT3 alpha block vertically. */ void flip2(); }; @@ -125,7 +131,9 @@ struct BlockDXT3 { void decodeBlock(ColorBlock *block) const; void decodeBlockNV5x(ColorBlock *block) const; + /** Flip DXT3 block vertically. */ void flip4(); + /** Flip half DXT3 block vertically. */ void flip2(); }; @@ -253,7 +261,9 @@ struct BlockDXT5 { void decodeBlock(ColorBlock *block) const; void decodeBlockNV5x(ColorBlock *block) const; + /** Flip DXT5 block vertically. */ void flip4(); + /** Flip half DXT5 block vertically. */ void flip2(); }; @@ -261,9 +271,12 @@ struct BlockDXT5 { struct BlockATI1 { AlphaBlockDXT5 alpha; + /** Decode ATI1 block. */ void decodeBlock(ColorBlock *block) const; + /** Flip ATI1 block vertically. */ void flip4(); + /** Flip half ATI1 block vertically. */ void flip2(); }; @@ -272,9 +285,12 @@ struct BlockATI2 { AlphaBlockDXT5 x; AlphaBlockDXT5 y; + /** Decode ATI2 block. */ void decodeBlock(ColorBlock *block) const; + /** Flip ATI2 block vertically. */ void flip4(); + /** Flip half ATI2 block vertically. */ void flip2(); }; @@ -292,7 +308,9 @@ struct BlockCTX1 { void decodeBlock(ColorBlock *block) const; + /** Flip CTX1 block vertically. */ void flip4(); + /** Flip half CTX1 block vertically. */ void flip2(); }; diff --git a/source/blender/imbuf/intern/dds/ColorBlock.cpp b/source/blender/imbuf/intern/dds/ColorBlock.cpp index 6974e0bf99d..0ab98c01f6f 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.cpp +++ b/source/blender/imbuf/intern/dds/ColorBlock.cpp @@ -46,7 +46,6 @@ inline static uint colorDistance(Color32 c0, Color32 c1) } #endif -/** Init the color block from an array of colors. */ ColorBlock::ColorBlock(const uint *linearImage) { for (uint i = 0; i < 16; i++) { @@ -54,7 +53,6 @@ ColorBlock::ColorBlock(const uint *linearImage) } } -/** Init the color block with the contents of the given block. */ ColorBlock::ColorBlock(const ColorBlock &block) { for (uint i = 0; i < 16; i++) { @@ -62,7 +60,6 @@ ColorBlock::ColorBlock(const ColorBlock &block) } } -/** Initialize this color block. */ ColorBlock::ColorBlock(const Image *img, uint x, uint y) { init(img, x, y); @@ -153,7 +150,6 @@ void ColorBlock::swizzle(uint x, uint y, uint z, uint w) } } -/** Returns true if the block has a single color. */ bool ColorBlock::isSingleColor(Color32 mask /*= Color32(0xFF, 0xFF, 0xFF, 0x00) */) const { uint u = m_color[0].u & mask.u; @@ -234,7 +230,6 @@ Color32 ColorBlock::averageColor() const } #endif -/** Return true if the block is not fully opaque. */ bool ColorBlock::hasAlpha() const { for (const auto &i : m_color) { diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h index 934837bb129..1dee5c76c9e 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.h +++ b/source/blender/imbuf/intern/dds/ColorBlock.h @@ -35,8 +35,11 @@ /** Uncompressed 4x4 color block. */ struct ColorBlock { ColorBlock() = default; + /** Init the color block from an array of colors. */ ColorBlock(const uint *linearImage); + /** Init the color block with the contents of the given block. */ ColorBlock(const ColorBlock &block); + /** Initialize this color block. */ ColorBlock(const Image *img, uint x, uint y); void init(const Image *img, uint x, uint y); @@ -45,7 +48,9 @@ struct ColorBlock { void swizzle(uint x, uint y, uint z, uint w); /* 0=r, 1=g, 2=b, 3=a, 4=0xFF, 5=0 */ + /** Returns true if the block has a single color. */ bool isSingleColor(Color32 mask = Color32(0xFF, 0xFF, 0xFF, 0x00)) const; + /** Return true if the block is not fully opaque. */ bool hasAlpha() const; /* Accessors */ diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp index b665996b18f..efa438c2af5 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp @@ -1107,8 +1107,6 @@ void DirectDrawSurface::mipmap(Image *img, uint face, uint mipmap) } } -/* It was easier to copy this function from upstream than to resync. - * This should be removed if a resync ever occurs. */ void *DirectDrawSurface::readData(uint &rsize) { uint header_size = 128; // sizeof(DDSHeader); diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.h b/source/blender/imbuf/intern/dds/DirectDrawSurface.h index 381fa51f75c..343a7367f91 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.h +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.h @@ -157,6 +157,10 @@ class DirectDrawSurface { void setUserVersion(int version); void mipmap(Image *img, uint f, uint m); + /** + * It was easier to copy this function from upstream than to resync. + * This should be removed if a resync ever occurs. + */ void *readData(uint &size); // void mipmap(FloatImage *img, uint f, uint m); @@ -174,7 +178,8 @@ class DirectDrawSurface { void readBlock(ColorBlock *rgba); private: - Stream stream; /* Memory where DDS file resides. */ + /** Memory where DDS file resides. */ + Stream stream; DDSHeader header; }; diff --git a/source/blender/imbuf/intern/dds/FlipDXT.cpp b/source/blender/imbuf/intern/dds/FlipDXT.cpp index 2acf072556a..6686d56e9d1 100644 --- a/source/blender/imbuf/intern/dds/FlipDXT.cpp +++ b/source/blender/imbuf/intern/dds/FlipDXT.cpp @@ -168,10 +168,16 @@ static void FlipDXT5BlockHalf(uint8_t *block) FlipDXT1BlockHalf(block + 8); } -/* Flips a DXTC image, by flipping and swapping DXTC blocks as appropriate. */ -int FlipDXTCImage( - unsigned int width, unsigned int height, unsigned int levels, int fourcc, uint8_t *data) +int FlipDXTCImage(unsigned int width, + unsigned int height, + unsigned int levels, + int fourcc, + uint8_t *data, + int data_size, + unsigned int *r_num_valid_levels) { + *r_num_valid_levels = 0; + /* Must have valid dimensions. */ if (width == 0 || height == 0) { return 0; @@ -205,14 +211,25 @@ int FlipDXTCImage( return 0; } + *r_num_valid_levels = levels; + unsigned int mip_width = width; unsigned int mip_height = height; + const uint8_t *data_end = data + data_size; + for (unsigned int i = 0; i < levels; i++) { unsigned int blocks_per_row = (mip_width + 3) / 4; unsigned int blocks_per_col = (mip_height + 3) / 4; unsigned int 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. */ + *r_num_valid_levels = i; + break; + } + if (mip_height == 1) { /* no flip to do, and we're done. */ break; diff --git a/source/blender/imbuf/intern/dds/FlipDXT.h b/source/blender/imbuf/intern/dds/FlipDXT.h index d35157251bd..f9d3ce6112e 100644 --- a/source/blender/imbuf/intern/dds/FlipDXT.h +++ b/source/blender/imbuf/intern/dds/FlipDXT.h @@ -18,6 +18,15 @@ #include "BLI_sys_types.h" -/* flip compressed DXT image vertically to fit OpenGL convention */ -int FlipDXTCImage( - unsigned int width, unsigned int height, unsigned int levels, int fourcc, uint8_t *data); +/** + * Flips a DXTC image, by flipping and swapping DXTC blocks as appropriate. + * + * Use to flip vertically to fit OpenGL convention. + */ +int FlipDXTCImage(unsigned int width, + unsigned int height, + unsigned int levels, + int fourcc, + uint8_t *data, + int data_size, + unsigned int *r_num_valid_levels); diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index 3dab3c35675..31bf2076ed1 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -26,6 +26,21 @@ static const char *msg_error_seek = "DDS: trying to seek beyond end of stream (corrupt file?)"; static const char *msg_error_read = "DDS: trying to read beyond end of stream (corrupt file?)"; +inline bool is_read_within_bounds(const Stream &mem, unsigned int cnt) +{ + if (mem.pos >= mem.size) { + /* No more data remained in the memory buffer. */ + return false; + } + + if (cnt > mem.size - mem.pos) { + /* Reading past the memory bounds. */ + return false; + } + + return true; +} + unsigned int Stream::seek(unsigned int p) { if (p > size) { @@ -40,7 +55,7 @@ unsigned int Stream::seek(unsigned int p) unsigned int mem_read(Stream &mem, unsigned long long &i) { - if (mem.pos + 8 > mem.size) { + if (!is_read_within_bounds(mem, 8)) { mem.set_failed(msg_error_seek); return 0; } @@ -51,7 +66,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i) unsigned int mem_read(Stream &mem, unsigned int &i) { - if (mem.pos + 4 > mem.size) { + if (!is_read_within_bounds(mem, 4)) { mem.set_failed(msg_error_read); return 0; } @@ -62,7 +77,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i) unsigned int mem_read(Stream &mem, unsigned short &i) { - if (mem.pos + 2 > mem.size) { + if (!is_read_within_bounds(mem, 2)) { mem.set_failed(msg_error_read); return 0; } @@ -73,7 +88,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i) unsigned int mem_read(Stream &mem, unsigned char &i) { - if (mem.pos + 1 > mem.size) { + if (!is_read_within_bounds(mem, 1)) { mem.set_failed(msg_error_read); return 0; } @@ -84,7 +99,7 @@ unsigned int mem_read(Stream &mem, unsigned char &i) unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int cnt) { - if (mem.pos + cnt > mem.size) { + if (!is_read_within_bounds(mem, cnt)) { mem.set_failed(msg_error_read); return 0; } diff --git a/source/blender/imbuf/intern/dds/dds_api.cpp b/source/blender/imbuf/intern/dds/dds_api.cpp index 1729a9a64f8..f576209ff62 100644 --- a/source/blender/imbuf/intern/dds/dds_api.cpp +++ b/source/blender/imbuf/intern/dds/dds_api.cpp @@ -186,8 +186,13 @@ struct ImBuf *imb_load_dds(const unsigned char *mem, /* flip compressed texture */ if (ibuf->dds_data.data) { - FlipDXTCImage( - dds.width(), dds.height(), dds.mipmapCount(), dds.fourCC(), ibuf->dds_data.data); + FlipDXTCImage(dds.width(), + dds.height(), + ibuf->dds_data.nummipmaps, + dds.fourCC(), + ibuf->dds_data.data, + ibuf->dds_data.size, + &ibuf->dds_data.nummipmaps); } } else { diff --git a/source/blender/imbuf/intern/dds/dds_api.h b/source/blender/imbuf/intern/dds/dds_api.h index 931c4f267f9..2d540f13a52 100644 --- a/source/blender/imbuf/intern/dds/dds_api.h +++ b/source/blender/imbuf/intern/dds/dds_api.h @@ -26,7 +26,7 @@ extern "C" { #endif -bool imb_is_a_dds(const unsigned char *mem, const size_t size); +bool imb_is_a_dds(const unsigned char *mem, size_t size); bool imb_save_dds(struct ImBuf *ibuf, const char *name, int flags); struct ImBuf *imb_load_dds(const unsigned char *mem, size_t size, diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index e6f42da1597..ff86fbfdd38 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -102,13 +102,11 @@ MINLINE void float_to_byte_dither_v4( b[3] = unit_float_to_uchar_clamp(f[3]); } -/* Test if colorspace conversions of pixels in buffer need to take into account alpha. */ bool IMB_alpha_affects_rgb(const ImBuf *ibuf) { return (ibuf->flags & IB_alphamode_channel_packed) == 0; } -/* float to byte pixels, output 4-channel RGBA */ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from, int channels_from, @@ -275,7 +273,6 @@ void IMB_buffer_byte_from_float(uchar *rect_to, } } -/* float to byte pixels, output 4-channel RGBA */ void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from, int channels_from, @@ -366,7 +363,6 @@ void IMB_buffer_byte_from_float_mask(uchar *rect_to, } } -/* Byte to float pixels, input and output 4-channel RGBA. */ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from, int profile_to, @@ -386,7 +382,7 @@ void IMB_buffer_float_from_byte(float *rect_to, /* RGBA input */ for (y = 0; y < height; y++) { - const uchar *from = rect_from + stride_from * y * 4; + const uchar *from = rect_from + ((size_t)stride_from) * y * 4; float *to = rect_to + ((size_t)stride_to) * y * 4; if (profile_to == profile_from) { @@ -426,7 +422,6 @@ void IMB_buffer_float_from_byte(float *rect_to, } } -/* float to float pixels, output 4-channel RGBA */ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, int channels_from, @@ -592,7 +587,6 @@ void IMB_buffer_float_from_float_threaded(float *rect_to, } } -/* float to float pixels, output 4-channel RGBA */ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from, @@ -646,7 +640,6 @@ void IMB_buffer_float_from_float_mask(float *rect_to, } } -/* byte to byte pixels, input and output 4-channel RGBA */ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from, int profile_to, @@ -791,17 +784,14 @@ void IMB_float_from_rect(ImBuf *ibuf) */ rect_float = ibuf->rect_float; if (rect_float == NULL) { - size_t size; - - size = ((size_t)ibuf->x) * ibuf->y; - size = sizeof(float[4]) * size; - ibuf->channels = 4; - + const size_t size = IMB_get_rect_len(ibuf) * sizeof(float[4]); rect_float = MEM_callocN(size, "IMB_float_from_rect"); if (rect_float == NULL) { return; } + + ibuf->channels = 4; } /* first, create float buffer in non-linear space */ @@ -834,10 +824,9 @@ void IMB_float_from_rect(ImBuf *ibuf) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Color to Grayscale +/** \name Color to Gray-Scale * \{ */ -/* no profile conversion */ void IMB_color_to_bw(ImBuf *ibuf) { float *rct_fl = ibuf->rect_float; @@ -845,13 +834,13 @@ void IMB_color_to_bw(ImBuf *ibuf) size_t i; if (rct_fl) { - for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4) { + for (i = IMB_get_rect_len(ibuf); i > 0; i--, rct_fl += 4) { rct_fl[0] = rct_fl[1] = rct_fl[2] = IMB_colormanagement_get_luminance(rct_fl); } } if (rct) { - for (i = ((size_t)ibuf->x * ibuf->y); i > 0; i--, rct += 4) { + for (i = IMB_get_rect_len(ibuf); i > 0; i--, rct += 4) { rct[0] = rct[1] = rct[2] = IMB_colormanagement_get_luminance_byte(rct); } } @@ -892,7 +881,7 @@ void IMB_saturation(ImBuf *ibuf, float sat) if (rct) { float rgb[3]; - for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct += 4) { + for (i = IMB_get_rect_len(ibuf); i > 0; i--, rct += 4) { rgb_uchar_to_float(rgb, rct); rgb_to_hsv_v(rgb, hsv); hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rgb, rgb + 1, rgb + 2); @@ -901,7 +890,7 @@ void IMB_saturation(ImBuf *ibuf, float sat) } if (rct_fl) { - for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4) { + for (i = IMB_get_rect_len(ibuf); i > 0; i--, rct_fl += 4) { rgb_to_hsv_v(rct_fl, hsv); hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rct_fl, rct_fl + 1, rct_fl + 2); } diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index 343c8cd8f64..324bc9806c1 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -420,12 +420,6 @@ static int check_pixel_assigned( return res; } -/** - * if alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0 - * - * When a mask is given, only effect pixels with a mask value of 1, - * defined as #BAKE_MASK_MARGIN in rendercore.c - */ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) { const int width = ibuf->x; @@ -557,7 +551,6 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) } } -/* threadsafe version, only recreates existing maps */ void IMB_remakemipmap(ImBuf *ibuf, int use_filter) { ImBuf *hbuf = ibuf; @@ -594,7 +587,6 @@ void IMB_remakemipmap(ImBuf *ibuf, int use_filter) } } -/* frees too (if there) and recreates new data */ void IMB_makemipmap(ImBuf *ibuf, int use_filter) { ImBuf *hbuf = ibuf; diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 804c9c3eb89..23c4c53d602 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -20,11 +20,10 @@ /** \file * \ingroup imbuf * - * This file was moved here from the src/ directory. It is meant to - * deal with endianness. It resided in a general blending lib. The - * other functions were only used during rendering. This single - * function remained. It should probably move to imbuf/intern/util.c, - * but we'll keep it here for the time being. (nzc) + * This file was moved here from the `src/` directory. + * It is meant to deal with endianness. It resided in a general blending lib. + * The other functions were only used during rendering. This single function remained. + * It should probably move to `imbuf/intern/util.c`, but we'll keep it here for the time being. */ #include <math.h> @@ -41,7 +40,6 @@ #include "IMB_imbuf_types.h" #include <math.h> -/* Only this one is used liberally here, and in imbuf */ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf) { size_t size; @@ -77,7 +75,8 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf) } } -static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y) +static void pixel_from_buffer( + const struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y) { size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x; @@ -96,7 +95,7 @@ static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float ** * \{ */ void bicubic_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { if (outF) { BLI_bicubic_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); @@ -106,7 +105,7 @@ void bicubic_interpolation_color( } } -void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) +void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { unsigned char *outI = NULL; float *outF = NULL; @@ -127,16 +126,16 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in /** \name Bi-Linear Interpolation * \{ */ -BLI_INLINE void bilinear_interpolation_color_fl( - struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) +void bilinear_interpolation_color_fl( + const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) { BLI_assert(outF); BLI_assert(in->rect_float); BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); } -BLI_INLINE void bilinear_interpolation_color_char( - struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) +void bilinear_interpolation_color_char( + const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) { BLI_assert(outI); BLI_assert(in->rect); @@ -144,7 +143,7 @@ BLI_INLINE void bilinear_interpolation_color_char( } void bilinear_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { if (outF) { BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v); @@ -157,12 +156,8 @@ void bilinear_interpolation_color( /* function assumes out to be zero'ed, only does RGBA */ /* BILINEAR INTERPOLATION */ -/* Note about wrapping, the u/v still needs to be within the image bounds, - * just the interpolation is wrapped. - * This the same as bilinear_interpolation_color except it wraps - * rather than using empty and emptyI. */ void bilinear_interpolation_color_wrap( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { float *row1, *row2, *row3, *row4, a, b; unsigned char *row1I, *row2I, *row3I, *row4I; @@ -233,7 +228,7 @@ void bilinear_interpolation_color_wrap( } } -void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) +void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { unsigned char *outI = NULL; float *outF = NULL; @@ -254,9 +249,8 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i /** \name Nearest Interpolation * \{ */ -/* functions assumes out to be zero'ed, only does RGBA */ -BLI_INLINE void nearest_interpolation_color_char( - struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) +void nearest_interpolation_color_char( + const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v) { BLI_assert(outI); BLI_assert(in->rect); @@ -270,7 +264,7 @@ BLI_INLINE void nearest_interpolation_color_char( return; } - const size_t offset = (in->x * y1 + x1) * 4; + const size_t offset = ((size_t)in->x * y1 + x1) * 4; const unsigned char *dataI = (unsigned char *)in->rect + offset; outI[0] = dataI[0]; outI[1] = dataI[1]; @@ -278,8 +272,8 @@ BLI_INLINE void nearest_interpolation_color_char( outI[3] = dataI[3]; } -BLI_INLINE void nearest_interpolation_color_fl( - struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) +void nearest_interpolation_color_fl( + const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v) { BLI_assert(outF); BLI_assert(in->rect_float); @@ -293,13 +287,13 @@ BLI_INLINE void nearest_interpolation_color_fl( return; } - const size_t offset = (in->x * y1 + x1) * 4; + const size_t offset = ((size_t)in->x * y1 + x1) * 4; const float *dataF = in->rect_float + offset; copy_v4_v4(outF, dataF); } void nearest_interpolation_color( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { if (outF) { nearest_interpolation_color_fl(in, outI, outF, u, v); @@ -310,7 +304,7 @@ void nearest_interpolation_color( } void nearest_interpolation_color_wrap( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) + const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v) { const float *dataF; unsigned char *dataI; @@ -348,7 +342,7 @@ void nearest_interpolation_color_wrap( } } -void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) +void nearest_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout) { unsigned char *outI = NULL; float *outF = NULL; @@ -363,139 +357,6 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in nearest_interpolation_color(in, outI, outF, u, v); } -/* -------------------------------------------------------------------- */ -/** \name Image transform - * \{ */ -typedef struct TransformUserData { - ImBuf *src; - ImBuf *dst; - float start_uv[2]; - float add_x[2]; - float add_y[2]; - rctf src_crop; -} TransformUserData; - -static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2]) -{ - float orig[2]; - orig[0] = 0.0f; - orig[1] = 0.0f; - mul_v2_m3v2(r_start_uv, transform_matrix, orig); -} - -static void imb_transform_calc_add_x(const float transform_matrix[3][3], - const float start_uv[2], - const int width, - float r_add_x[2]) -{ - float uv_max_x[2]; - uv_max_x[0] = width; - uv_max_x[1] = 0.0f; - mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); - sub_v2_v2(r_add_x, start_uv); - mul_v2_fl(r_add_x, 1.0f / width); -} - -static void imb_transform_calc_add_y(const float transform_matrix[3][3], - const float start_uv[2], - const int height, - float r_add_y[2]) -{ - float uv_max_y[2]; - uv_max_y[0] = 0.0f; - uv_max_y[1] = height; - mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); - sub_v2_v2(r_add_y, start_uv); - mul_v2_fl(r_add_y, 1.0f / height); -} - -typedef void (*InterpolationColorFunction)( - struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); -BLI_INLINE void imb_transform_scanlines(const TransformUserData *user_data, - int scanline, - InterpolationColorFunction interpolation) -{ - const int width = user_data->dst->x; - - float uv[2]; - madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline); - - unsigned char *outI = NULL; - float *outF = NULL; - pixel_from_buffer(user_data->dst, &outI, &outF, 0, scanline); - - for (int xi = 0; xi < width; xi++) { - if (uv[0] >= user_data->src_crop.xmin && uv[0] < user_data->src_crop.xmax && - uv[1] >= user_data->src_crop.ymin && uv[1] < user_data->src_crop.ymax) { - interpolation(user_data->src, outI, outF, uv[0], uv[1]); - } - add_v2_v2(uv, user_data->add_x); - if (outI) { - outI += 4; - } - if (outF) { - outF += 4; - } - } -} - -static void imb_transform_nearest_scanlines(void *custom_data, int scanline) -{ - const TransformUserData *user_data = custom_data; - InterpolationColorFunction interpolation = NULL; - if (user_data->dst->rect_float) { - interpolation = nearest_interpolation_color_fl; - } - else { - interpolation = nearest_interpolation_color_char; - } - imb_transform_scanlines(user_data, scanline, interpolation); -} - -static void imb_transform_bilinear_scanlines(void *custom_data, int scanline) -{ - const TransformUserData *user_data = custom_data; - InterpolationColorFunction interpolation = NULL; - if (user_data->dst->rect_float) { - interpolation = bilinear_interpolation_color_fl; - } - else if (user_data->dst->rect) { - interpolation = bilinear_interpolation_color_char; - } - imb_transform_scanlines(user_data, scanline, interpolation); -} - -static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFilterMode filter) -{ - ScanlineThreadFunc scanline_func = NULL; - switch (filter) { - case IMB_FILTER_NEAREST: - scanline_func = imb_transform_nearest_scanlines; - break; - case IMB_FILTER_BILINEAR: - scanline_func = imb_transform_bilinear_scanlines; - break; - } - return scanline_func; -} - -void IMB_transform(struct ImBuf *src, - struct ImBuf *dst, - float transform_matrix[3][3], - struct rctf *src_crop, - const eIMBInterpolationFilterMode filter) -{ - TransformUserData user_data; - user_data.src = src; - user_data.dst = dst; - user_data.src_crop = *src_crop; - imb_transform_calc_start_uv(transform_matrix, user_data.start_uv); - imb_transform_calc_add_x(transform_matrix, user_data.start_uv, src->x, user_data.add_x); - imb_transform_calc_add_y(transform_matrix, user_data.start_uv, src->y, user_data.add_y); - ScanlineThreadFunc scanline_func = imb_transform_scanline_func(filter); - IMB_processor_apply_threaded_scanlines(dst->y, scanline_func, &user_data); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -637,7 +498,6 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float b /** \name Sample Pixel * \{ */ -/* Sample pixel of image using NEAREST method. */ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]) { if (ibuf->rect_float) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index bbb0f3b5b22..a85ba65d014 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -37,6 +37,8 @@ # include "BLI_winstuff.h" #endif +#include "PIL_time.h" + #include "IMB_anim.h" #include "IMB_indexer.h" #include "imbuf.h" @@ -170,7 +172,6 @@ struct anim_index *IMB_indexer_open(const char *name) int i; if (!fp) { - fprintf(stderr, "Couldn't open indexer file: %s\n", name); return NULL; } @@ -183,13 +184,13 @@ struct anim_index *IMB_indexer_open(const char *name) header[12] = 0; if (memcmp(header, binary_header_str, 8) != 0) { - fprintf(stderr, "Error reading %s: Binary file type string missmatch\n", name); + fprintf(stderr, "Error reading %s: Binary file type string mismatch\n", name); fclose(fp); return NULL; } if (atoi(header + 9) != INDEX_FILE_VERSION) { - fprintf(stderr, "Error reading %s: File version missmatch\n", name); + fprintf(stderr, "Error reading %s: File version mismatch\n", name); fclose(fp); return NULL; } @@ -222,7 +223,7 @@ struct anim_index *IMB_indexer_open(const char *name) } if (UNLIKELY(items_read != idx->num_entries * 5)) { - fprintf(stderr, "Error: Element data size missmatch in: %s\n", name); + fprintf(stderr, "Error: Element data size mismatch in: %s\n", name); MEM_freeN(idx->entries); MEM_freeN(idx); fclose(fp); @@ -815,12 +816,16 @@ typedef struct FFmpegIndexBuilderContext { double pts_time_base; int frameno, frameno_gapless; int start_pts_set; + + bool build_only_on_bad_performance; + bool building_cancelled; } FFmpegIndexBuilderContext; static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, - int quality) + int quality, + bool build_only_on_bad_performance) { FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext), "FFmpeg index builder context"); @@ -832,6 +837,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, context->proxy_sizes_in_use = proxy_sizes_in_use; context->num_proxy_sizes = IMB_PROXY_MAX_SLOT; context->num_indexers = IMB_TC_MAX_SLOT; + context->build_only_on_bad_performance = build_only_on_bad_performance; memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx)); memset(context->indexer, 0, sizeof(context->indexer)); @@ -937,15 +943,17 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int { int i; + const bool do_rollback = stop || context->building_cancelled; + for (i = 0; i < context->num_indexers; i++) { if (context->tcs_in_use & tc_types[i]) { - IMB_index_builder_finish(context->indexer[i], stop); + IMB_index_builder_finish(context->indexer[i], do_rollback); } } for (i = 0; i < context->num_proxy_sizes; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - free_proxy_output_ffmpeg(context->proxy_ctx[i], stop); + free_proxy_output_ffmpeg(context->proxy_ctx[i], do_rollback); } } @@ -1096,6 +1104,111 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, return 1; } +/* Get number of frames, that can be decoded in specified time period. */ +static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *context, + const double time_period) +{ + AVFrame *in_frame = av_frame_alloc(); + AVPacket *packet = av_packet_alloc(); + + const double start = PIL_check_seconds_timer(); + int frames_decoded = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + + int ret = avcodec_send_packet(context->iCodecCtx, packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + + if (ret < 0) { + fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); + break; + } + frames_decoded++; + } + + const double end = PIL_check_seconds_timer(); + + if (end > start + time_period) { + break; + } + } + + avcodec_flush_buffers(context->iCodecCtx); + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return frames_decoded; +} + +/* Read up to 10k movie packets and return max GOP size detected. + * Number of packets is arbitrary. It should be as large as possible, but processed within + * reasonable time period, so detected GOP size is as close to real as possible. */ +static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *context) +{ + AVPacket *packet = av_packet_alloc(); + + const int packets_max = 10000; + int packet_index = 0; + int max_gop = 0; + int cur_gop = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + packet_index++; + cur_gop++; + + if (packet->flags & AV_PKT_FLAG_KEY) { + max_gop = max_ii(max_gop, cur_gop); + cur_gop = 0; + } + + if (packet_index > packets_max) { + break; + } + } + + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return max_gop; +} + +/* Assess scrubbing performance of provided file. This function is not meant to be very exact. + * It compares number number of frames decoded in reasonable time with largest detected GOP size. + * Because seeking happens in single GOP, it means, that maximum seek time can be detected this + * way. + * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or + * equal. + */ +static bool indexer_need_to_build_proxy(FFmpegIndexBuilderContext *context) +{ + if (!context->build_only_on_bad_performance) { + return true; + } + + /* Make sure, that file is not cold read. */ + indexer_performance_get_decode_rate(context, 0.1); + /* Get decode rate per 100ms. This is arbitrary, but seems to be good baseline cadence of + * seeking. */ + const int decode_rate = indexer_performance_get_decode_rate(context, 0.1); + const int max_gop_size = indexer_performance_get_max_gop_size(context); + + if (max_gop_size <= 10 || max_gop_size < decode_rate) { + printf("Skipping proxy building for %s: Decoding performance is already good.\n", + context->iFormatCtx->url); + context->building_cancelled = true; + return false; + } + + return true; +} + #endif /* ---------------------------------------------------------------------- @@ -1275,7 +1388,8 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_sizes_in_use, int quality, const bool overwrite, - GSet *file_list) + GSet *file_list, + bool build_only_on_bad_performance) { IndexBuildContext *context = NULL; IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use; @@ -1329,9 +1443,13 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, switch (anim->curtype) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); + context = index_ffmpeg_create_context( + anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance); break; +#else + UNUSED_VARS(build_only_on_bad_performance); #endif + #ifdef WITH_AVI default: context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); @@ -1359,7 +1477,9 @@ void IMB_anim_index_rebuild(struct IndexBuildContext *context, switch (context->anim_type) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) { + index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + } break; #endif #ifdef WITH_AVI diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index df516d2a5c4..1c8548009e8 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -251,12 +251,6 @@ bool imb_is_a_iris(const uchar *mem, size_t size) return ((GS(mem) == IMAGIC) || (GSS(mem) == IMAGIC)); } -/* - * longimagedata - - * read in a B/W RGB or RGBA iris image file and return a - * pointer to an array of ints. - */ - struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { uint *base, *lptr = NULL; @@ -274,7 +268,7 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors return NULL; } - /* Could pe part of the magic check above, + /* Could be part of the magic check above, * by convention this check only requests the size needed to read it's magic though. */ if (size < HEADER_SIZE) { return NULL; @@ -542,7 +536,7 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors } } else if (image.zsize == 2) { - /* grayscale with alpha */ + /* Gray-scale with alpha. */ rect = (uchar *)ibuf->rect; for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) { rect[0] = rect[2]; @@ -570,7 +564,7 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors } } else if (image.zsize == 2) { - /* grayscale with alpha */ + /* Gray-scale with alpha. */ fbase = ibuf->rect_float; for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) { fbase[0] = fbase[2]; diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 117e0d97b2e..3af7367ef6e 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -424,13 +424,13 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, h = image->comps[0].h; switch (image->numcomps) { - case 1: /* Grayscale */ - case 3: /* Color */ + case 1: /* Gray-scale. */ + case 3: /* Color. */ planes = 24; use_alpha = false; break; - default: /* 2 or 4 - Grayscale or Color + alpha */ - planes = 32; /* grayscale + alpha */ + default: /* 2 or 4 - Gray-scale or Color + alpha. */ + planes = 32; /* Gray-scale + alpha. */ use_alpha = true; break; } @@ -529,7 +529,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream, r = image->comps[0].data; a = (use_alpha) ? image->comps[1].data : NULL; - /* grayscale */ + /* Gray-scale. */ if (use_alpha) { a = image->comps[3].data; PIXEL_LOOPER_BEGIN (rect_uchar) { @@ -848,7 +848,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* standard linear-to-SRGB conversion if float buffer wasn't managed */ chanel_colormanage_cb = linearrgb_to_srgb; } @@ -891,8 +891,8 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) prec = 8; } - /* 32bit images == alpha channel */ - /* grayscale not supported yet */ + /* 32bit images == alpha channel. */ + /* Gray-scale not supported yet. */ numcomps = (ibuf->planes == 32) ? 4 : 3; } diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 48b5b0c34db..c3a07d7face 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -40,8 +40,9 @@ #include "IMB_imbuf_types.h" #include "IMB_metadata.h" #include "imbuf.h" -#include "jerror.h" -#include "jpeglib.h" + +#include <jerror.h> +#include <jpeglib.h> #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 6cc1932eff6..f290ab1a060 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -87,6 +87,8 @@ typedef struct MovieCacheItem { ImBuf *ibuf; MEM_CacheLimiterHandleC *c_handle; void *priority_data; + /* Indicates that #ibuf is null, because there was an error during load. */ + bool added_empty; } MovieCacheItem; static unsigned int moviecache_hashhash(const void *keyv) @@ -120,8 +122,13 @@ static void moviecache_valfree(void *val) PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf); - if (item->ibuf) { + if (item->c_handle) { + BLI_mutex_lock(&limitor_lock); MEM_CacheLimiter_unmanage(item->c_handle); + BLI_mutex_unlock(&limitor_lock); + } + + if (item->ibuf) { IMB_freeImBuf(item->ibuf); } @@ -141,11 +148,16 @@ static void check_unused_keys(MovieCache *cache) while (!BLI_ghashIterator_done(&gh_iter)) { const MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); const MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); - bool remove; BLI_ghashIterator_step(&gh_iter); - remove = !item->ibuf; + if (item->added_empty) { + /* Don't remove entries that have been added empty. Those indicate that the image couldn't be + * loaded correctly. */ + continue; + } + + bool remove = !item->ibuf; if (remove) { PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item); @@ -230,6 +242,9 @@ static int get_item_priority(void *item_v, int default_priority) static bool get_item_destroyable(void *item_v) { MovieCacheItem *item = (MovieCacheItem *)item_v; + if (item->ibuf == NULL) { + return true; + } /* IB_BITMAPDIRTY means image was modified from inside blender and * changes are not saved to disk. * @@ -253,6 +268,7 @@ void IMB_moviecache_destruct(void) { if (limitor) { delete_MEM_CacheLimiter(limitor); + limitor = NULL; } } @@ -309,7 +325,9 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo IMB_moviecache_init(); } - IMB_refImBuf(ibuf); + if (ibuf != NULL) { + IMB_refImBuf(ibuf); + } key = BLI_mempool_alloc(cache->keys_pool); key->cache_owner = cache; @@ -324,6 +342,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo item->cache_owner = cache; item->c_handle = NULL; item->priority_data = NULL; + item->added_empty = ibuf == NULL; if (cache->getprioritydatafp) { item->priority_data = cache->getprioritydatafp(userkey); @@ -365,7 +384,7 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu size_t mem_in_use, mem_limit, elem_size; bool result = false; - elem_size = get_size_in_memory(ibuf); + elem_size = (ibuf == NULL) ? 0 : get_size_in_memory(ibuf); mem_limit = MEM_CacheLimiter_get_maximum(); BLI_mutex_lock(&limitor_lock); @@ -389,7 +408,7 @@ void IMB_moviecache_remove(MovieCache *cache, void *userkey) BLI_ghash_remove(cache->hash, &key, moviecache_keyfree, moviecache_valfree); } -ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) +ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey, bool *r_is_cached_empty) { MovieCacheKey key; MovieCacheItem *item; @@ -398,6 +417,10 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) key.userkey = userkey; item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key); + if (r_is_cached_empty) { + *r_is_cached_empty = false; + } + if (item) { if (item->ibuf) { BLI_mutex_lock(&limitor_lock); @@ -408,6 +431,9 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) return item->ibuf; } + if (r_is_cached_empty) { + *r_is_cached_empty = true; + } } return NULL; @@ -470,7 +496,6 @@ void IMB_moviecache_cleanup(MovieCache *cache, } } -/* get segments of cached frames. useful for debugging cache policies */ void IMB_moviecache_get_cache_segments( MovieCache *cache, int proxy, int render_flags, int *r_totseg, int **r_points) { diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index 3ad902a241d..22533b04b58 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -221,7 +221,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac string ics = spec.get_string_attribute("oiio:ColorSpace"); BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE); - /* only use colorspaces exis */ + /* Only use color-spaces exist. */ if (colormanage_colorspace_get_named(file_colorspace)) { strcpy(colorspace, file_colorspace); } diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.h b/source/blender/imbuf/intern/oiio/openimageio_api.h index 659050cdb00..1201bd1b5e0 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.h +++ b/source/blender/imbuf/intern/oiio/openimageio_api.h @@ -31,7 +31,7 @@ extern "C" { struct ImBuf; -bool imb_is_a_photoshop(const unsigned char *mem, const size_t size); +bool imb_is_a_photoshop(const unsigned char *mem, size_t size); int imb_save_photoshop(struct ImBuf *ibuf, const char *name, int flags); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index adf09f8dda8..ec8cf36dd49 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -327,10 +327,6 @@ static half float_to_half_safe(const float value) extern "C" { -/** - * Test presence of OpenEXR file. - * \param mem: pointer to loaded OpenEXR bitstream - */ bool imb_is_a_openexr(const unsigned char *mem, const size_t size) { /* No define is exposed for this size. */ @@ -687,7 +683,7 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data); void *IMB_exr_get_handle(void) { - ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle"); + ExrHandle *data = MEM_cnew<ExrHandle>("exr handle"); data->multiView = new StringVector(); BLI_addtail(&exrhandles, data); @@ -781,9 +777,6 @@ static void imb_exr_insert_view_name(char *name_full, const char *passname, cons } } -/* adds flattened ExrChannels */ -/* xstride, ystride and rect can be done in set_channel too, for tile writing */ -/* passname does not include view */ void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, @@ -796,7 +789,7 @@ void IMB_exr_add_channel(void *handle, ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; - echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel"); + echan = MEM_cnew<ExrChannel>("exr channel"); echan->m = new MultiViewChannelName(); if (layname && layname[0] != '\0') { @@ -840,7 +833,6 @@ void IMB_exr_add_channel(void *handle, BLI_addtail(&data->channels, echan); } -/* used for output files (from RenderResult) (single and multilayer, single and multiview) */ bool IMB_exr_begin_write(void *handle, const char *filename, int width, @@ -896,8 +888,6 @@ bool IMB_exr_begin_write(void *handle, return (data->ofile != nullptr); } -/* only used for writing temp. render results (not image files) - * (FSA and Save Buffers) */ void IMB_exrtile_begin_write( void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { @@ -963,7 +953,6 @@ void IMB_exrtile_begin_write( } } -/* read from file */ bool IMB_exr_begin_read( void *handle, const char *filename, int *width, int *height, const bool parse_channels) { @@ -1024,8 +1013,6 @@ bool IMB_exr_begin_read( return true; } -/* still clumsy name handling, layers/channels can be ordered as list in list later */ -/* passname here is the raw channel name without the layer */ void IMB_exr_set_channel( void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { @@ -1167,8 +1154,6 @@ void IMB_exr_write_channels(void *handle) } } -/* temporary function, used for FSA and Save Buffers */ -/* called once per tile * view */ void IMB_exrtile_write_channels( void *handle, int partx, int party, int level, const char *viewname, bool empty) { @@ -1511,7 +1496,7 @@ static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname) ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name)); if (lay == nullptr) { - lay = (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer"); + lay = MEM_cnew<ExrLayer>("exr layer"); BLI_addtail(lb, lay); BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME); } @@ -1524,7 +1509,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name)); if (pass == nullptr) { - pass = (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass"); + pass = MEM_cnew<ExrPass>("exr pass"); if (STREQ(passname, "Combined")) { BLI_addhead(lb, pass); @@ -1937,8 +1922,8 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, file = new MultiPartInputFile(*membuf); Box2i dw = file->header(0).dataWindow(); - const int width = dw.max.x - dw.min.x + 1; - const int height = dw.max.y - dw.min.y + 1; + const size_t width = dw.max.x - dw.min.x + 1; + const size_t height = dw.max.y - dw.min.y + 1; // printf("OpenEXR-load: image data window %d %d %d %d\n", // dw.min.x, dw.min.y, dw.max.x, dw.max.y); @@ -2001,8 +1986,8 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, const bool has_luma = exr_has_luma(*file); FrameBuffer frameBuffer; float *first; - int xstride = sizeof(float[4]); - int ystride = -xstride * width; + size_t xstride = sizeof(float[4]); + size_t ystride = -xstride * width; imb_addrectfloatImBuf(ibuf); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h index 940715690a7..4321c95db30 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.h +++ b/source/blender/imbuf/intern/openexr/openexr_api.h @@ -32,7 +32,11 @@ extern "C" { void imb_initopenexr(void); void imb_exitopenexr(void); -bool imb_is_a_openexr(const unsigned char *mem, const size_t size); +/** + * Test presence of OpenEXR file. + * \param mem: pointer to loaded OpenEXR bit-stream. + */ +bool imb_is_a_openexr(const unsigned char *mem, size_t size); bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags); diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 82a5d161ded..0c3c4a4400f 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -23,7 +23,7 @@ #pragma once -/* experiment with more advanced exr api */ +/* Experiment with more advanced EXR API. */ /* XXX layer+pass name max 64? */ /* This api also supports max 8 channels per pass now. easy to fix! */ @@ -41,6 +41,12 @@ struct StampData; void *IMB_exr_get_handle(void); void *IMB_exr_get_handle_name(const char *name); + +/** + * Adds flattened #ExrChannel's + * `xstride`, `ystride` and `rect` can be done in set_channel too, for tile writing. + * \param passname: Does not include view. + */ void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, @@ -50,17 +56,32 @@ void IMB_exr_add_channel(void *handle, float *rect, bool use_half_float); +/** + * Read from file. + */ bool IMB_exr_begin_read( - void *handle, const char *filename, int *width, int *height, const bool parse_channels); + void *handle, const char *filename, int *width, int *height, bool parse_channels); +/** + * Used for output files (from #RenderResult) (single and multi-layer, single and multi-view). + */ bool IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress, const struct StampData *stamp); +/** + * Only used for writing temp. render results (not image files) + * (FSA and Save Buffers). + */ void IMB_exrtile_begin_write( void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); +/** + * Still clumsy name handling, layers/channels can be ordered as list in list later. + * + * \param passname: Here is the raw channel name without the layer. + */ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, @@ -74,6 +95,10 @@ float *IMB_exr_channel_rect(void *handle, void IMB_exr_read_channels(void *handle); void IMB_exr_write_channels(void *handle); +/** + * Temporary function, used for FSA and Save Buffers. + * called once per `tile * view`. + */ void IMB_exrtile_write_channels( void *handle, int partx, int party, int level, const char *viewname, bool empty); void IMB_exr_clear_channels(void *handle); diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 639100ac6fe..9b4d6178613 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -49,7 +49,7 @@ bool IMB_exr_begin_read(void * /*handle*/, int * /*height*/, const bool /*add_channels*/) { - return 0; + return false; } bool IMB_exr_begin_write(void * /*handle*/, const char * /*filename*/, diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 399fd487065..aaf56b1daa0 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -23,7 +23,7 @@ * \todo Save floats as 16 bits per channel, currently readonly. */ -#include "png.h" +#include <png.h> #include "BLI_fileops.h" #include "BLI_math.h" @@ -153,7 +153,7 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* Standard linear-to-SRGB conversion if float buffer wasn't managed. */ chanel_colormanage_cb = linearrgb_to_srgb; } diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 7f4e4dd31df..925ef0a8502 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -77,7 +77,7 @@ static const unsigned char *oldreadcolrs(RGBE *scan, 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; i--) { + for (i = scan[0][EXP] << rshift; i > 0 && len > 0; i--) { COPY_RGBE(scan[-1], scan[0]); scan++; len--; @@ -227,7 +227,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, int found = 0; int width = 0, height = 0; const unsigned char *ptr, *mem_eof = mem + size; - char oriY[80], oriX[80]; + char oriY[3], oriX[3]; if (!imb_is_a_hdr(mem, size)) { return NULL; @@ -244,22 +244,33 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, } } - if ((found && (x < (size + 2))) == 0) { + if ((found && (x < (size - 1))) == 0) { /* Data not found! */ return NULL; } - if (sscanf((const char *)&mem[x + 1], - "%79s %d %79s %d", - (char *)&oriY, - &height, - (char *)&oriX, - &width) != 4) { + 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 unsigned char *)strchr((const char *)&mem[x + 1], '\n'); + ptr = (const unsigned char *)strchr((const char *)&mem[x], '\n'); + if (ptr == NULL || ptr >= mem_eof) { + return NULL; + } ptr++; if (flags & IB_test) { diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 50210650f05..c75bdfa375c 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -126,7 +126,7 @@ ImBuf *IMB_ibImageFromMemory(const unsigned char *mem, } if ((flags & IB_test) == 0) { - fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr); + fprintf(stderr, "%s: unknown file-format (%s)\n", __func__, descr); } return NULL; diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 4b5d68b9c13..1d81ee768e9 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -251,9 +251,6 @@ static void rect_crop_16bytes(void **buf_p, const int size_src[2], const rcti *c *buf_p = (void *)MEM_reallocN(*buf_p, sizeof(uint[4]) * size_dst[0] * size_dst[1]); } -/** - * In-place image crop. - */ void IMB_rect_crop(ImBuf *ibuf, const rcti *crop) { const int size_src[2] = { @@ -302,9 +299,6 @@ static void rect_realloc_16bytes(void **buf_p, const uint size[2]) *buf_p = MEM_mallocN(sizeof(uint[4]) * size[0] * size[1], __func__); } -/** - * In-place size setting (caller must fill in buffer contents). - */ void IMB_rect_size_set(ImBuf *ibuf, const uint size[2]) { BLI_assert(size[0] > 0 && size[1] > 0); @@ -1070,11 +1064,6 @@ void IMB_rectblend_threaded(ImBuf *dbuf, } } -/** - * Replace pixels of entire image with solid color. - * \param ibuf: 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]) { int num; @@ -1107,15 +1096,6 @@ void IMB_rectfill(ImBuf *drect, const float col[4]) } } -/** - * Replace pixels of image area with solid color. - * \param ibuf: 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. - * \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_replace( const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2) { @@ -1273,21 +1253,6 @@ void buf_rectfill_area(unsigned char *rect, } } -/** - * Blend pixels of image area with solid color. - * - * For images with `uchar` buffer use color matching image color-space. - * For images with float buffer use color display color-space. - * If display color-space can not be referenced, use color in SRGB color-space. - * - * \param ibuf: an image to be filled with color. It must be 4 channel image. - * \param col: RGBA color. - * \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. - * \param display: color-space reference for display space. - */ void IMB_rectfill_area(ImBuf *ibuf, const float col[4], int x1, diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c index c2fc2190ce5..f02f3e37d6a 100644 --- a/source/blender/imbuf/intern/rotate.c +++ b/source/blender/imbuf/intern/rotate.c @@ -32,7 +32,7 @@ void IMB_flipy(struct ImBuf *ibuf) { - int x, y; + size_t x_size, y_size; if (ibuf == NULL) { return; @@ -41,21 +41,23 @@ void IMB_flipy(struct ImBuf *ibuf) if (ibuf->rect) { unsigned int *top, *bottom, *line; - x = ibuf->x; - y = ibuf->y; + x_size = ibuf->x; + y_size = ibuf->y; + + const size_t stride = x_size * sizeof(int); top = ibuf->rect; - bottom = top + ((y - 1) * x); - line = MEM_mallocN(x * sizeof(int), "linebuf"); + bottom = top + ((y_size - 1) * x_size); + line = MEM_mallocN(stride, "linebuf"); - y >>= 1; + y_size >>= 1; - for (; y > 0; y--) { - memcpy(line, top, x * sizeof(int)); - memcpy(top, bottom, x * sizeof(int)); - memcpy(bottom, line, x * sizeof(int)); - bottom -= x; - top += x; + for (; y_size > 0; y_size--) { + memcpy(line, top, stride); + memcpy(top, bottom, stride); + memcpy(bottom, line, stride); + bottom -= x_size; + top += x_size; } MEM_freeN(line); @@ -64,21 +66,23 @@ void IMB_flipy(struct ImBuf *ibuf) if (ibuf->rect_float) { float *topf = NULL, *bottomf = NULL, *linef = NULL; - x = ibuf->x; - y = ibuf->y; + x_size = ibuf->x; + y_size = ibuf->y; + + const size_t stride = x_size * 4 * sizeof(float); topf = ibuf->rect_float; - bottomf = topf + 4 * ((y - 1) * x); - linef = MEM_mallocN(4 * x * sizeof(float), "linebuff"); + bottomf = topf + 4 * ((y_size - 1) * x_size); + linef = MEM_mallocN(stride, "linebuf"); - y >>= 1; + y_size >>= 1; - for (; y > 0; y--) { - memcpy(linef, topf, 4 * x * sizeof(float)); - memcpy(topf, bottomf, 4 * x * sizeof(float)); - memcpy(bottomf, linef, 4 * x * sizeof(float)); - bottomf -= 4 * x; - topf += 4 * x; + for (; y_size > 0; y_size--) { + memcpy(linef, topf, stride); + memcpy(topf, bottomf, stride); + memcpy(bottomf, linef, stride); + bottomf -= 4 * x_size; + topf += 4 * x_size; } MEM_freeN(linef); @@ -99,20 +103,22 @@ void IMB_flipx(struct ImBuf *ibuf) if (ibuf->rect) { for (yi = y - 1; yi >= 0; yi--) { + const size_t x_offset = (size_t)x * yi; for (xr = x - 1, xl = 0; xr >= xl; xr--, xl++) { - SWAP(unsigned int, ibuf->rect[(x * yi) + xr], ibuf->rect[(x * yi) + xl]); + SWAP(unsigned int, ibuf->rect[x_offset + xr], ibuf->rect[x_offset + xl]); } } } if (ibuf->rect_float) { for (yi = y - 1; yi >= 0; yi--) { + const size_t x_offset = (size_t)x * yi; for (xr = x - 1, xl = 0; xr >= xl; xr--, xl++) { - memcpy(&px_f, &ibuf->rect_float[((x * yi) + xr) * 4], sizeof(float[4])); - memcpy(&ibuf->rect_float[((x * yi) + xr) * 4], - &ibuf->rect_float[((x * yi) + xl) * 4], + memcpy(&px_f, &ibuf->rect_float[(x_offset + xr) * 4], sizeof(float[4])); + memcpy(&ibuf->rect_float[(x_offset + xr) * 4], + &ibuf->rect_float[(x_offset + xl) * 4], sizeof(float[4])); - memcpy(&ibuf->rect_float[((x * yi) + xl) * 4], &px_f, sizeof(float[4])); + memcpy(&ibuf->rect_float[(x_offset + xl) * 4], &px_f, sizeof(float[4])); } } } diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c index 1c4b7af6ef1..a18ba6748de 100644 --- a/source/blender/imbuf/intern/scaling.c +++ b/source/blender/imbuf/intern/scaling.c @@ -372,7 +372,6 @@ MINLINE void premul_ushort_to_straight_uchar(unsigned char *result, const unsign } } -/* result in ibuf2, scaling should be done correctly */ void imb_onehalf_no_alloc(struct ImBuf *ibuf2, struct ImBuf *ibuf1) { int x, y; @@ -912,7 +911,7 @@ static ImBuf *scaledownx(struct ImBuf *ibuf, int newx) { const int do_rect = (ibuf->rect != NULL); const int do_float = (ibuf->rect_float != NULL); - const size_t rect_size = ibuf->x * ibuf->y * 4; + const size_t rect_size = IMB_get_rect_len(ibuf) * 4; uchar *rect, *_newrect, *newrect; float *rectf, *_newrectf, *newrectf; @@ -1053,7 +1052,7 @@ static ImBuf *scaledowny(struct ImBuf *ibuf, int newy) { const int do_rect = (ibuf->rect != NULL); const int do_float = (ibuf->rect_float != NULL); - const size_t rect_size = ibuf->x * ibuf->y * 4; + const size_t rect_size = IMB_get_rect_len(ibuf) * 4; uchar *rect, *_newrect, *newrect; float *rectf, *_newrectf, *newrectf; @@ -1661,9 +1660,6 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy) } } -/** - * Return true if \a ibuf is modified. - */ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) { BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); @@ -1709,9 +1705,6 @@ struct imbufRGBA { float r, g, b, a; }; -/** - * Return true if \a ibuf is modified. - */ bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) { BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index d3c91b55f22..dae2604802f 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -757,7 +757,6 @@ float *IMB_stereo3d_from_rectf(ImageFormatData *im_format, return r_rectf; } -/* left/right are always float */ ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) { ImBuf *ibuf_stereo = NULL; @@ -1275,7 +1274,6 @@ static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) /** \name Preparing To Call The Read Functions * \{ */ -/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, ImBuf **r_ibuf_left, diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index aa1da65253d..c39ce2e9a2a 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -523,7 +523,6 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im path, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); } -/* read thumbnail for file and returns new imbuf for thumbnail */ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) { char thumb[FILE_MAX]; @@ -540,7 +539,6 @@ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) return img; } -/* delete all thumbs for the file */ void IMB_thumb_delete(const char *path, ThumbSize size) { char thumb[FILE_MAX]; @@ -559,7 +557,6 @@ void IMB_thumb_delete(const char *path, ThumbSize size) } } -/* create the thumb if necessary and manage failed and old thumbs */ ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source) { char thumb_path[FILE_MAX]; diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index d9e1db27ef0..1d3589f00a6 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -29,7 +29,7 @@ * 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 (grayscale) images correctly, with + * 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. */ @@ -52,7 +52,7 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -#include "tiffio.h" +#include <tiffio.h> #ifdef WIN32 # include "utfconv.h" @@ -457,7 +457,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) } /* simple RGBA image */ - if (!(bitspersample == 32 || bitspersample == 16)) { + if (!(ELEM(bitspersample, 32, 16))) { success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0); } /* contiguous channels: RGBRGBRGB */ @@ -488,7 +488,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */ copy_vn_fl(fbuf, ibuf->x, 1.0f); } - else if (chan >= spp) { /* for grayscale, duplicate first channel into G and B */ + else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */ success |= TIFFReadScanline(image, fbuf, row, 0); } else { @@ -500,7 +500,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */ copy_vn_ushort(sbuf, ibuf->x, 65535); } - else if (chan >= spp) { /* for grayscale, duplicate first channel into G and B */ + else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */ success |= TIFFReadScanline(image, fbuf, row, 0); } else { @@ -553,15 +553,6 @@ void imb_inittiff(void) } } -/** - * Loads a TIFF file. - * \param mem: Memory containing the TIFF file. - * \param size: Size of the mem buffer. - * \param flags: If flags has IB_test set then the file is not actually loaded, - * but all other operations take place. - * - * \return A newly allocated #ImBuf structure if successful, otherwise NULL. - */ ImBuf *imb_loadtiff(const unsigned char *mem, size_t size, int flags, @@ -744,21 +735,6 @@ void imb_loadtiletiff( /** \name Save TIFF * \{ */ -/** - * Saves a TIFF file. - * - * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA - * respectively) are accepted, and interpreted correctly. Note that the TIFF - * convention is to use pre-multiplied alpha, which can be achieved within - * Blender by setting "Premul" alpha handling. Other alpha conventions are - * not strictly correct, but are permitted anyhow. - * - * \param ibuf: Image buffer. - * \param name: Name of the TIFF file to create. - * \param flags: Currently largely ignored. - * - * \return 1 if the function is successful, 0 on failure. - */ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) { TIFF *image = NULL; @@ -872,7 +848,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else if (samplesperpixel == 1) { - /* grayscale images, 1 channel */ + /* Gray-scale images, 1 channel */ TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } @@ -895,9 +871,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) copy_v3_v3(rgb, &fromf[from_i]); } else { - /* Standard linear-to-srgb conversion if float buffer - * wasn't managed. - */ + /* 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) { diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc new file mode 100644 index 00000000000..e1c451a8412 --- /dev/null +++ b/source/blender/imbuf/intern/transform.cc @@ -0,0 +1,604 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup imbuf + */ + +#include <array> +#include <type_traits> + +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +namespace blender::imbuf::transform { + +struct TransformUserData { + /** \brief Source image buffer to read from. */ + const ImBuf *src; + /** \brief Destination image buffer to write to. */ + ImBuf *dst; + /** \brief UV coordinates at the origin (0,0) in source image space. */ + float start_uv[2]; + + /** + * \brief delta UV coordinates along the source image buffer, when moving a single pixel in the X + * axis of the dst image buffer. + */ + float add_x[2]; + + /** + * \brief delta UV coordinate along the source image buffer, when moving a single pixel in the Y + * axes of the dst image buffer. + */ + float add_y[2]; + + /** + * \brief Cropping region in source image pixel space. + */ + rctf src_crop; + + /** + * \brief Initialize the start_uv, add_x and add_y fields based on the given transform matrix. + */ + void init(const float transform_matrix[4][4]) + { + init_start_uv(transform_matrix); + init_add_x(transform_matrix); + init_add_y(transform_matrix); + } + + private: + void init_start_uv(const float transform_matrix[4][4]) + { + float start_uv_v3[3]; + float orig[3]; + zero_v3(orig); + mul_v3_m4v3(start_uv_v3, transform_matrix, orig); + copy_v2_v2(start_uv, start_uv_v3); + } + + void init_add_x(const float transform_matrix[4][4]) + { + const int width = src->x; + float add_x_v3[3]; + float uv_max_x[3]; + zero_v3(uv_max_x); + uv_max_x[0] = width; + uv_max_x[1] = 0.0f; + mul_v3_m4v3(add_x_v3, transform_matrix, uv_max_x); + sub_v2_v2(add_x_v3, start_uv); + mul_v2_fl(add_x_v3, 1.0f / width); + copy_v2_v2(add_x, add_x_v3); + } + + void init_add_y(const float transform_matrix[4][4]) + { + const int height = src->y; + float add_y_v3[3]; + float uv_max_y[3]; + zero_v3(uv_max_y); + uv_max_y[0] = 0.0f; + uv_max_y[1] = height; + mul_v3_m4v3(add_y_v3, transform_matrix, uv_max_y); + sub_v2_v2(add_y_v3, start_uv); + mul_v2_fl(add_y_v3, 1.0f / height); + copy_v2_v2(add_y, add_y_v3); + } +}; + +/** + * \brief Base class for source discarding. + * + * The class decides if a specific uv coordinate from the source buffer should be ignored. + * This is used to mix multiple images over a single output buffer. Discarded pixels will + * not change the output buffer. + */ +class BaseDiscard { + public: + virtual ~BaseDiscard() = default; + + /** + * \brief Should the source pixel at the given uv coordinate be discarded. + */ + virtual bool should_discard(const TransformUserData &user_data, const float uv[2]) = 0; +}; + +/** + * \brief Crop uv-coordinates that are outside the user data src_crop rect. + */ +class CropSource : public BaseDiscard { + public: + /** + * \brief Should the source pixel at the given uv coordinate be discarded. + * + * Uses user_data.src_crop to determine if the uv coordinate should be skipped. + */ + bool should_discard(const TransformUserData &user_data, const float uv[2]) override + { + return uv[0] < user_data.src_crop.xmin || uv[0] >= user_data.src_crop.xmax || + uv[1] < user_data.src_crop.ymin || uv[1] >= user_data.src_crop.ymax; + } +}; + +/** + * \brief Discard that does not discard anything. + */ +class NoDiscard : public BaseDiscard { + public: + /** + * \brief Should the source pixel at the given uv coordinate be discarded. + * + * Will never discard any pixels. + */ + bool should_discard(const TransformUserData &UNUSED(user_data), + const float UNUSED(uv[2])) override + { + return false; + } +}; + +/** + * \brief Pointer to a pixel to write to in serial. + */ +template< + /** + * \brief Kind of buffer. + * Possible options: float, unsigned char. + */ + typename StorageType = float, + + /** + * \brief Number of channels of a single pixel. + */ + int NumChannels = 4> +class PixelPointer { + public: + static const int ChannelLen = NumChannels; + + private: + StorageType *pointer; + + public: + void init_pixel_pointer(const ImBuf *image_buffer, int x, int y) + { + const size_t offset = (y * (size_t)image_buffer->x + x) * NumChannels; + + if constexpr (std::is_same_v<StorageType, float>) { + pointer = image_buffer->rect_float + offset; + } + else if constexpr (std::is_same_v<StorageType, unsigned char>) { + pointer = const_cast<unsigned char *>( + static_cast<const unsigned char *>(static_cast<const void *>(image_buffer->rect)) + + offset); + } + else { + pointer = nullptr; + } + } + + /** + * \brief Get pointer to the current pixel to write to. + */ + StorageType *get_pointer() + { + return pointer; + } + + void increase_pixel_pointer() + { + pointer += NumChannels; + } +}; + +/** + * \brief Wrapping mode for the uv coordinates. + * + * Subclasses have the ability to change the UV coordinates when sampling the source buffer. + */ +class BaseUVWrapping { + public: + /** + * \brief modify the given u coordinate. + */ + virtual float modify_u(const ImBuf *source_buffer, float u) = 0; + + /** + * \brief modify the given v coordinate. + */ + virtual float modify_v(const ImBuf *source_buffer, float v) = 0; +}; + +/** + * \brief UVWrapping method that does not modify the UV coordinates. + */ +class PassThroughUV : public BaseUVWrapping { + public: + float modify_u(const ImBuf *UNUSED(source_buffer), float u) override + { + return u; + } + + float modify_v(const ImBuf *UNUSED(source_buffer), float v) override + { + return v; + } +}; + +/** + * \brief UVWrapping method that wrap repeats the UV coordinates. + */ +class WrapRepeatUV : public BaseUVWrapping { + public: + float modify_u(const ImBuf *source_buffer, float u) override + + { + int x = (int)floor(u); + x = x % source_buffer->x; + if (x < 0) { + x += source_buffer->x; + } + return x; + } + + float modify_v(const ImBuf *source_buffer, float v) override + { + int y = (int)floor(v); + y = y % source_buffer->y; + if (y < 0) { + y += source_buffer->y; + } + return y; + } +}; + +/** + * \brief Read a sample from an image buffer. + * + * A sampler can read from an image buffer. + * + */ +template< + /** \brief Interpolation mode to use when sampling. */ + eIMBInterpolationFilterMode Filter, + + /** \brief storage type of a single pixel channel (unsigned char or float). */ + typename StorageType, + /** + * \brief number of channels if the image to read. + * + * Must match the actual channels of the image buffer that is sampled. + */ + int NumChannels, + /** + * \brief Wrapping method to perform + * + * Should be a subclass of BaseUVWrapper + */ + typename UVWrapping> +class Sampler { + UVWrapping uv_wrapper; + + public: + using ChannelType = StorageType; + static const int ChannelLen = NumChannels; + using SampleType = std::array<StorageType, NumChannels>; + + void sample(const ImBuf *source, const float u, const float v, SampleType &r_sample) + { + if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float> && + NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); + bilinear_interpolation_color_fl(source, nullptr, &r_sample[0], wrapped_u, wrapped_v); + } + else if constexpr (Filter == IMB_FILTER_NEAREST && + std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); + nearest_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + } + else if constexpr (Filter == IMB_FILTER_BILINEAR && + std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); + bilinear_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + } + else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float>) { + if constexpr (std::is_same_v<UVWrapping, WrapRepeatUV>) { + BLI_bilinear_interpolation_wrap_fl( + source->rect_float, &r_sample[0], source->x, source->y, NumChannels, u, v, true, true); + } + else { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); + BLI_bilinear_interpolation_fl(source->rect_float, + &r_sample[0], + source->x, + source->y, + NumChannels, + wrapped_u, + wrapped_v); + } + } + else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, float>) { + const float wrapped_u = uv_wrapper.modify_u(source, u); + const float wrapped_v = uv_wrapper.modify_v(source, v); + sample_nearest_float(source, wrapped_u, wrapped_v, r_sample); + } + else { + /* Unsupported sampler. */ + BLI_assert_unreachable(); + } + } + + private: + void sample_nearest_float(const ImBuf *source, + const float u, + const float v, + SampleType &r_sample) + { + BLI_STATIC_ASSERT(std::is_same_v<StorageType, float>); + + /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ + int x1 = (int)(u); + int y1 = (int)(v); + + /* Break when sample outside image is requested. */ + if (x1 < 0 || x1 >= source->x || y1 < 0 || y1 >= source->y) { + for (int i = 0; i < NumChannels; i++) { + r_sample[i] = 0.0f; + } + return; + } + + const size_t offset = ((size_t)source->x * y1 + x1) * NumChannels; + const float *dataF = source->rect_float + offset; + for (int i = 0; i < NumChannels; i++) { + r_sample[i] = dataF[i]; + } + } +}; + +/** + * \brief Change the number of channels and store it. + * + * Template class to convert and store a sample in a PixelPointer. + * It supports: + * - 4 channel unsigned char -> 4 channel unsigned char. + * - 4 channel float -> 4 channel float. + * - 3 channel float -> 4 channel float. + * - 2 channel float -> 4 channel float. + * - 1 channel float -> 4 channel float. + */ +template<typename StorageType, int SourceNumChannels, int DestinationNumChannels> +class ChannelConverter { + public: + using SampleType = std::array<StorageType, SourceNumChannels>; + using PixelType = PixelPointer<StorageType, DestinationNumChannels>; + + /** + * \brief Convert the number of channels of the given sample to match the pixel pointer and store + * it at the location the pixel_pointer points at. + */ + void convert_and_store(const SampleType &sample, PixelType &pixel_pointer) + { + if constexpr (std::is_same_v<StorageType, unsigned char>) { + BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); + BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); + + copy_v4_v4_uchar(pixel_pointer.get_pointer(), &sample[0]); + } + else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 4 && + DestinationNumChannels == 4) { + copy_v4_v4(pixel_pointer.get_pointer(), &sample[0]); + } + else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 3 && + DestinationNumChannels == 4) { + copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[1], sample[2], 1.0f); + } + else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 2 && + DestinationNumChannels == 4) { + copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[1], 0.0f, 1.0f); + } + else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 1 && + DestinationNumChannels == 4) { + copy_v4_fl4(pixel_pointer.get_pointer(), sample[0], sample[0], sample[0], 1.0f); + } + else { + BLI_assert_unreachable(); + } + } +}; + +/** + * \brief Processor for a scanline. + */ +template< + /** + * \brief Discard function to use. + * + * \attention Should be a subclass of BaseDiscard. + */ + typename Discard, + + /** + * \brief Color interpolation function to read from the source buffer. + */ + typename Sampler, + + /** + * \brief Kernel to store to the destination buffer. + * Should be an PixelPointer + */ + typename OutputPixelPointer> +class ScanlineProcessor { + Discard discarder; + OutputPixelPointer output; + Sampler sampler; + + /** + * \brief Channels sizzling logic to convert between the input image buffer and the output image + * buffer. + */ + ChannelConverter<typename Sampler::ChannelType, + Sampler::ChannelLen, + OutputPixelPointer::ChannelLen> + channel_converter; + + public: + /** + * \brief Inner loop of the transformations, processing a full scanline. + */ + void process(const TransformUserData *user_data, int scanline) + { + const int width = user_data->dst->x; + + float uv[2]; + madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline); + + output.init_pixel_pointer(user_data->dst, 0, scanline); + for (int xi = 0; xi < width; xi++) { + if (!discarder.should_discard(*user_data, uv)) { + typename Sampler::SampleType sample; + sampler.sample(user_data->src, uv[0], uv[1], sample); + channel_converter.convert_and_store(sample, output); + } + + add_v2_v2(uv, user_data->add_x); + output.increase_pixel_pointer(); + } + } +}; + +/** + * \brief callback function for threaded transformation. + */ +template<typename Processor> void transform_scanline_function(void *custom_data, int scanline) +{ + const TransformUserData *user_data = static_cast<const TransformUserData *>(custom_data); + Processor processor; + processor.process(user_data, scanline); +} + +template<eIMBInterpolationFilterMode Filter, + typename StorageType, + int SourceNumChannels, + int DestinationNumChannels> +ScanlineThreadFunc get_scanline_function(const eIMBTransformMode mode) + +{ + switch (mode) { + case IMB_TRANSFORM_MODE_REGULAR: + return transform_scanline_function< + ScanlineProcessor<NoDiscard, + Sampler<Filter, StorageType, SourceNumChannels, PassThroughUV>, + PixelPointer<StorageType, DestinationNumChannels>>>; + case IMB_TRANSFORM_MODE_CROP_SRC: + return transform_scanline_function< + ScanlineProcessor<CropSource, + Sampler<Filter, StorageType, SourceNumChannels, PassThroughUV>, + PixelPointer<StorageType, DestinationNumChannels>>>; + case IMB_TRANSFORM_MODE_WRAP_REPEAT: + return transform_scanline_function< + ScanlineProcessor<NoDiscard, + Sampler<Filter, StorageType, SourceNumChannels, WrapRepeatUV>, + PixelPointer<StorageType, DestinationNumChannels>>>; + } + + BLI_assert_unreachable(); + return nullptr; +} + +template<eIMBInterpolationFilterMode Filter> +ScanlineThreadFunc get_scanline_function(const TransformUserData *user_data, + const eIMBTransformMode mode) +{ + const ImBuf *src = user_data->src; + const ImBuf *dst = user_data->dst; + + if (src->channels == 4 && dst->channels == 4) { + return get_scanline_function<Filter, float, 4, 4>(mode); + } + if (src->channels == 3 && dst->channels == 4) { + return get_scanline_function<Filter, float, 3, 4>(mode); + } + if (src->channels == 2 && dst->channels == 4) { + return get_scanline_function<Filter, float, 2, 4>(mode); + } + if (src->channels == 1 && dst->channels == 4) { + return get_scanline_function<Filter, float, 1, 4>(mode); + } + return nullptr; +} + +template<eIMBInterpolationFilterMode Filter> +static void transform_threaded(TransformUserData *user_data, const eIMBTransformMode mode) +{ + ScanlineThreadFunc scanline_func = nullptr; + + if (user_data->dst->rect_float && user_data->src->rect_float) { + scanline_func = get_scanline_function<Filter>(user_data, mode); + } + else if (user_data->dst->rect && user_data->src->rect) { + /* Number of channels is always 4 when using unsigned char buffers (sRGB + straight alpha). */ + scanline_func = get_scanline_function<Filter, unsigned char, 4, 4>(mode); + } + + if (scanline_func != nullptr) { + IMB_processor_apply_threaded_scanlines(user_data->dst->y, scanline_func, user_data); + } +} + +} // namespace blender::imbuf::transform + +extern "C" { + +using namespace blender::imbuf::transform; + +void IMB_transform(const struct ImBuf *src, + struct ImBuf *dst, + const eIMBTransformMode mode, + const eIMBInterpolationFilterMode filter, + const float transform_matrix[4][4], + const struct rctf *src_crop) +{ + BLI_assert_msg(mode != IMB_TRANSFORM_MODE_CROP_SRC || src_crop != nullptr, + "No source crop rect given, but crop source is requested. Or source crop rect " + "was given, but crop source was not requested."); + + TransformUserData user_data; + user_data.src = src; + user_data.dst = dst; + if (mode == IMB_TRANSFORM_MODE_CROP_SRC) { + user_data.src_crop = *src_crop; + } + user_data.init(transform_matrix); + + if (filter == IMB_FILTER_NEAREST) { + transform_threaded<IMB_FILTER_NEAREST>(&user_data, mode); + } + else { + transform_threaded<IMB_FILTER_BILINEAR>(&user_data, mode); + } +} +} diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 1bb047f1317..18ed4710e78 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -98,7 +98,7 @@ const char *imb_ext_movie[] = { ".mpg2", ".vob", ".mkv", ".flv", ".divx", ".xvid", ".mxf", ".webm", NULL, }; -/* sort of wrong being here... */ +/** Sort of wrong having audio extensions in imbuf. */ const char *imb_ext_audio[] = { ".wav", ".ogg", diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 2b99a0aa81d..cc6fef634d5 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -162,8 +162,6 @@ static void *imb_gpu_get_data(const ImBuf *ibuf, return data_rect; } -/* 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(). */ GPUTexture *IMB_touch_gpu_texture( const char *name, ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth) { @@ -183,9 +181,6 @@ GPUTexture *IMB_touch_gpu_texture( return tex; } -/* Will update a GPUTexture 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(GPUTexture *tex, ImBuf *ibuf, int x, |