diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-03-23 09:51:56 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-03-23 11:15:23 +0300 |
commit | 72c43e028557dc52e00969ed75424e8ac98cd6e3 (patch) | |
tree | 9aec33928db543262c51b77fed05a6ee9834f7c6 /source/blender/editors/sculpt_paint/paint_image_undo.c | |
parent | 529c21acc2e6e0830844a610d4f8d6a9b72a9902 (diff) |
Cleanup: move image undo into own file
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_image_undo.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_undo.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c new file mode 100644 index 00000000000..87b0eac9155 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_image_undo.c @@ -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(); +} |