Fix T97251: Store generated type information for each UDIM tile

Various situations can lead to un-saved UDIM tiles potentially losing
their contents. The most notable situation is a save and re-load of a
.blend file that has "generated" UDIM tiles that haven't been written to
disk yet. Normal "generated" images are reconstructed on demand in these
circumstances but UDIM tiles do not retain the information required for
reconstruction and empty tiles are presented to the user.

This patch stores the generated type information for each tile to solve
this particular issue. It also shifts the Image generation info into the
1st tile. The existing DNA fields are deprecated but RNA was modified as
to not break API compat.

There's two broad changes here that merit special callout:
- How to distinguish between a tile that should be reconstructed vs.
a tile that should remain empty because loading failed for the UDIMs
- How to better handle Image Source changes

The first issue is addressed as follows:
- Each time a tile is filled with generated content we set a new
IMA_GEN_TILE flag
- Each time a tile is saved to disk we remove the IMA_GEN_TILE flag
- When requesting an ibuf: If the ibuf is null, we check to see if
IMA_GEN_TILE is set. If it is set, go ahead and re-create the tile.
Otherwise, do nothing.

The second set of changes have to do with ensuring that information is
carried along as far as possible when the, sometimes destructive, act of
changing an Image Source is performed. Behavior should be a bit more
natural and expected now; though users will rarely, or should rarely, be
modifying this property. The full table describing the behavior is in
the differential.

Differential Revision: https://developer.blender.org/D14885
This commit is contained in:
Jesse Yurkovich 2022-08-03 22:00:52 -07:00
parent 646207c9af
commit 72ab6faf5d
Notes: blender-bot 2024-02-07 12:12:09 +01:00
Referenced by commit 4130cad489, Fix T101607: Changing Image source inadvertently clears file path
Referenced by issue #101607, Regression: Blender 3.4 can't find missing files
Referenced by issue #97251, Generated UDIM tiles do not store any info about their parameter
Referenced by pull request #117472, Fix #117411: In memory UDIMs fail to pack correctly
Referenced by commit 0c0885d323, Fix #117411: In memory UDIMs fail to pack correctly
Referenced by issue #117907, EEVEE: UDIM Bump/Height Texture makes UV seams visible
7 changed files with 312 additions and 112 deletions

View File

@ -364,14 +364,7 @@ bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile);
void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number);
void BKE_image_sort_tiles(struct Image *ima);
bool BKE_image_fill_tile(struct Image *ima,
struct ImageTile *tile,
int width,
int height,
const float color[4],
int gen_type,
int planes,
bool is_float);
bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile);
typedef enum {
UDIM_TILE_FORMAT_NONE = 0,

View File

@ -627,6 +627,16 @@ void BKE_image_free_data(Image *ima)
image_free_data(&ima->id);
}
static ImageTile *imagetile_alloc(int tile_number)
{
ImageTile *tile = MEM_cnew<ImageTile>("Image Tile");
tile->tile_number = tile_number;
tile->gen_x = 1024;
tile->gen_y = 1024;
tile->gen_type = IMA_GENTYPE_GRID;
return tile;
}
/* only image block itself */
static void image_init(Image *ima, short source, short type)
{
@ -641,8 +651,7 @@ static void image_init(Image *ima, short source, short type)
ima->flag |= IMA_VIEW_AS_RENDER;
}
ImageTile *tile = MEM_cnew<ImageTile>("Image Tiles");
tile->tile_number = 1001;
ImageTile *tile = imagetile_alloc(1001);
BLI_addtail(&ima->tiles, tile);
if (type == IMA_TYPE_R_RESULT) {
@ -1100,73 +1109,70 @@ static void image_buf_fill_isolated(void *usersata_v)
}
}
static ImBuf *add_ibuf_size(unsigned int width,
unsigned int height,
const char *name,
int depth,
int floatbuf,
short gen_type,
const float color[4],
ColorManagedColorspaceSettings *colorspace_settings)
static ImBuf *add_ibuf_for_tile(Image *ima, ImageTile *tile)
{
ImBuf *ibuf;
unsigned char *rect = nullptr;
float *rect_float = nullptr;
float fill_color[4];
const bool floatbuf = (tile->gen_flag & IMA_GEN_FLOAT) != 0;
if (floatbuf) {
ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat);
ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rectfloat);
if (colorspace_settings->name[0] == '\0') {
if (ima->colorspace_settings.name[0] == '\0') {
const char *colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_DEFAULT_FLOAT);
STRNCPY(colorspace_settings->name, colorspace);
STRNCPY(ima->colorspace_settings.name, colorspace);
}
if (ibuf != nullptr) {
rect_float = ibuf->rect_float;
IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name);
IMB_colormanagement_check_is_data(ibuf, ima->colorspace_settings.name);
}
if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) {
copy_v4_v4(fill_color, color);
if (IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
copy_v4_v4(fill_color, tile->gen_color);
}
else {
/* The input color here should ideally be linear already, but for now
* we just convert and postpone breaking the API for later. */
srgb_to_linearrgb_v4(fill_color, color);
srgb_to_linearrgb_v4(fill_color, tile->gen_color);
}
}
else {
ibuf = IMB_allocImBuf(width, height, depth, IB_rect);
ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rect);
if (colorspace_settings->name[0] == '\0') {
if (ima->colorspace_settings.name[0] == '\0') {
const char *colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_DEFAULT_BYTE);
STRNCPY(colorspace_settings->name, colorspace);
STRNCPY(ima->colorspace_settings.name, colorspace);
}
if (ibuf != nullptr) {
rect = (unsigned char *)ibuf->rect;
IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name);
IMB_colormanagement_assign_rect_colorspace(ibuf, ima->colorspace_settings.name);
}
copy_v4_v4(fill_color, color);
copy_v4_v4(fill_color, tile->gen_color);
}
if (!ibuf) {
return nullptr;
}
STRNCPY(ibuf->name, name);
STRNCPY(ibuf->name, ima->filepath);
/* Mark the tile itself as having been generated. */
tile->gen_flag |= IMA_GEN_TILE;
ImageFillData data;
data.gen_type = gen_type;
data.width = width;
data.height = height;
data.gen_type = tile->gen_type;
data.width = tile->gen_x;
data.height = tile->gen_y;
data.rect = rect;
data.rect_float = rect_float;
copy_v4_v4(data.fill_color, fill_color);
@ -1205,12 +1211,16 @@ Image *BKE_image_add_generated(Main *bmain,
/* NOTE: leave `ima->filepath` unset,
* setting it to a dummy value may write to an invalid file-path. */
ima->gen_x = width;
ima->gen_y = height;
ima->gen_type = gen_type;
ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0);
ima->gen_depth = depth;
copy_v4_v4(ima->gen_color, color);
/* The generation info is always stored in the tiles. The first tile
* will be used for non-tiled images. */
ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first);
tile->gen_x = width;
tile->gen_y = height;
tile->gen_type = gen_type;
tile->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0);
tile->gen_depth = depth;
copy_v4_v4(tile->gen_color, color);
if (is_data) {
STRNCPY(ima->colorspace_settings.name,
@ -1219,8 +1229,7 @@ Image *BKE_image_add_generated(Main *bmain,
for (view_id = 0; view_id < 2; view_id++) {
ImBuf *ibuf;
ibuf = add_ibuf_size(
width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
ibuf = add_ibuf_for_tile(ima, tile);
int index = tiled ? 0 : IMA_NO_INDEX;
int entry = tiled ? 1001 : 0;
image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry);
@ -3001,6 +3010,28 @@ static void image_free_tile(Image *ima, ImageTile *tile)
}
}
static bool image_remove_tile(Image *ima, ImageTile *tile)
{
if (BLI_listbase_is_single(&ima->tiles)) {
/* Can't remove the last remaining tile. */
return false;
}
image_free_tile(ima, tile);
BLI_remlink(&ima->tiles, tile);
MEM_freeN(tile);
return true;
}
static void image_remove_all_tiles(Image *ima)
{
/* Remove all but the final tile. */
while (image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) {
;
}
}
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
{
if (ima == nullptr) {
@ -3027,11 +3058,12 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
}
if (ima->source == IMA_SRC_GENERATED) {
if (ima->gen_x == 0 || ima->gen_y == 0) {
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
if (base_tile->gen_x == 0 || base_tile->gen_y == 0) {
ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr);
if (ibuf) {
ima->gen_x = ibuf->x;
ima->gen_y = ibuf->y;
base_tile->gen_x = ibuf->x;
base_tile->gen_y = ibuf->y;
IMB_freeImBuf(ibuf);
}
}
@ -3048,16 +3080,40 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
if (ima->source != IMA_SRC_TILED) {
/* Free all but the first tile. */
image_remove_all_tiles(ima);
/* If the remaining tile is generated, we need to again ensure that we
* wouldn't continue to use the old filepath.
*
* Otherwise, if this used to be a UDIM image, get the concrete filepath associated
* with the remaining tile and use that as the new filepath. */
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
BLI_assert(base_tile == ima->tiles.first);
for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) {
tile_next = tile->next;
image_free_tile(ima, tile);
MEM_freeN(tile);
if ((base_tile->gen_flag & IMA_GEN_TILE) != 0) {
ima->filepath[0] = '\0';
}
base_tile->next = nullptr;
else if (BKE_image_is_filename_tokenized(ima->filepath)) {
const bool was_relative = BLI_path_is_rel(ima->filepath);
eUDIM_TILE_FORMAT tile_format;
char *udim_pattern = BKE_image_get_tile_strformat(ima->filepath, &tile_format);
BKE_image_set_filepath_from_tile_number(
ima->filepath, udim_pattern, tile_format, base_tile->tile_number);
MEM_freeN(udim_pattern);
if (was_relative) {
const char *relbase = ID_BLEND_PATH(bmain, &ima->id);
BLI_path_rel(ima->filepath, relbase);
}
}
/* If the remaining tile was not number 1001, we need to reassign it so that
* ibuf lookups from the cache still succeed. */
base_tile->tile_number = 1001;
ima->tiles.last = base_tile;
}
else {
/* When changing to UDIM, attempt to tokenize the filepath. */
char *filename = (char *)BLI_path_basename(ima->filepath);
BKE_image_ensure_tile_token(filename);
}
/* image buffers for non-sequence multilayer will share buffers with RenderResult,
@ -3073,6 +3129,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
image_tag_frame_recalc(ima, nullptr, iuser, ima);
}
BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc);
BKE_image_partial_update_mark_full_update(ima);
break;
@ -3124,9 +3181,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
* to account for how the two sets might or might not overlap. To be complete, we start
* the refresh process by clearing all existing tiles, stopping when there's only 1 tile
* left. */
while (BKE_image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) {
;
}
image_remove_all_tiles(ima);
int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number;
bool needs_final_cleanup = true;
@ -3309,8 +3364,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
}
}
ImageTile *tile = MEM_cnew<ImageTile>("image new tile");
tile->tile_number = tile_number;
ImageTile *tile = imagetile_alloc(tile_number);
if (next_tile) {
BLI_insertlinkbefore(&ima->tiles, next_tile, tile);
@ -3345,16 +3399,7 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile)
return false;
}
if (BLI_listbase_is_single(&ima->tiles)) {
/* Can't remove the last remaining tile. */
return false;
}
image_free_tile(ima, tile);
BLI_remlink(&ima->tiles, tile);
MEM_freeN(tile);
return true;
return image_remove_tile(ima, tile);
}
void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number)
@ -3416,14 +3461,7 @@ void BKE_image_sort_tiles(struct Image *ima)
BLI_listbase_sort(&ima->tiles, tile_sort_cb);
}
bool BKE_image_fill_tile(struct Image *ima,
ImageTile *tile,
int width,
int height,
const float color[4],
int gen_type,
int planes,
bool is_float)
bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile)
{
if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) {
return false;
@ -3431,8 +3469,7 @@ bool BKE_image_fill_tile(struct Image *ima,
image_free_tile(ima, tile);
ImBuf *tile_ibuf = add_ibuf_size(
width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings);
ImBuf *tile_ibuf = add_ibuf_for_tile(ima, tile);
if (tile_ibuf != nullptr) {
image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number);
@ -4605,14 +4642,22 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
}
}
else if (ima->source == IMA_SRC_TILED) {
if (ima->type == IMA_TYPE_IMAGE) {
/* Regular files, ibufs in flip-book, allows saving */
ibuf = image_load_image_file(ima, iuser, entry, 0, false);
/* Nothing was cached. Check to see if the tile should be generated. */
ImageTile *tile = BKE_image_get_tile(ima, entry);
if ((tile->gen_flag & IMA_GEN_TILE) != 0) {
ibuf = add_ibuf_for_tile(ima, tile);
image_assign_ibuf(ima, ibuf, 0, entry);
}
/* no else; on load the ima type can change */
if (ima->type == IMA_TYPE_MULTILAYER) {
/* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */
ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0);
else {
if (ima->type == IMA_TYPE_IMAGE) {
/* Regular files, ibufs in flip-book, allows saving */
ibuf = image_load_image_file(ima, iuser, entry, 0, false);
}
/* no else; on load the ima type can change */
if (ima->type == IMA_TYPE_MULTILAYER) {
/* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */
ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0);
}
}
}
else if (ima->source == IMA_SRC_FILE) {
@ -4630,23 +4675,17 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
else if (ima->source == IMA_SRC_GENERATED) {
/* Generated is: `ibuf` is allocated dynamically. */
/* UV test-grid or black or solid etc. */
if (ima->gen_x == 0) {
ima->gen_x = 1024;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
if (base_tile->gen_x == 0) {
base_tile->gen_x = 1024;
}
if (ima->gen_y == 0) {
ima->gen_y = 1024;
if (base_tile->gen_y == 0) {
base_tile->gen_y = 1024;
}
if (ima->gen_depth == 0) {
ima->gen_depth = 24;
if (base_tile->gen_depth == 0) {
base_tile->gen_depth = 24;
}
ibuf = add_ibuf_size(ima->gen_x,
ima->gen_y,
ima->filepath,
ima->gen_depth,
(ima->gen_flag & IMA_GEN_FLOAT) != 0,
ima->gen_type,
ima->gen_color,
&ima->colorspace_settings);
ibuf = add_ibuf_for_tile(ima, base_tile);
image_assign_ibuf(ima, ibuf, index, 0);
}
else if (ima->source == IMA_SRC_VIEWER) {

View File

@ -320,6 +320,8 @@ static void image_save_post(ReportList *reports,
if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
ima->source = IMA_SRC_FILE;
ima->type = IMA_TYPE_IMAGE;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
base_tile->gen_flag &= ~IMA_GEN_TILE;
}
/* Update image file color space when saving to another color space. */
@ -666,8 +668,11 @@ bool BKE_image_save(
}
}
/* Set the image path only if all tiles were ok. */
/* Set the image path and clear the per-tile generated flag only if all tiles were ok. */
if (ok) {
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->gen_flag &= ~IMA_GEN_TILE;
}
image_save_update_filepath(ima, opts->filepath, opts);
}
MEM_freeN(udim_pattern);

View File

@ -3318,5 +3318,19 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
/* Image generation information transfered to tiles. */
if (!DNA_struct_elem_find(fd->filesdna, "ImageTile", "int", "gen_x")) {
for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
for (ImageTile *tile = ima->tiles.first; tile; tile = tile->next) {
tile->gen_x = ima->gen_x;
tile->gen_y = ima->gen_y;
tile->gen_type = ima->gen_type;
tile->gen_flag = ima->gen_flag;
tile->gen_depth = ima->gen_depth;
copy_v4_v4(tile->gen_color, ima->gen_color);
}
}
}
}
}

View File

@ -3847,15 +3847,16 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot)
static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile)
{
float color[4];
RNA_float_get_array(ptr, "color", color);
int gen_type = RNA_enum_get(ptr, "generated_type");
int width = RNA_int_get(ptr, "width");
int height = RNA_int_get(ptr, "height");
RNA_float_get_array(ptr, "color", tile->gen_color);
tile->gen_type = RNA_enum_get(ptr, "generated_type");
tile->gen_x = RNA_int_get(ptr, "width");
tile->gen_y = RNA_int_get(ptr, "height");
bool is_float = RNA_boolean_get(ptr, "float");
int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24;
return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float);
tile->gen_flag = is_float ? IMA_GEN_FLOAT : 0;
tile->gen_depth = RNA_boolean_get(ptr, "alpha") ? 32 : 24;
return BKE_image_fill_tile(ima, tile);
}
static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout)

View File

@ -92,8 +92,14 @@ typedef struct ImageTile {
struct ImageTile_Runtime runtime;
char _pad[4];
int tile_number;
/* for generated images */
int gen_x, gen_y;
char gen_type, gen_flag;
short gen_depth;
float gen_color[4];
char label[64];
} ImageTile;
@ -167,10 +173,10 @@ typedef struct Image {
int lastused;
/* for generated images */
int gen_x, gen_y;
char gen_type, gen_flag;
short gen_depth;
float gen_color[4];
int gen_x DNA_DEPRECATED, gen_y DNA_DEPRECATED;
char gen_type DNA_DEPRECATED, gen_flag DNA_DEPRECATED;
short gen_depth DNA_DEPRECATED;
float gen_color[4] DNA_DEPRECATED;
/* display aspect - for UV editing images resized for faster openGL display */
float aspx, aspy;
@ -262,7 +268,8 @@ enum {
/** #Image.gen_flag */
enum {
IMA_GEN_FLOAT = 1,
IMA_GEN_FLOAT = (1 << 0),
IMA_GEN_TILE = (1 << 1),
};
/** #Image.alpha_mode */

View File

@ -52,6 +52,7 @@ static const EnumPropertyItem image_source_items[] = {
#ifdef RNA_RUNTIME
# include "BLI_math_base.h"
# include "BLI_math_vector.h"
# include "BKE_global.h"
@ -85,6 +86,10 @@ static void rna_Image_source_set(PointerRNA *ptr, int value)
ima->source = value;
BLI_assert(BKE_id_is_in_global_main(&ima->id));
BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_SRC_CHANGE);
if (ima->source == IMA_SRC_TILED) {
BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_RELOAD);
}
DEG_id_tag_update(&ima->id, 0);
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS);
DEG_relations_tag_update(G_MAIN);
@ -100,6 +105,83 @@ static void rna_Image_reload_update(Main *bmain, Scene *UNUSED(scene), PointerRN
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS);
}
static int rna_Image_generated_type_get(PointerRNA *ptr)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
return base_tile->gen_type;
}
static void rna_Image_generated_type_set(PointerRNA *ptr, int value)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
base_tile->gen_type = value;
}
static int rna_Image_generated_width_get(PointerRNA *ptr)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
return base_tile->gen_x;
}
static void rna_Image_generated_width_set(PointerRNA *ptr, int value)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
base_tile->gen_x = CLAMPIS(value, 1, 65536);
}
static int rna_Image_generated_height_get(PointerRNA *ptr)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
return base_tile->gen_y;
}
static void rna_Image_generated_height_set(PointerRNA *ptr, int value)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
base_tile->gen_y = CLAMPIS(value, 1, 65536);
}
static bool rna_Image_generated_float_get(PointerRNA *ptr)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
return (base_tile->gen_flag & IMA_GEN_FLOAT) != 0;
}
static void rna_Image_generated_float_set(PointerRNA *ptr, bool value)
{
Image *ima = (Image *)ptr->data;
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
if (value) {
base_tile->gen_flag |= IMA_GEN_FLOAT;
}
else {
base_tile->gen_flag &= ~IMA_GEN_FLOAT;
}
}
void rna_Image_generated_color_get(PointerRNA *ptr, float values[4])
{
Image *ima = (Image *)(ptr->data);
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
copy_v4_v4(values, base_tile->gen_color);
}
void rna_Image_generated_color_set(PointerRNA *ptr, const float values[4])
{
Image *ima = (Image *)(ptr->data);
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
for (unsigned int i = 0; i < 4; i++) {
base_tile->gen_color[i] = CLAMPIS(values[i], 0.0f, FLT_MAX);
}
}
static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Image *ima = (Image *)ptr->owner_id;
@ -335,6 +417,18 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value)
}
}
static void rna_UDIMTile_generated_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Image *ima = (Image *)ptr->owner_id;
ImageTile *tile = (ImageTile *)ptr->data;
/* If the tile is still marked as generated, then update the tile as requested. */
if ((tile->gen_flag & IMA_GEN_TILE) != 0) {
BKE_image_fill_tile(ima, tile);
BKE_image_partial_update_mark_full_update(ima);
}
}
static int rna_Image_active_tile_index_get(PointerRNA *ptr)
{
Image *image = (Image *)ptr->data;
@ -896,6 +990,43 @@ static void rna_def_udim_tile(BlenderRNA *brna)
RNA_def_property_int_funcs(prop, "rna_UDIMTile_channels_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Channels", "Number of channels in the tile pixels buffer");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
/* Generated tile information. */
prop = RNA_def_property(srna, "generated_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "gen_type");
RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items);
RNA_def_property_ui_text(prop, "Generated Type", "Generated image type");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "generated_width", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "gen_x");
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Width", "Generated image width");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "generated_height", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "gen_y");
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Height", "Generated image height");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT);
RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "generated_color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "gen_color");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
}
static void rna_def_udim_tiles(BlenderRNA *brna, PropertyRNA *cprop)
@ -1079,6 +1210,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "gen_type");
RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items);
RNA_def_property_ui_text(prop, "Generated Type", "Generated image type");
RNA_def_property_enum_funcs(
prop, "rna_Image_generated_type_get", "rna_Image_generated_type_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@ -1087,6 +1220,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Width", "Generated image width");
RNA_def_property_int_funcs(
prop, "rna_Image_generated_width_get", "rna_Image_generated_width_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@ -1095,12 +1230,16 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Height", "Generated image height");
RNA_def_property_int_funcs(
prop, "rna_Image_generated_height_get", "rna_Image_generated_height_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT);
RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer");
RNA_def_property_boolean_funcs(
prop, "rna_Image_generated_float_get", "rna_Image_generated_float_set");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@ -1108,6 +1247,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "gen_color");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image");
RNA_def_property_float_funcs(
prop, "rna_Image_generated_color_get", "rna_Image_generated_color_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);