Cleanup: move image undo into own file
This commit is contained in:
parent
529c21acc2
commit
72c43e0285
|
@ -54,11 +54,13 @@ void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore,
|
|||
void ED_undo_paint_push_end(int type);
|
||||
|
||||
/* paint_image.c */
|
||||
/* image painting specific undo */
|
||||
void ED_image_undo_restore(struct bContext *C, struct ListBase *lb);
|
||||
void ED_image_undo_free(struct ListBase *lb);
|
||||
void ED_imapaint_clear_partial_redraw(void);
|
||||
void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old);
|
||||
void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op);
|
||||
|
||||
/* paint_image_undo.c */
|
||||
void ED_image_undo_push_begin(const char *name);
|
||||
void ED_image_undo_push_end(void);
|
||||
void ED_image_undo_restore(void);
|
||||
|
||||
#endif /* __ED_PAINT_H__ */
|
||||
|
|
|
@ -46,6 +46,7 @@ set(SRC
|
|||
paint_image.c
|
||||
paint_image_2d.c
|
||||
paint_image_proj.c
|
||||
paint_image_undo.c
|
||||
paint_mask.c
|
||||
paint_ops.c
|
||||
paint_stroke.c
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -55,7 +54,6 @@
|
|||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_node.h"
|
||||
|
@ -86,43 +84,10 @@
|
|||
|
||||
#include "paint_intern.h"
|
||||
|
||||
typedef struct UndoImageTile {
|
||||
struct UndoImageTile *next, *prev;
|
||||
|
||||
char idname[MAX_ID_NAME]; /* name instead of pointer*/
|
||||
char ibufname[IMB_FILENAME_SIZE];
|
||||
|
||||
union {
|
||||
float *fp;
|
||||
unsigned int *uint;
|
||||
void *pt;
|
||||
} rect;
|
||||
|
||||
unsigned short *mask;
|
||||
|
||||
int x, y;
|
||||
|
||||
Image *ima;
|
||||
short source, use_float;
|
||||
char gen_type;
|
||||
bool valid;
|
||||
} UndoImageTile;
|
||||
|
||||
/* this is a static resource for non-globality,
|
||||
* Maybe it should be exposed as part of the
|
||||
* paint operation, but for now just give a public interface */
|
||||
static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0};
|
||||
static SpinLock undolock;
|
||||
|
||||
void image_undo_init_locks(void)
|
||||
{
|
||||
BLI_spin_init(&undolock);
|
||||
}
|
||||
|
||||
void image_undo_end_locks(void)
|
||||
{
|
||||
BLI_spin_end(&undolock);
|
||||
}
|
||||
|
||||
ImagePaintPartialRedraw *get_imapaintpartial(void)
|
||||
{
|
||||
|
@ -134,296 +99,6 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr)
|
|||
imapaintpartial = *ippr;
|
||||
}
|
||||
|
||||
/* UNDO */
|
||||
typedef enum {
|
||||
COPY = 0,
|
||||
RESTORE = 1,
|
||||
RESTORE_COPY = 2
|
||||
} CopyMode;
|
||||
|
||||
static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode)
|
||||
{
|
||||
if (mode == COPY) {
|
||||
/* copy or swap contents of tile->rect and region in ibuf->rect */
|
||||
IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mode == RESTORE_COPY) {
|
||||
IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
}
|
||||
/* swap to the tmpbuf for easy copying */
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
|
||||
IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
|
||||
if (mode == RESTORE) {
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
|
||||
if (tile->use_float == use_float) {
|
||||
if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) {
|
||||
if (mask) {
|
||||
/* allocate mask if requested */
|
||||
if (!tile->mask) {
|
||||
tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
|
||||
"UndoImageTile.mask");
|
||||
}
|
||||
|
||||
*mask = tile->mask;
|
||||
}
|
||||
if (validate)
|
||||
tile->valid = true;
|
||||
|
||||
return tile->rect.pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int allocsize;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
void *data;
|
||||
|
||||
/* check if tile is already pushed */
|
||||
|
||||
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
|
||||
if (find_prev) {
|
||||
data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true);
|
||||
if (data)
|
||||
return data;
|
||||
}
|
||||
|
||||
if (*tmpibuf == NULL)
|
||||
*tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
|
||||
|
||||
tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile");
|
||||
BLI_strncpy(tile->idname, ima->id.name, sizeof(tile->idname));
|
||||
tile->x = x_tile;
|
||||
tile->y = y_tile;
|
||||
|
||||
/* add mask explicitly here */
|
||||
if (mask)
|
||||
*mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
|
||||
"UndoImageTile.mask");
|
||||
|
||||
allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char);
|
||||
tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect");
|
||||
|
||||
BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname));
|
||||
|
||||
tile->gen_type = ima->gen_type;
|
||||
tile->source = ima->source;
|
||||
tile->use_float = use_float;
|
||||
tile->valid = true;
|
||||
tile->ima = ima;
|
||||
|
||||
if (valid)
|
||||
*valid = &tile->valid;
|
||||
|
||||
undo_copy_tile(tile, *tmpibuf, ibuf, COPY);
|
||||
|
||||
if (proj)
|
||||
BLI_spin_lock(&undolock);
|
||||
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize);
|
||||
BLI_addtail(lb, tile);
|
||||
|
||||
if (proj)
|
||||
BLI_spin_unlock(&undolock);
|
||||
|
||||
return tile->rect.pt;
|
||||
}
|
||||
|
||||
void image_undo_remove_masks(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
if (tile->mask) {
|
||||
MEM_freeN(tile->mask);
|
||||
tile->mask = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void image_undo_restore_runtime(ListBase *lb)
|
||||
{
|
||||
ImBuf *ibuf, *tmpibuf;
|
||||
UndoImageTile *tile;
|
||||
|
||||
tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
|
||||
IB_rectfloat | IB_rect);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
Image *ima = tile->ima;
|
||||
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
||||
|
||||
undo_copy_tile(tile, tmpibuf, ibuf, RESTORE);
|
||||
|
||||
GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */
|
||||
if (ibuf->rect_float)
|
||||
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
|
||||
if (ibuf->mipmap[0])
|
||||
ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */
|
||||
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
}
|
||||
|
||||
IMB_freeImBuf(tmpibuf);
|
||||
}
|
||||
|
||||
void ED_image_undo_restore(bContext *C, ListBase *lb)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Image *ima = NULL;
|
||||
ImBuf *ibuf, *tmpibuf;
|
||||
UndoImageTile *tile;
|
||||
|
||||
tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
|
||||
IB_rectfloat | IB_rect);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
short use_float;
|
||||
|
||||
/* find image based on name, pointer becomes invalid with global undo */
|
||||
if (ima && STREQ(tile->idname, ima->id.name)) {
|
||||
/* ima is valid */
|
||||
}
|
||||
else {
|
||||
ima = BLI_findstring(&bmain->image, tile->idname, offsetof(ID, name));
|
||||
}
|
||||
|
||||
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
||||
|
||||
if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) {
|
||||
/* current ImBuf filename was changed, probably current frame
|
||||
* was changed when painting on image sequence, rather than storing
|
||||
* full image user (which isn't so obvious, btw) try to find ImBuf with
|
||||
* matched file name in list of already loaded images */
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
|
||||
ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname);
|
||||
}
|
||||
|
||||
if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ima->gen_type != tile->gen_type || ima->source != tile->source) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
use_float = ibuf->rect_float ? 1 : 0;
|
||||
|
||||
if (use_float != tile->use_float) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY);
|
||||
|
||||
GPU_free_image(ima); /* force OpenGL reload */
|
||||
if (ibuf->rect_float)
|
||||
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
|
||||
if (ibuf->mipmap[0])
|
||||
ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */
|
||||
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
||||
|
||||
DAG_id_tag_update(&ima->id, 0);
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
}
|
||||
|
||||
IMB_freeImBuf(tmpibuf);
|
||||
}
|
||||
|
||||
void ED_image_undo_free(ListBase *lb)
|
||||
{
|
||||
UndoImageTile *tile;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next)
|
||||
MEM_freeN(tile->rect.pt);
|
||||
}
|
||||
|
||||
static void image_undo_end(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int deallocsize = 0;
|
||||
int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
|
||||
/* first dispose of invalid tiles (may happen due to drag dot for instance) */
|
||||
for (tile = lb->first; tile;) {
|
||||
if (!tile->valid) {
|
||||
UndoImageTile *tmp_tile = tile->next;
|
||||
deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
|
||||
MEM_freeN(tile->rect.pt);
|
||||
BLI_freelinkN(lb, tile);
|
||||
tile = tmp_tile;
|
||||
}
|
||||
else {
|
||||
tile = tile->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* don't forget to remove the size of deallocated tiles */
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize);
|
||||
|
||||
ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
|
||||
}
|
||||
|
||||
static void image_undo_invalidate(void)
|
||||
{
|
||||
UndoImageTile *tile;
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next)
|
||||
tile->valid = false;
|
||||
}
|
||||
|
||||
/* Imagepaint Partial Redraw & Dirty Region */
|
||||
|
||||
void ED_imapaint_clear_partial_redraw(void)
|
||||
|
@ -453,7 +128,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
|
|||
|
||||
if (w == 0 || h == 0)
|
||||
return;
|
||||
|
||||
|
||||
if (!imapaintpartial.enabled) {
|
||||
imapaintpartial.x1 = x;
|
||||
imapaintpartial.y1 = y;
|
||||
|
@ -475,7 +150,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
|
|||
image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
|
||||
|
||||
ibuf->userflags |= IB_BITMAPDIRTY;
|
||||
|
||||
|
||||
if (tmpibuf)
|
||||
IMB_freeImBuf(tmpibuf);
|
||||
}
|
||||
|
@ -774,20 +449,11 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo
|
|||
}
|
||||
|
||||
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
|
||||
ED_image_undo_restore, ED_image_undo_free, NULL);
|
||||
ED_image_undo_push_begin(op->type->name);
|
||||
|
||||
return pop;
|
||||
}
|
||||
|
||||
/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
|
||||
static void paint_stroke_restore(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
image_undo_restore_runtime(lb);
|
||||
image_undo_invalidate();
|
||||
}
|
||||
|
||||
static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
||||
{
|
||||
PaintOperation *pop = paint_stroke_mode_data(stroke);
|
||||
|
@ -824,7 +490,7 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
|
|||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
|
||||
|
||||
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
|
||||
paint_stroke_restore();
|
||||
ED_image_undo_restore();
|
||||
}
|
||||
|
||||
if (pop->mode == PAINT_MODE_3D_PROJECT) {
|
||||
|
@ -901,7 +567,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|||
WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor);
|
||||
}
|
||||
|
||||
image_undo_end();
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* duplicate warning, see texpaint_init */
|
||||
#if 0
|
||||
|
@ -1510,8 +1176,7 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
|
|||
SpaceImage *sima = CTX_wm_space_image(C);
|
||||
Image *ima = sima->image;
|
||||
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
|
||||
ED_image_undo_restore, ED_image_undo_free, NULL);
|
||||
ED_image_undo_push_begin(op->type->name);
|
||||
|
||||
paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
|
||||
|
||||
|
|
|
@ -5384,8 +5384,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
|
|||
|
||||
scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING;
|
||||
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
|
||||
ED_image_undo_restore, ED_image_undo_free, NULL);
|
||||
ED_image_undo_push_begin(op->type->name);
|
||||
|
||||
/* allocate and initialize spatial data structures */
|
||||
project_paint_begin(&ps, false, 0);
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/sculpt_paint/image_undo.c
|
||||
* \ingroup edsculpt
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "ED_paint.h"
|
||||
|
||||
#include "GPU_draw.h"
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
||||
typedef struct UndoImageTile {
|
||||
struct UndoImageTile *next, *prev;
|
||||
|
||||
char idname[MAX_ID_NAME]; /* name instead of pointer*/
|
||||
char ibufname[IMB_FILENAME_SIZE];
|
||||
|
||||
union {
|
||||
float *fp;
|
||||
unsigned int *uint;
|
||||
void *pt;
|
||||
} rect;
|
||||
|
||||
unsigned short *mask;
|
||||
|
||||
int x, y;
|
||||
|
||||
Image *ima;
|
||||
short source, use_float;
|
||||
char gen_type;
|
||||
bool valid;
|
||||
} UndoImageTile;
|
||||
|
||||
/* this is a static resource for non-globality,
|
||||
* Maybe it should be exposed as part of the
|
||||
* paint operation, but for now just give a public interface */
|
||||
static SpinLock undolock;
|
||||
|
||||
void image_undo_init_locks(void)
|
||||
{
|
||||
BLI_spin_init(&undolock);
|
||||
}
|
||||
|
||||
void image_undo_end_locks(void)
|
||||
{
|
||||
BLI_spin_end(&undolock);
|
||||
}
|
||||
|
||||
/* UNDO */
|
||||
typedef enum {
|
||||
COPY = 0,
|
||||
RESTORE = 1,
|
||||
RESTORE_COPY = 2
|
||||
} CopyMode;
|
||||
|
||||
static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode)
|
||||
{
|
||||
if (mode == COPY) {
|
||||
/* copy or swap contents of tile->rect and region in ibuf->rect */
|
||||
IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mode == RESTORE_COPY) {
|
||||
IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
}
|
||||
/* swap to the tmpbuf for easy copying */
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
|
||||
IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE,
|
||||
tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
|
||||
|
||||
if (mode == RESTORE) {
|
||||
if (ibuf->rect_float) {
|
||||
SWAP(float *, tmpibuf->rect_float, tile->rect.fp);
|
||||
}
|
||||
else {
|
||||
SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
|
||||
if (tile->use_float == use_float) {
|
||||
if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) {
|
||||
if (mask) {
|
||||
/* allocate mask if requested */
|
||||
if (!tile->mask) {
|
||||
tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
|
||||
"UndoImageTile.mask");
|
||||
}
|
||||
|
||||
*mask = tile->mask;
|
||||
}
|
||||
if (validate) {
|
||||
tile->valid = true;
|
||||
}
|
||||
return tile->rect.pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int allocsize;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
void *data;
|
||||
|
||||
/* check if tile is already pushed */
|
||||
|
||||
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
|
||||
if (find_prev) {
|
||||
data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true);
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
if (*tmpibuf == NULL) {
|
||||
*tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
|
||||
}
|
||||
|
||||
tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile");
|
||||
BLI_strncpy(tile->idname, ima->id.name, sizeof(tile->idname));
|
||||
tile->x = x_tile;
|
||||
tile->y = y_tile;
|
||||
|
||||
/* add mask explicitly here */
|
||||
if (mask) {
|
||||
*mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE,
|
||||
"UndoImageTile.mask");
|
||||
}
|
||||
allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char);
|
||||
tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect");
|
||||
|
||||
BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname));
|
||||
|
||||
tile->gen_type = ima->gen_type;
|
||||
tile->source = ima->source;
|
||||
tile->use_float = use_float;
|
||||
tile->valid = true;
|
||||
tile->ima = ima;
|
||||
|
||||
if (valid) {
|
||||
*valid = &tile->valid;
|
||||
}
|
||||
undo_copy_tile(tile, *tmpibuf, ibuf, COPY);
|
||||
|
||||
if (proj) {
|
||||
BLI_spin_lock(&undolock);
|
||||
}
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize);
|
||||
BLI_addtail(lb, tile);
|
||||
|
||||
if (proj) {
|
||||
BLI_spin_unlock(&undolock);
|
||||
}
|
||||
return tile->rect.pt;
|
||||
}
|
||||
|
||||
void image_undo_remove_masks(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
if (tile->mask) {
|
||||
MEM_freeN(tile->mask);
|
||||
tile->mask = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void image_undo_restore_runtime(ListBase *lb)
|
||||
{
|
||||
ImBuf *ibuf, *tmpibuf;
|
||||
UndoImageTile *tile;
|
||||
|
||||
tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
|
||||
IB_rectfloat | IB_rect);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
Image *ima = tile->ima;
|
||||
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
||||
|
||||
undo_copy_tile(tile, tmpibuf, ibuf, RESTORE);
|
||||
|
||||
GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */
|
||||
if (ibuf->rect_float) {
|
||||
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
|
||||
}
|
||||
if (ibuf->mipmap[0]) {
|
||||
ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */
|
||||
}
|
||||
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
}
|
||||
|
||||
IMB_freeImBuf(tmpibuf);
|
||||
}
|
||||
|
||||
static void image_undo_restore_list(bContext *C, ListBase *lb)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Image *ima = NULL;
|
||||
ImBuf *ibuf, *tmpibuf;
|
||||
UndoImageTile *tile;
|
||||
|
||||
tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
|
||||
IB_rectfloat | IB_rect);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
short use_float;
|
||||
|
||||
/* find image based on name, pointer becomes invalid with global undo */
|
||||
if (ima && STREQ(tile->idname, ima->id.name)) {
|
||||
/* ima is valid */
|
||||
}
|
||||
else {
|
||||
ima = BLI_findstring(&bmain->image, tile->idname, offsetof(ID, name));
|
||||
}
|
||||
|
||||
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
||||
|
||||
if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) {
|
||||
/* current ImBuf filename was changed, probably current frame
|
||||
* was changed when painting on image sequence, rather than storing
|
||||
* full image user (which isn't so obvious, btw) try to find ImBuf with
|
||||
* matched file name in list of already loaded images */
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
|
||||
ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname);
|
||||
}
|
||||
|
||||
if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ima->gen_type != tile->gen_type || ima->source != tile->source) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
use_float = ibuf->rect_float ? 1 : 0;
|
||||
|
||||
if (use_float != tile->use_float) {
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY);
|
||||
|
||||
GPU_free_image(ima); /* force OpenGL reload */
|
||||
if (ibuf->rect_float) {
|
||||
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
|
||||
}
|
||||
if (ibuf->mipmap[0]) {
|
||||
ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */
|
||||
}
|
||||
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
||||
|
||||
DAG_id_tag_update(&ima->id, 0);
|
||||
|
||||
BKE_image_release_ibuf(ima, ibuf, NULL);
|
||||
}
|
||||
|
||||
IMB_freeImBuf(tmpibuf);
|
||||
}
|
||||
|
||||
static void image_undo_free_list(ListBase *lb)
|
||||
{
|
||||
UndoImageTile *tile;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
MEM_freeN(tile->rect.pt);
|
||||
}
|
||||
}
|
||||
|
||||
void ED_image_undo_push_begin(const char *name)
|
||||
{
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, name, image_undo_restore_list, image_undo_free_list, NULL);
|
||||
}
|
||||
|
||||
void ED_image_undo_push_end(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int deallocsize = 0;
|
||||
int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
|
||||
/* first dispose of invalid tiles (may happen due to drag dot for instance) */
|
||||
for (tile = lb->first; tile;) {
|
||||
if (!tile->valid) {
|
||||
UndoImageTile *tmp_tile = tile->next;
|
||||
deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
|
||||
MEM_freeN(tile->rect.pt);
|
||||
BLI_freelinkN(lb, tile);
|
||||
tile = tmp_tile;
|
||||
}
|
||||
else {
|
||||
tile = tile->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* don't forget to remove the size of deallocated tiles */
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize);
|
||||
|
||||
ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
|
||||
}
|
||||
|
||||
static void image_undo_invalidate(void)
|
||||
{
|
||||
UndoImageTile *tile;
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
tile->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
|
||||
void ED_image_undo_restore(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
image_undo_restore_runtime(lb);
|
||||
image_undo_invalidate();
|
||||
}
|
|
@ -2617,8 +2617,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
|
||||
if (support_undo) {
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
|
||||
ED_image_undo_restore, ED_image_undo_free, NULL);
|
||||
ED_image_undo_push_begin(op->type->name);
|
||||
/* 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_imapaint_clear_partial_redraw();
|
||||
|
|
Loading…
Reference in New Issue