Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Stockner <lukas.stockner@freenet.de>2019-12-12 18:06:08 +0300
committerLukas Stockner <lukas.stockner@freenet.de>2019-12-12 20:40:37 +0300
commitc30d6571bb47734e0bcb2fced5cf11cb6d8b1169 (patch)
treef92f065147296e6c72e6cd85f26711157d81e09c /source/blender/editors/space_image
parentd7a8a606889fed58775c88bfdc079bee3c9333e2 (diff)
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender. With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser. Therefore, code that is not yet aware of tiles will just access the default tile as usual. The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9. Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator. The following features are supported so far: - Automatic detection and loading of all tiles when opening the first tile (1001) - Saving all tiles - Adding and removing tiles - Filling tiles with generated images - Drawing all tiles in the Image Editor - Viewing a tiled grid even if no image is selected - Rendering tiled images in Eevee - Rendering tiled images in Cycles (in SVM mode) - Automatically skipping loading of unused tiles in Cycles - 2D texture painting (also across tiles) - 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders) - Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID) - Different resolutions between tiles There still are some missing features that will be added later (see T72390): - Workbench engine support - Packing/Unpacking support - Baking support - Cycles OSL support - many other Blender features that rely on images Thanks to Brecht for the review and to all who tested the intermediate versions! Differential Revision: https://developer.blender.org/D3509
Diffstat (limited to 'source/blender/editors/space_image')
-rw-r--r--source/blender/editors/space_image/image_draw.c161
-rw-r--r--source/blender/editors/space_image/image_edit.c9
-rw-r--r--source/blender/editors/space_image/image_intern.h4
-rw-r--r--source/blender/editors/space_image/image_ops.c459
-rw-r--r--source/blender/editors/space_image/image_undo.c82
-rw-r--r--source/blender/editors/space_image/space_image.c10
6 files changed, 644 insertions, 81 deletions
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 2d4ca6dc15a..9a633427d82 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -37,6 +37,7 @@
#include "PIL_time.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_threads.h"
@@ -531,6 +532,36 @@ static void sima_draw_zbuffloat_pixels(Scene *scene,
MEM_freeN(rectf);
}
+static void draw_udim_label(ARegion *ar, float fx, float fy, const char *label)
+{
+ if (label == NULL || !label[0]) {
+ return;
+ }
+
+ /* find window pixel coordinates of origin */
+ int x, y;
+ UI_view2d_view_to_region(&ar->v2d, fx, fy, &x, &y);
+
+ GPU_blend_set_func_separate(
+ GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(true);
+
+ int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10;
+ float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur);
+ float opacity;
+ if (textwidth < 0.5f * (stepx - 10))
+ opacity = 1.0f;
+ else if (textwidth < (stepx - 10))
+ opacity = 2.0f - 2.0f * (textwidth / (stepx - 10));
+ else
+ opacity = 0.0f;
+ BLF_color4ub(blf_mono_font, 220, 220, 220, 150 * opacity);
+ BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0);
+ BLF_draw_ascii(blf_mono_font, label, strlen(label));
+
+ GPU_blend(false);
+}
+
static void draw_image_buffer(const bContext *C,
SpaceImage *sima,
ARegion *ar,
@@ -760,6 +791,83 @@ static void draw_image_paint_helpers(
}
}
+static void draw_udim_tile_grid(unsigned int pos_attr,
+ unsigned int color_attr,
+ ARegion *ar,
+ int x,
+ int y,
+ float stepx,
+ float stepy,
+ const float color[3])
+{
+ float x1, y1;
+ UI_view2d_view_to_region_fl(&ar->v2d, x, y, &x1, &y1);
+ int gridpos[5][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
+ for (int i = 0; i < 4; i++) {
+ immAttr3fv(color_attr, color);
+ immVertex2f(pos_attr, x1 + gridpos[i][0] * stepx, y1 + gridpos[i][1] * stepy);
+ immAttr3fv(color_attr, color);
+ immVertex2f(pos_attr, x1 + gridpos[i + 1][0] * stepx, y1 + gridpos[i + 1][1] * stepy);
+ }
+}
+
+static void draw_udim_tile_grids(ARegion *ar, SpaceImage *sima, Image *ima)
+{
+ int num_tiles;
+ if (ima != NULL) {
+ num_tiles = BLI_listbase_count(&ima->tiles);
+
+ if (ima->source != IMA_SRC_TILED) {
+ return;
+ }
+ }
+ else {
+ num_tiles = sima->tile_grid_shape[0] * sima->tile_grid_shape[1];
+ }
+
+ float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur);
+ float stepy = BLI_rcti_size_y(&ar->v2d.mask) / BLI_rctf_size_y(&ar->v2d.cur);
+
+ GPUVertFormat *format = immVertexFormat();
+ unsigned int pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ unsigned color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(GPU_PRIM_LINES, 8 * num_tiles);
+
+ float theme_color[3], selected_color[3];
+ UI_GetThemeColorShade3fv(TH_BACK, 60.0f, theme_color);
+ UI_GetThemeColor3fv(TH_FACE_SELECT, selected_color);
+
+ if (ima != NULL) {
+ ImageTile *cur_tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
+
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ if (tile != cur_tile) {
+ int x = (tile->tile_number - 1001) % 10;
+ int y = (tile->tile_number - 1001) / 10;
+ draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color);
+ }
+ }
+
+ if (cur_tile != NULL) {
+ int cur_x = (cur_tile->tile_number - 1001) % 10;
+ int cur_y = (cur_tile->tile_number - 1001) / 10;
+ draw_udim_tile_grid(pos, color, ar, cur_x, cur_y, stepx, stepy, selected_color);
+ }
+ }
+ else {
+ for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
+ for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
+ draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color);
+ }
+ }
+ }
+
+ immEnd();
+ immUnbindProgram();
+}
+
/* draw main image region */
void draw_image_main(const bContext *C, ARegion *ar)
@@ -827,18 +935,43 @@ void draw_image_main(const bContext *C, ARegion *ar)
}
}
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
+
+ int main_w = 0;
+ int main_h = 0;
/* draw the image or grid */
if (ibuf == NULL) {
- ED_region_grid_draw(ar, zoomx, zoomy);
+ if (ima != NULL) {
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ int x = (tile->tile_number - 1001) % 10;
+ int y = (tile->tile_number - 1001) / 10;
+ ED_region_grid_draw(ar, zoomx, zoomy, x, y);
+ }
+ }
+ else {
+ for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
+ for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
+ ED_region_grid_draw(ar, zoomx, zoomy, x, y);
+ }
+ }
+ }
}
else {
if (sima->flag & SI_DRAW_TILE) {
draw_image_buffer_repeated(C, sima, ar, scene, ibuf, zoomx, zoomy);
}
else {
+ main_w = ibuf->x;
+ main_h = ibuf->y;
+
draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy);
+ if (ima->source == IMA_SRC_TILED) {
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ char label[sizeof(tile->label)];
+ BKE_image_get_tile_label(ima, tile, label, sizeof(label));
+ draw_udim_label(ar, 0.0f, 0.0f, label);
+ }
}
if (sima->flag & SI_DRAW_METADATA) {
@@ -854,6 +987,30 @@ void draw_image_main(const bContext *C, ARegion *ar)
ED_space_image_release_buffer(sima, ibuf, lock);
+ if (ima != NULL && ima->source == IMA_SRC_TILED) {
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ if (tile->tile_number == 1001) {
+ continue;
+ }
+
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, tile->tile_number);
+ if (ibuf != NULL) {
+ int x_pos = (tile->tile_number - 1001) % 10;
+ int y_pos = (tile->tile_number - 1001) / 10;
+ char label[sizeof(tile->label)];
+ BKE_image_get_tile_label(ima, tile, label, sizeof(label));
+
+ float tile_zoomx = (zoomx * main_w) / ibuf->x;
+ float tile_zoomy = (zoomy * main_h) / ibuf->y;
+ draw_image_buffer(C, sima, ar, scene, ibuf, x_pos, y_pos, tile_zoomx, tile_zoomy);
+ draw_udim_label(ar, x_pos, y_pos, label);
+ }
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ }
+
+ draw_udim_tile_grids(ar, sima, ima);
+
/* paint helpers */
if (show_paint) {
draw_image_paint_helpers(C, ar, scene, zoomx, zoomy);
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index ec2b1cc7fbe..c1ed049130e 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -136,7 +136,7 @@ void ED_space_image_set_mask(bContext *C, SpaceImage *sima, Mask *mask)
}
}
-ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock)
+ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock, int tile)
{
ImBuf *ibuf;
@@ -148,7 +148,9 @@ ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock)
else
#endif
{
+ sima->iuser.tile = tile;
ibuf = BKE_image_acquire_ibuf(sima->image, &sima->iuser, r_lock);
+ sima->iuser.tile = 0;
}
if (ibuf) {
@@ -179,7 +181,7 @@ bool ED_space_image_has_buffer(SpaceImage *sima)
void *lock;
bool has_buffer;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
has_buffer = (ibuf != NULL);
ED_space_image_release_buffer(sima, ibuf, lock);
@@ -192,7 +194,8 @@ void ED_space_image_get_size(SpaceImage *sima, int *width, int *height)
ImBuf *ibuf;
void *lock;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ /* TODO(lukas): Support tiled images with different sizes */
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
if (ibuf && ibuf->x > 0 && ibuf->y > 0) {
*width = ibuf->x;
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index f8ce065d46c..f3ec68db562 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -89,6 +89,10 @@ void IMAGE_OT_read_viewlayers(struct wmOperatorType *ot);
void IMAGE_OT_render_border(struct wmOperatorType *ot);
void IMAGE_OT_clear_render_border(struct wmOperatorType *ot);
+void IMAGE_OT_tile_add(struct wmOperatorType *ot);
+void IMAGE_OT_tile_remove(struct wmOperatorType *ot);
+void IMAGE_OT_tile_fill(struct wmOperatorType *ot);
+
/* image_panels.c */
struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree);
void image_buttons_register(struct ARegionType *art);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 8d17b703449..4404f904891 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -35,7 +35,10 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_fileops.h"
+#include "BLI_fileops_types.h"
#include "BLI_ghash.h"
+#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -1264,6 +1267,51 @@ static int image_cmp_frame(const void *a, const void *b)
return 0;
}
+static int image_get_udim(const char *filepath, LinkNodePair *udim_tiles)
+{
+ char filename[FILE_MAX], dirname[FILE_MAXDIR];
+ BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename));
+
+ if (strstr(filename, "1001") == NULL) {
+ return 0;
+ }
+
+ bool is_udim = true;
+ int max_udim = 0;
+
+ unsigned short digits;
+ char base_head[FILE_MAX], base_tail[FILE_MAX];
+ int id = BLI_stringdec(filename, base_head, base_tail, &digits);
+ if (id == 1001) {
+ struct direntry *dir;
+ uint totfile = BLI_filelist_dir_contents(dirname, &dir);
+ for (int i = 0; i < totfile; i++) {
+ if (!(dir[i].type & S_IFREG)) {
+ continue;
+ }
+ char head[FILE_MAX], tail[FILE_MAX];
+ id = BLI_stringdec(dir[i].relname, head, tail, &digits);
+
+ if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) ||
+ !(STREQLEN(base_tail, tail, FILE_MAX))) {
+ continue;
+ }
+
+ if (id < 1001 || id >= 2000) {
+ is_udim = false;
+ break;
+ }
+
+ BLI_linklist_append(udim_tiles, POINTER_FROM_INT(id));
+ max_udim = max_ii(max_udim, id);
+ }
+
+ BLI_filelist_free(dir, totfile);
+ }
+
+ return is_udim ? (max_udim - 1001) : 0;
+}
+
/**
* Return the start (offset) and the length of the sequence of
* continuous frames in the list of frames.
@@ -1272,21 +1320,27 @@ static int image_cmp_frame(const void *a, const void *b)
* \param ofs: [out] offset the first frame number in the sequence.
* \return the number of contiguous frames in the sequence
*/
-static int image_sequence_get_len(ListBase *frames, int *ofs)
+static int image_sequence_get_len(ImageFrameRange *frame_range, int *ofs, LinkNodePair *udim_tiles)
{
ImageFrame *frame;
- BLI_listbase_sort(frames, image_cmp_frame);
+ BLI_listbase_sort(&frame_range->frames, image_cmp_frame);
- frame = frames->first;
- if (frame) {
+ frame = frame_range->frames.first;
+ if (frame != NULL) {
int frame_curr = frame->framenr;
(*ofs) = frame_curr;
- while (frame && (frame->framenr == frame_curr)) {
- frame_curr++;
- frame = frame->next;
+
+ if (udim_tiles != NULL && (frame_curr == 1001)) {
+ return 1 + image_get_udim(frame_range->filepath, udim_tiles);
+ }
+ else {
+ while (frame != NULL && (frame->framenr == frame_curr)) {
+ frame_curr++;
+ frame = frame->next;
+ }
+ return frame_curr - (*ofs);
}
- return frame_curr - (*ofs);
}
*ofs = 0;
return 0;
@@ -1298,7 +1352,9 @@ static Image *image_open_single(Main *bmain,
const char *relbase,
bool is_relative_path,
bool use_multiview,
- int frame_seq_len)
+ int frame_seq_len,
+ int frame_seq_ofs,
+ LinkNodePair *udim_tiles)
{
bool exists = false;
Image *ima = NULL;
@@ -1339,7 +1395,15 @@ static Image *image_open_single(Main *bmain,
}
if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) {
- ima->source = IMA_SRC_SEQUENCE;
+ if (udim_tiles && frame_seq_ofs == 1001) {
+ ima->source = IMA_SRC_TILED;
+ for (LinkNode *node = udim_tiles->list; node; node = node->next) {
+ BKE_image_add_tile(ima, POINTER_AS_INT(node->link), NULL);
+ }
+ }
+ else {
+ ima->source = IMA_SRC_SEQUENCE;
+ }
}
}
@@ -1358,9 +1422,11 @@ static int image_open_exec(bContext *C, wmOperator *op)
char filepath[FILE_MAX];
int frame_seq_len = 0;
int frame_ofs = 1;
+ LinkNodePair udim_tiles = {NULL};
const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
+ const bool use_udim = RNA_boolean_get(op->ptr, "use_udim_detecting");
if (!op->customdata) {
image_open_init(C, op);
@@ -1378,7 +1444,10 @@ static int image_open_exec(bContext *C, wmOperator *op)
for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range;
frame_range = frame_range->next) {
int frame_range_ofs;
- int frame_range_seq_len = image_sequence_get_len(&frame_range->frames, &frame_range_ofs);
+
+ LinkNodePair *udim_tiles_ptr = use_udim ? (&udim_tiles) : NULL;
+ int frame_range_seq_len = image_sequence_get_len(
+ frame_range, &frame_range_ofs, udim_tiles_ptr);
BLI_freelistN(&frame_range->frames);
char filepath_range[FILE_MAX];
@@ -1394,7 +1463,9 @@ static int image_open_exec(bContext *C, wmOperator *op)
BKE_main_blendfile_path(bmain),
is_relative_path,
use_multiview,
- frame_range_seq_len);
+ frame_range_seq_len,
+ frame_range_ofs,
+ udim_tiles_ptr);
/* take the first image */
if ((ima == NULL) && ima_range) {
@@ -1407,10 +1478,31 @@ static int image_open_exec(bContext *C, wmOperator *op)
}
else {
/* for drag & drop etc. */
- ima = image_open_single(
- bmain, op, filepath, BKE_main_blendfile_path(bmain), is_relative_path, use_multiview, 1);
+ frame_seq_len = 1;
+
+ if (use_udim) {
+ /* Try to find UDIM tiles corresponding to the image */
+ frame_seq_len = 1 + image_get_udim(filepath, &udim_tiles);
+
+ /* If we found something, mark the image as tiled. */
+ if (frame_seq_len > 1) {
+ frame_ofs = 1001;
+ }
+ }
+
+ ima = image_open_single(bmain,
+ op,
+ filepath,
+ BKE_main_blendfile_path(bmain),
+ is_relative_path,
+ use_multiview,
+ frame_seq_len,
+ frame_ofs,
+ &udim_tiles);
}
+ BLI_linklist_free(udim_tiles.list, NULL);
+
if (ima == NULL) {
return OPERATOR_CANCELLED;
}
@@ -1458,7 +1550,8 @@ static int image_open_exec(bContext *C, wmOperator *op)
/* initialize because of new image */
if (iuser) {
- iuser->frames = frame_seq_len;
+ /* If the sequence was a tiled image, we only have one frame. */
+ iuser->frames = (ima->source == IMA_SRC_SEQUENCE) ? frame_seq_len : 1;
iuser->sfra = 1;
iuser->framenr = 1;
if (ima->source == IMA_SRC_MOVIE) {
@@ -1604,6 +1697,11 @@ void IMAGE_OT_open(wmOperatorType *ot)
true,
"Detect Sequences",
"Automatically detect animated sequences in selected images (based on file names)");
+ RNA_def_boolean(ot->srna,
+ "use_udim_detecting",
+ true,
+ "Detect UDIMs",
+ "Detect selected UDIM files and load all matching tiles");
}
/** \} */
@@ -1870,6 +1968,12 @@ static int image_save_options_init(Main *bmain,
BLI_path_make_safe(opts->filepath);
BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
}
+
+ /* append UDIM numbering if not present */
+ if (ima->source == IMA_SRC_TILED && (BLI_stringdec(ima->name, NULL, NULL, NULL) != 1001)) {
+ int len = strlen(opts->filepath);
+ STR_CONCAT(opts->filepath, len, ".1001");
+ }
}
/* color management */
@@ -2608,13 +2712,23 @@ static int image_new_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "color", color);
alpha = RNA_boolean_get(op->ptr, "alpha");
stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d");
+ bool tiled = RNA_boolean_get(op->ptr, "tiled");
if (!alpha) {
color[3] = 1.0f;
}
- ima = BKE_image_add_generated(
- bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d, false);
+ ima = BKE_image_add_generated(bmain,
+ width,
+ height,
+ name,
+ alpha ? 32 : 24,
+ floatbuf,
+ gen_type,
+ color,
+ stereo3d,
+ false,
+ tiled);
if (!ima) {
image_new_free(op);
@@ -2698,6 +2812,9 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
uiItemL(col[0], "", ICON_NONE);
uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE);
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "tiled", 0, NULL, ICON_NONE);
+
#if 0
if (is_multiview) {
uiItemL(col[0], "", ICON_NONE);
@@ -2753,6 +2870,8 @@ void IMAGE_OT_new(wmOperatorType *ot)
prop = RNA_def_boolean(
ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views");
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "tiled", 0, "Tiled", "Create a tiled image");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
#undef IMA_DEF_NAME
@@ -2783,7 +2902,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf);
+ ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, 0);
if (is_paint) {
ED_imapaint_clear_partial_redraw();
@@ -2927,7 +3046,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
RNA_property_int_set_array(op->ptr, prop, size);
}
- ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf);
+ ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, 0);
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
IMB_scaleImBuf(ibuf, size[0], size[1]);
@@ -2977,7 +3096,7 @@ static bool image_pack_test(bContext *C, wmOperator *op)
return 0;
}
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported");
return 0;
}
@@ -3045,7 +3164,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -3078,7 +3197,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
return OPERATOR_CANCELLED;
}
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -3210,9 +3329,12 @@ static void image_sample_draw(const bContext *C, ARegion *ar, void *arg_info)
/* Returns color in linear space, matching ED_space_node_color_sample(). */
bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], float r_col[3])
{
+ float uv[2];
+ UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &uv[0], &uv[1]);
+ int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
- float fx, fy;
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
bool ret = false;
if (ibuf == NULL) {
@@ -3220,12 +3342,10 @@ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], flo
return false;
}
- UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &fx, &fy);
-
- if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
const float *fp;
unsigned char *cp;
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
+ int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -3326,10 +3446,15 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event
{
SpaceImage *sima = CTX_wm_space_image(C);
ARegion *ar = CTX_wm_region(C);
+ Image *image = ED_space_image(sima);
+
+ float uv[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]);
+ int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
ImageSampleInfo *info = op->customdata;
- float fx, fy;
Scene *scene = CTX_data_scene(C);
CurveMapping *curve_mapping = scene->view_settings.curve_mapping;
@@ -3339,11 +3464,8 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event
return;
}
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fx, &fy);
-
- if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
- Image *image = ED_space_image(sima);
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
+ int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -3551,18 +3673,25 @@ static int image_sample_line_exec(bContext *C, wmOperator *op)
SpaceImage *sima = CTX_wm_space_image(C);
ARegion *ar = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
+ Image *ima = ED_space_image(sima);
int x_start = RNA_int_get(op->ptr, "xstart");
int y_start = RNA_int_get(op->ptr, "ystart");
int x_end = RNA_int_get(op->ptr, "xend");
int y_end = RNA_int_get(op->ptr, "yend");
+ float uv1[2], uv2[2], ofs[2];
+ UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &uv1[0], &uv1[1]);
+ UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &uv2[0], &uv2[1]);
+
+ /* If the image has tiles, shift the positions accordingly. */
+ int tile = BKE_image_get_tile_from_pos(ima, uv1, uv1, ofs);
+ sub_v2_v2(uv2, ofs);
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
Histogram *hist = &sima->sample_line_hist;
- float x1f, y1f, x2f, y2f;
-
if (ibuf == NULL) {
ED_space_image_release_buffer(sima, ibuf, lock);
return OPERATOR_CANCELLED;
@@ -3573,13 +3702,8 @@ static int image_sample_line_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f);
- UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f);
-
- hist->co[0][0] = x1f;
- hist->co[0][1] = y1f;
- hist->co[1][0] = x2f;
- hist->co[1][1] = y2f;
+ copy_v2_v2(hist->co[0], uv1);
+ copy_v2_v2(hist->co[1], uv2);
/* enable line drawing */
hist->flag |= HISTO_FLAG_SAMPLELINE;
@@ -4085,3 +4209,248 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot)
}
/** \} */
+
+/* ********************* Add tile operator ****************** */
+
+static bool tile_poll(bContext *C)
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ return (ima != NULL && ima->source == IMA_SRC_TILED);
+}
+
+static int tile_add_exec(bContext *C, wmOperator *op)
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ int tile_number = RNA_int_get(op->ptr, "number");
+
+ char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0);
+
+ ImageTile *tile = BKE_image_add_tile(ima, tile_number, label);
+ MEM_freeN(label);
+
+ if (tile == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ima->active_tile_index = BLI_findindex(&ima->tiles, tile);
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ /* Find the first gap in tile numbers or the number after the last if
+ * no gap exists. */
+ int next_number = 0;
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ next_number = tile->tile_number + 1;
+ if (tile->next == NULL || tile->next->tile_number > next_number) {
+ break;
+ }
+ }
+
+ RNA_int_set(op->ptr, "number", next_number);
+ RNA_string_set(op->ptr, "label", "");
+
+ return WM_operator_props_dialog_popup(C, op, 5 * UI_UNIT_X, 5 * UI_UNIT_Y);
+}
+
+static void tile_add_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *split, *col[2];
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ split = uiLayoutSplit(layout, 0.5f, false);
+ col[0] = uiLayoutColumn(split, false);
+ col[1] = uiLayoutColumn(split, false);
+
+ uiItemL(col[0], IFACE_("Number"), ICON_NONE);
+ uiItemR(col[1], &ptr, "number", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Label"), ICON_NONE);
+ uiItemR(col[1], &ptr, "label", 0, "", ICON_NONE);
+}
+
+void IMAGE_OT_tile_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add tile";
+ ot->description = "Adds a tile to the image";
+ ot->idname = "IMAGE_OT_tile_add";
+
+ /* api callbacks */
+ ot->poll = tile_poll;
+ ot->exec = tile_add_exec;
+ ot->invoke = tile_add_invoke;
+ ot->ui = tile_add_draw;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(
+ ot->srna, "number", 1002, 1001, INT_MAX, "Number", "UDIM number of the tile", 1001, 1099);
+ RNA_def_string(ot->srna, "label", NULL, 0, "Label", "Optional tile label");
+}
+
+/* ********************* Remove tile operator ****************** */
+
+static bool tile_remove_poll(bContext *C)
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ return (ima != NULL && ima->source == IMA_SRC_TILED && ima->active_tile_index != 0);
+}
+
+static int tile_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
+ if (!BKE_image_remove_tile(ima, tile)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Ensure that the active index is valid. */
+ ima->active_tile_index = min_ii(ima->active_tile_index, BLI_listbase_count(&ima->tiles) - 1);
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_tile_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove tile";
+ ot->description = "Removes a tile from the image";
+ ot->idname = "IMAGE_OT_tile_remove";
+
+ /* api callbacks */
+ ot->poll = tile_remove_poll;
+ ot->exec = tile_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************* Fill tile operator ****************** */
+
+static int tile_fill_exec(bContext *C, wmOperator *op)
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ float color[4];
+ RNA_float_get_array(op->ptr, "color", color);
+ int gen_type = RNA_enum_get(op->ptr, "generated_type");
+ int width = RNA_int_get(op->ptr, "width");
+ int height = RNA_int_get(op->ptr, "height");
+ bool is_float = RNA_boolean_get(op->ptr, "float");
+ int planes = RNA_boolean_get(op->ptr, "alpha") ? 32 : 24;
+
+ ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
+ if (!BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Image *ima = CTX_data_edit_image(C);
+
+ /* Acquire first tile to get the defaults. */
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ if (ibuf != NULL) {
+ RNA_int_set(op->ptr, "width", ibuf->x);
+ RNA_int_set(op->ptr, "height", ibuf->y);
+ RNA_boolean_set(op->ptr, "float", ibuf->rect_float != NULL);
+ RNA_boolean_set(op->ptr, "alpha", ibuf->planes > 24);
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+
+ return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y);
+}
+
+static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *split, *col[2];
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ /* copy of WM_operator_props_dialog_popup() layout */
+
+ split = uiLayoutSplit(layout, 0.5f, false);
+ col[0] = uiLayoutColumn(split, false);
+ col[1] = uiLayoutColumn(split, false);
+
+ uiItemL(col[0], IFACE_("Color"), ICON_NONE);
+ uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Width"), ICON_NONE);
+ uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Height"), ICON_NONE);
+ uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE);
+
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE);
+ uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE);
+
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE);
+}
+
+void IMAGE_OT_tile_fill(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Fill tile";
+ ot->description = "Fill the current tile with a generated image";
+ ot->idname = "IMAGE_OT_tile_fill";
+
+ /* api callbacks */
+ ot->poll = tile_poll;
+ ot->exec = tile_fill_exec;
+ ot->invoke = tile_fill_invoke;
+ ot->ui = tile_fill_draw;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ PropertyRNA *prop;
+ static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ prop = RNA_def_float_color(
+ ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
+ RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
+ RNA_def_property_float_array_default(prop, default_color);
+ RNA_def_enum(ot->srna,
+ "generated_type",
+ rna_enum_image_generated_type_items,
+ IMA_GENTYPE_BLANK,
+ "Generated Type",
+ "Fill the image with a grid for UV map testing");
+ prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+ prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+
+ /* Only needed when filling the first tile. */
+ RNA_def_boolean(
+ ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth");
+ RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel");
+}
diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c
index b6b32293cee..79aa4d2ed7f 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.c
@@ -107,6 +107,7 @@ typedef struct PaintTile {
struct PaintTile *next, *prev;
Image *image;
ImBuf *ibuf;
+ int tile_number;
union {
float *fp;
uint *uint;
@@ -148,6 +149,7 @@ static void ptile_invalidate_list(ListBase *paint_tiles)
void *ED_image_paint_tile_find(ListBase *paint_tiles,
Image *image,
ImBuf *ibuf,
+ int tile_number,
int x_tile,
int y_tile,
ushort **r_mask,
@@ -155,7 +157,7 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles,
{
for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) {
if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) {
- if (ptile->image == image && ptile->ibuf == ibuf) {
+ if (ptile->image == image && ptile->ibuf == ibuf && ptile->tile_number == tile_number) {
if (r_mask) {
/* allocate mask if requested. */
if (!ptile->mask) {
@@ -178,6 +180,7 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
Image *image,
ImBuf *ibuf,
ImBuf **tmpibuf,
+ int tile_number,
int x_tile,
int y_tile,
ushort **r_mask,
@@ -191,7 +194,8 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
if (find_prev) {
- void *data = ED_image_paint_tile_find(paint_tiles, image, ibuf, x_tile, y_tile, r_mask, true);
+ void *data = ED_image_paint_tile_find(
+ paint_tiles, image, ibuf, tile_number, x_tile, y_tile, r_mask, true);
if (data) {
return data;
}
@@ -205,6 +209,7 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
ptile->image = image;
ptile->ibuf = ibuf;
+ ptile->tile_number = tile_number;
ptile->x_tile = x_tile;
ptile->y_tile = y_tile;
@@ -259,7 +264,10 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) {
Image *image = ptile->image;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL);
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
+ iuser.tile = ptile->tile_number;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, NULL);
const bool has_float = (ibuf->rect_float != NULL);
if (has_float) {
@@ -460,6 +468,8 @@ static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf)
}
}
+ BLI_assert(i == ubuf->tiles_len);
+
IMB_freeImBuf(tmpibuf);
}
@@ -514,13 +524,13 @@ typedef struct UndoImageHandle {
/** Each undo handle refers to a single image which may have multiple buffers. */
UndoRefID_Image image_ref;
+ /** Each tile of a tiled image has its own UndoImageHandle.
+ * The tile number of this IUser is used to distinguish them.
+ */
+ ImageUser iuser;
+
/**
* List of #UndoImageBuf's to support multiple buffers per image.
- *
- * \note To properly support multiple buffers per image
- * we would need to store an #ImageUser for each #UndoImageBuf.
- * since when restoring the image we use:
- * `BKE_image_acquire_ibuf(image, NULL, NULL)`.
*/
ListBase buffers;
@@ -533,7 +543,8 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) {
/* Tiles only added to second set of tiles. */
Image *image = uh->image_ref.ptr;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL);
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, NULL);
if (UNLIKELY(ibuf == NULL)) {
CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2);
continue;
@@ -626,40 +637,44 @@ static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBu
return ubuf;
}
-static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles, const Image *image)
+static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles,
+ const Image *image,
+ int tile_number)
{
for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) {
- if (STREQ(image->id.name + 2, uh->image_ref.name + 2)) {
+ if (STREQ(image->id.name + 2, uh->image_ref.name + 2) && uh->iuser.tile == tile_number) {
return uh;
}
}
return NULL;
}
-static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image)
+static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image, int tile_number)
{
for (UndoImageHandle *uh = undo_handles->first; uh; uh = uh->next) {
- if (image == uh->image_ref.ptr) {
+ if (image == uh->image_ref.ptr && uh->iuser.tile == tile_number) {
return uh;
}
}
return NULL;
}
-static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image)
+static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, int tile_number)
{
- BLI_assert(uhandle_lookup(undo_handles, image) == NULL);
+ BLI_assert(uhandle_lookup(undo_handles, image, tile_number) == NULL);
UndoImageHandle *uh = MEM_callocN(sizeof(*uh), __func__);
uh->image_ref.ptr = image;
+ uh->iuser.ok = 1;
+ uh->iuser.tile = tile_number;
BLI_addtail(undo_handles, uh);
return uh;
}
-static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image)
+static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, int tile_number)
{
- UndoImageHandle *uh = uhandle_lookup(undo_handles, image);
+ UndoImageHandle *uh = uhandle_lookup(undo_handles, image, tile_number);
if (uh == NULL) {
- uh = uhandle_add(undo_handles, image);
+ uh = uhandle_add(undo_handles, image, tile_number);
}
return uh;
}
@@ -693,10 +708,11 @@ typedef struct ImageUndoStep {
*/
static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
const Image *image,
+ int tile_number,
const UndoImageBuf *ubuf)
{
/* Use name lookup because because the pointer is cleared for previous steps. */
- UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image);
+ UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number);
if (uh_prev != NULL) {
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_name);
if (ubuf_reference) {
@@ -763,7 +779,7 @@ static bool image_undosys_step_encode(struct bContext *C,
/* Initialize undo tiles from ptiles (if they exist). */
for (PaintTile *ptile = us->paint_tiles.first, *ptile_next; ptile; ptile = ptile_next) {
if (ptile->valid) {
- UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image);
+ UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image, ptile->tile_number);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf);
UndoImageTile *utile = MEM_callocN(sizeof(*utile), "UndoImageTile");
@@ -783,7 +799,7 @@ static bool image_undosys_step_encode(struct bContext *C,
for (UndoImageHandle *uh = us->handles.first; uh; uh = uh->next) {
for (UndoImageBuf *ubuf_pre = uh->buffers.first; ubuf_pre; ubuf_pre = ubuf_pre->next) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, NULL, NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, NULL);
const bool has_float = ibuf->rect_float;
@@ -797,10 +813,10 @@ static bool image_undosys_step_encode(struct bContext *C,
}
else {
/* Search for the previous buffer. */
- UndoImageBuf *ubuf_reference = (us_reference ?
- ubuf_lookup_from_reference(
- us_reference, uh->image_ref.ptr, ubuf_post) :
- NULL);
+ UndoImageBuf *ubuf_reference =
+ (us_reference ? ubuf_lookup_from_reference(
+ us_reference, uh->image_ref.ptr, uh->iuser.tile, ubuf_post) :
+ NULL);
int i = 0;
for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) {
@@ -850,6 +866,8 @@ static bool image_undosys_step_encode(struct bContext *C,
i += 1;
}
}
+ BLI_assert(i == ubuf_pre->tiles_len);
+ BLI_assert(i == ubuf_post->tiles_len);
}
BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, NULL);
}
@@ -1026,11 +1044,15 @@ void ED_image_undo_push_begin(const char *name, int paint_mode)
image_undo_push_begin(name, paint_mode);
}
-void ED_image_undo_push_begin_with_image(const char *name, Image *image, ImBuf *ibuf)
+void ED_image_undo_push_begin_with_image(const char *name,
+ Image *image,
+ ImBuf *ibuf,
+ int tile_number)
{
ImageUndoStep *us = image_undo_push_begin(name, PAINT_MODE_TEXTURE_2D);
- UndoImageHandle *uh = uhandle_ensure(&us->handles, image);
+ BLI_assert(BKE_image_get_tile(image, tile_number));
+ UndoImageHandle *uh = uhandle_ensure(&us->handles, image, tile_number);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf);
BLI_assert(ubuf_pre->post == NULL);
@@ -1038,9 +1060,9 @@ void ED_image_undo_push_begin_with_image(const char *name, Image *image, ImBuf *
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
us_reference = (ImageUndoStep *)us_reference->step.prev;
}
- UndoImageBuf *ubuf_reference = (us_reference ?
- ubuf_lookup_from_reference(us_reference, image, ubuf_pre) :
- NULL);
+ UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference(
+ us_reference, image, tile_number, ubuf_pre) :
+ NULL);
if (ubuf_reference) {
memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len);
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index a88ecc91868..f30c0e97cab 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -134,6 +134,9 @@ static SpaceLink *image_new(const ScrArea *UNUSED(area), const Scene *UNUSED(sce
BKE_scopes_new(&simage->scopes);
simage->sample_line_hist.height = 100;
+ simage->tile_grid_shape[0] = 1;
+ simage->tile_grid_shape[1] = 1;
+
/* tool header */
ar = MEM_callocN(sizeof(ARegion), "tool header for image");
@@ -246,6 +249,10 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_read_viewlayers);
WM_operatortype_append(IMAGE_OT_render_border);
WM_operatortype_append(IMAGE_OT_clear_render_border);
+
+ WM_operatortype_append(IMAGE_OT_tile_add);
+ WM_operatortype_append(IMAGE_OT_tile_remove);
+ WM_operatortype_append(IMAGE_OT_tile_fill);
}
static void image_keymap(struct wmKeyConfig *keyconf)
@@ -783,7 +790,8 @@ static void image_buttons_region_draw(const bContext *C, ARegion *ar)
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ /* TODO(lukas): Support tiles in scopes? */
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
/* XXX performance regression if name of scopes category changes! */
PanelCategoryStack *category = UI_panel_category_active_find(ar, "Scopes");