Image: support storing full image buffers for each undo step

Update image undo to store buffers for each step:

- Undo buffers share tiles to avoid using too much memory.
- Undo support for different sized buffers
  allowing operations such as crop or resize.
- Paint tiles have been split into separate API/storage.
- Painting speed wont be impacted significantly
  since storing the extra tiles is done after the stroke & only
  for the first undo step.

Resolves T61263, see D5939 for details.
This commit is contained in:
Campbell Barton 2019-10-02 00:07:06 +10:00
parent bdd142bc02
commit 151cc02b6f
Notes: blender-bot 2023-02-14 07:31:34 +01:00
Referenced by commit 5b18997543, T71094: anchored & drag-dot brushes paint continuously
Referenced by issue #83806, Undo - Current Status and Important Fixes Needed
Referenced by issue #61263, Undo steps with texture paint break across memfile steps
6 changed files with 726 additions and 261 deletions

View File

@ -42,6 +42,10 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperat
/* paint_image_undo.c */
void ED_image_undo_push_begin(const char *name, int paint_mode);
void ED_image_undo_push_begin_with_image(const char *name,
struct Image *image,
struct ImBuf *ibuf);
void ED_image_undo_push_end(void);
void ED_image_undo_restore(struct UndoStep *us);

View File

@ -31,6 +31,7 @@ set(INC
../../render/extern/include
../../windowmanager
../../../../intern/atomic
../../../../intern/clog
../../../../intern/glew-mx
../../../../intern/guardedalloc
)

File diff suppressed because it is too large Load Diff

View File

@ -2765,8 +2765,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
Image *ima = image_from_context(C);
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
SpaceImage *sima = CTX_wm_space_image(C);
/* undo is supported only on image paint mode currently */
bool support_undo = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
/* flags indicate if this channel should be inverted */
const bool r = RNA_boolean_get(op->ptr, "invert_r");
@ -2781,14 +2780,12 @@ static int image_invert_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (support_undo) {
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
/* not strictly needed, because we only imapaint_dirty_region to invalidate all tiles
* but better do this right in case someone copies this for a tool that uses partial
* redraw better */
ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf);
if (is_paint) {
ED_imapaint_clear_partial_redraw();
ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false);
}
/* TODO: make this into an IMB_invert_channels(ibuf,r,g,b,a) method!? */
if (ibuf->rect_float) {
@ -2842,9 +2839,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
ibuf->userflags |= IB_MIPMAP_INVALID;
}
if (support_undo) {
ED_image_undo_push_end();
}
ED_image_undo_push_end();
/* force GPU reupload, all image is invalid */
GPU_free_image(ima);
@ -2880,7 +2875,7 @@ void IMAGE_OT_invert(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->flag = OPTYPE_REGISTER;
}
/** \} */

View File

@ -228,6 +228,8 @@ void IMB_blend_color_float(float dst[4],
void IMB_rect_crop(struct ImBuf *ibuf, const struct rcti *crop);
void IMB_rect_size_set(struct ImBuf *ibuf, const uint size[2]);
void IMB_rectclip(struct ImBuf *dbuf,
const struct ImBuf *sbuf,
int *destx,

View File

@ -279,6 +279,45 @@ void IMB_rect_crop(ImBuf *ibuf, const rcti *crop)
ibuf->y = size_dst[1];
}
/** Re-alloc buffers at a new size */
static void rect_realloc_4bytes(void **buf_p, const uint size[2])
{
if (*buf_p == NULL) {
return;
}
MEM_freeN(*buf_p);
*buf_p = MEM_mallocN(sizeof(uint) * size[0] * size[1], __func__);
}
static void rect_realloc_16bytes(void **buf_p, const uint size[2])
{
if (*buf_p == NULL) {
return;
}
MEM_freeN(*buf_p);
*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[0] > 0);
if ((size[0] == ibuf->x) && (size[1] == ibuf->y)) {
return;
}
rect_realloc_4bytes((void **)&ibuf->rect, size);
rect_realloc_4bytes((void **)&ibuf->zbuf, size);
rect_realloc_4bytes((void **)&ibuf->zbuf_float, size);
rect_realloc_16bytes((void **)&ibuf->rect_float, size);
ibuf->x = size[0];
ibuf->y = size[1];
}
/* clipping */
void IMB_rectclip(ImBuf *dbuf,