diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 21 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_2d.c | 722 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_proj.c | 118 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 2 |
4 files changed, 559 insertions, 304 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 0b770f17314..de09a52258f 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -119,7 +119,8 @@ void imapaint_region_tiles( *ty = (y >> ED_IMAGE_UNDO_TILE_BITS); } -void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) +void ED_imapaint_dirty_region( + Image *ima, ImBuf *ibuf, int tile_number, int x, int y, int w, int h, bool find_old) { ImBuf *tmpibuf = NULL; int tilex, tiley, tilew, tileh, tx, ty; @@ -152,7 +153,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { ED_image_paint_tile_push( - undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); + undo_tiles, ima, ibuf, &tmpibuf, tile_number, tx, ty, NULL, NULL, false, find_old); } } @@ -163,7 +164,8 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int } } -void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) +void imapaint_image_update( + SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint) { if (imapaintpartial.x1 != imapaintpartial.x2 && imapaintpartial.y1 != imapaintpartial.y2) { IMB_partial_display_buffer_update_delayed( @@ -180,8 +182,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int h = imapaintpartial.y2 - imapaintpartial.y1; if (w && h) { /* Testing with partial update in uv editor too */ - GPU_paint_update_image( - image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); + GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); } } } @@ -623,7 +624,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) else { srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); } - paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); } else { paint_proj_stroke(C, @@ -1297,7 +1298,10 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) +void ED_imapaint_bucket_fill(struct bContext *C, + float color[3], + wmOperator *op, + const int mouse[2]) { wmWindowManager *wm = CTX_wm_manager(C); SpaceImage *sima = CTX_wm_space_image(C); @@ -1307,7 +1311,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + float mouse_init[2] = {mouse[0], mouse[1]}; + paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL); BKE_undosys_step_push(wm->undo_stack, C, op->type->name); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 004caae8a00..06d79b8a49d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -30,6 +30,7 @@ #include "DNA_space_types.h" #include "DNA_object_types.h" +#include "BLI_listbase.h" #include "BLI_math_color_blend.h" #include "BLI_stack.h" #include "BLI_bitmap.h" @@ -84,22 +85,21 @@ typedef struct BrushPainterCache { unsigned short *tex_mask_old; unsigned int tex_mask_old_w; unsigned int tex_mask_old_h; + + int image_size[2]; } BrushPainterCache; typedef struct BrushPainter { Scene *scene; Brush *brush; - float lastpaintpos[2]; /* position of last paint op */ - float startpaintpos[2]; /* position of first paint */ - short firsttouch; /* first paint op */ struct ImagePool *pool; /* image pool */ rctf tex_mapping; /* texture coordinate mapping */ rctf mask_mapping; /* mask texture coordinate mapping */ - BrushPainterCache cache; + bool cache_invert; } BrushPainter; typedef struct ImagePaintRegion { @@ -108,6 +108,27 @@ typedef struct ImagePaintRegion { int width, height; } ImagePaintRegion; +typedef enum ImagePaintTileState { + PAINT2D_TILE_UNINITIALIZED = 0, + PAINT2D_TILE_MISSING, + PAINT2D_TILE_READY, +} ImagePaintTileState; + +typedef struct ImagePaintTile { + ImageUser iuser; + ImBuf *canvas; + float radius_fac; + int size[2]; + float uv_origin[2]; /* Stores the position of this tile in UV space. */ + bool need_redraw; + BrushPainterCache cache; + + ImagePaintTileState state; + + float last_paintpos[2]; /* position of last paint op */ + float start_paintpos[2]; /* position of first paint */ +} ImagePaintTile; + typedef struct ImagePaintState { BrushPainter *painter; SpaceImage *sima; @@ -119,10 +140,7 @@ typedef struct ImagePaintState { Brush *brush; short tool, blend; Image *image; - ImBuf *canvas; ImBuf *clonecanvas; - const char *warnpackedfile; - const char *warnmultifile; bool do_masking; @@ -133,7 +151,8 @@ typedef struct ImagePaintState { int do_facesel; int symmetry; - bool need_redraw; + ImagePaintTile *tiles; + int num_tiles; BlurKernel *blurkernel; } ImagePaintState; @@ -145,63 +164,60 @@ static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool inver painter->brush = brush; painter->scene = scene; painter->firsttouch = 1; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - painter->cache.invert = invert; + painter->cache_invert = invert; return painter; } -static void brush_painter_2d_require_imbuf(BrushPainter *painter, - bool use_float, - bool use_color_correction) +static void brush_painter_2d_require_imbuf( + Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction, bool invert) { - Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; - if ((painter->cache.use_float != use_float)) { - if (painter->cache.ibuf) { - IMB_freeImBuf(painter->cache.ibuf); + if ((cache->use_float != use_float)) { + if (cache->ibuf) { + IMB_freeImBuf(cache->ibuf); } - if (painter->cache.curve_mask) { - MEM_freeN(painter->cache.curve_mask); + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); } - if (painter->cache.tex_mask) { - MEM_freeN(painter->cache.tex_mask); + if (cache->tex_mask) { + MEM_freeN(cache->tex_mask); } - if (painter->cache.tex_mask_old) { - MEM_freeN(painter->cache.tex_mask_old); + if (cache->tex_mask_old) { + MEM_freeN(cache->tex_mask_old); } - painter->cache.ibuf = NULL; - painter->cache.curve_mask = NULL; - painter->cache.tex_mask = NULL; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - } - - painter->cache.use_float = use_float; - painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? - true : - false; - painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; + cache->ibuf = NULL; + cache->curve_mask = NULL; + cache->tex_mask = NULL; + cache->lastdiameter = -1; /* force ibuf create in refresh */ + cache->invert = invert; + } + + cache->use_float = use_float; + cache->use_color_correction = use_float && use_color_correction; + cache->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : + false; + cache->is_maskbrush = (brush->mask_mtex.tex) ? true : false; } -static void brush_painter_2d_free(BrushPainter *painter) +static void brush_painter_cache_2d_free(BrushPainterCache *cache) { - if (painter->cache.ibuf) { - IMB_freeImBuf(painter->cache.ibuf); + if (cache->ibuf) { + IMB_freeImBuf(cache->ibuf); } - if (painter->cache.texibuf) { - IMB_freeImBuf(painter->cache.texibuf); + if (cache->texibuf) { + IMB_freeImBuf(cache->texibuf); } - if (painter->cache.curve_mask) { - MEM_freeN(painter->cache.curve_mask); + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); } - if (painter->cache.tex_mask) { - MEM_freeN(painter->cache.tex_mask); + if (cache->tex_mask) { + MEM_freeN(cache->tex_mask); } - if (painter->cache.tex_mask_old) { - MEM_freeN(painter->cache.tex_mask_old); + if (cache->tex_mask_old) { + MEM_freeN(cache->tex_mask_old); } - MEM_freeN(painter); } static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) @@ -212,7 +228,7 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) } /* create a mask with the mask texture */ -static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) +static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size) { Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -240,6 +256,7 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si /* update rectangular section of the brush image */ static void brush_painter_mask_imbuf_update(BrushPainter *painter, + ImagePaintTile *tile, unsigned short *tex_mask_old, int origx, int origy, @@ -247,10 +264,11 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter, int h, int xt, int yt, - int diameter) + const int diameter) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; rctf tex_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; unsigned short res; @@ -259,8 +277,8 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter, int x, y, thread = 0; - unsigned short *tex_mask = painter->cache.tex_mask; - unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + unsigned short *tex_mask = cache->tex_mask; + unsigned short *tex_mask_cur = cache->tex_mask_old; /* fill pixels */ for (y = origy; y < h; y++) { @@ -280,8 +298,7 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter, /* read from old texture buffer */ if (use_texture_old) { - res = *(tex_mask_old + - ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + res = *(tex_mask_old + ((y - origy + yt) * cache->tex_mask_old_w + (x - origx + xt))); } /* write to new texture mask */ @@ -298,10 +315,11 @@ static void brush_painter_mask_imbuf_update(BrushPainter *painter, * textures that stick to the surface where only part of the pixels are new */ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, + ImagePaintTile *tile, const float pos[2], - int diameter) + const int diameter) { - BrushPainterCache *cache = &painter->cache; + BrushPainterCache *cache = &tile->cache; unsigned short *tex_mask_old; int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; @@ -319,15 +337,16 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, if (tex_mask_old) { ImBuf maskibuf; ImBuf maskibuf_old; - maskibuf.x = maskibuf.y = diameter; + maskibuf.x = diameter; + maskibuf.y = diameter; maskibuf_old.x = cache->tex_mask_old_w; maskibuf_old.y = cache->tex_mask_old_h; srcx = srcy = 0; w = cache->tex_mask_old_w; h = cache->tex_mask_old_h; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); /* hack, use temporary rects so that clipping works */ IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); @@ -345,7 +364,8 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) { - brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + brush_painter_mask_imbuf_update( + painter, tile, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); } if (tex_mask_old) { @@ -354,16 +374,17 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, /* sample texture in new areas */ if ((0 < x1) && (0 < diameter)) { - brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); + brush_painter_mask_imbuf_update(painter, tile, NULL, 0, 0, x1, diameter, 0, 0, diameter); } if ((x2 < diameter) && (0 < diameter)) { - brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + brush_painter_mask_imbuf_update( + painter, tile, NULL, x2, 0, diameter, diameter, 0, 0, diameter); } if ((x1 < x2) && (0 < y1)) { - brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); + brush_painter_mask_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0, diameter); } if ((x1 < x2) && (y2 < diameter)) { - brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + brush_painter_mask_imbuf_update(painter, tile, NULL, x1, y2, x2, diameter, 0, 0, diameter); } /* through with sampling, now update sizes */ @@ -445,13 +466,12 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, } /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, - int size, - float pressure, - float distance) +static ImBuf *brush_painter_imbuf_new( + BrushPainter *painter, ImagePaintTile *tile, const int size, float pressure, float distance) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); @@ -459,9 +479,9 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, rctf tex_mapping = painter->tex_mapping; struct ImagePool *pool = painter->pool; - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; + bool use_color_correction = cache->use_color_correction; + bool use_float = cache->use_float; + bool is_texbrush = cache->is_texbrush; int x, y, thread = 0; float brush_rgb[3]; @@ -471,14 +491,8 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, - brush, - use_color_correction, - painter->cache.invert, - distance, - pressure, - brush_rgb, - display); + paint_brush_color_get( + scene, brush, use_color_correction, cache->invert, distance, pressure, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -526,11 +540,19 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, } /* update rectangular section of the brush image */ -static void brush_painter_imbuf_update( - BrushPainter *painter, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt) +static void brush_painter_imbuf_update(BrushPainter *painter, + ImagePaintTile *tile, + ImBuf *oldtexibuf, + int origx, + int origy, + int w, + int h, + int xt, + int yt) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); @@ -538,21 +560,21 @@ static void brush_painter_imbuf_update( rctf tex_mapping = painter->tex_mapping; struct ImagePool *pool = painter->pool; - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; + bool use_color_correction = cache->use_color_correction; + bool use_float = cache->use_float; + bool is_texbrush = cache->is_texbrush; bool use_texture_old = (oldtexibuf != NULL); int x, y, thread = 0; float brush_rgb[3]; - ImBuf *ibuf = painter->cache.ibuf; - ImBuf *texibuf = painter->cache.texibuf; + ImBuf *ibuf = cache->ibuf; + ImBuf *texibuf = cache->texibuf; /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { paint_brush_color_get( - scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); + scene, brush, use_color_correction, cache->invert, 0.0, 1.0, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -641,10 +663,11 @@ static void brush_painter_imbuf_update( * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ static void brush_painter_imbuf_partial_update(BrushPainter *painter, + ImagePaintTile *tile, const float pos[2], - int diameter) + const int diameter) { - BrushPainterCache *cache = &painter->cache; + BrushPainterCache *cache = &tile->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; @@ -663,8 +686,8 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, srcx = srcy = 0; w = oldtexibuf->x; h = oldtexibuf->y; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } @@ -681,7 +704,7 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) { - brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); + brush_painter_imbuf_update(painter, tile, oldtexibuf, x1, y1, x2, y2, srcx, srcy); } if (oldtexibuf) { @@ -690,29 +713,30 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) { - brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, 0, 0, x1, ibuf->y, 0, 0); } if ((x2 < ibuf->x) && (0 < ibuf->y)) { - brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); } if ((x1 < x2) && (0 < y1)) { - brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0); } if ((x1 < x2) && (y2 < ibuf->y)) { - brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x1, y2, x2, ibuf->y, 0, 0); } } static void brush_painter_2d_tex_mapping(ImagePaintState *s, - int diameter, + ImBuf *canvas, + const int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { - float invw = 1.0f / (float)s->canvas->x; - float invh = 1.0f / (float)s->canvas->y; + float invw = 1.0f / (float)canvas->x; + float invh = 1.0f / (float)canvas->y; int xmin, ymin, xmax, ymax; int ipos[2]; @@ -756,6 +780,7 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, + ImagePaintTile *tile, const float pos[2], const float mouse[2], float pressure, @@ -765,7 +790,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; - BrushPainterCache *cache = &painter->cache; + BrushPainterCache *cache = &tile->cache; /* Adding 4 pixels of padding for brush antialiasing */ const int diameter = MAX2(1, size * 2) + 4; @@ -782,7 +807,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, painter->pool = BKE_image_pool_new(); /* determine how can update based on textures used */ - if (painter->cache.is_texbrush) { + if (cache->is_texbrush) { if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { tex_rotation += ups->brush_rotation; } @@ -794,15 +819,16 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } brush_painter_2d_tex_mapping(s, + tile->canvas, diameter, - painter->startpaintpos, + tile->start_paintpos, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } - if (painter->cache.is_maskbrush) { + if (cache->is_maskbrush) { bool renew_maxmask = false; bool do_partial_update_mask = false; /* invalidate case for all mapping modes */ @@ -822,7 +848,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, renew_maxmask = true; } - if ((diameter != cache->lastdiameter) || (mask_rotation != cache->last_mask_rotation) || + if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) || renew_maxmask) { if (cache->tex_mask) { MEM_freeN(cache->tex_mask); @@ -830,15 +856,16 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } brush_painter_2d_tex_mapping(s, + tile->canvas, diameter, - painter->startpaintpos, + tile->start_paintpos, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); if (do_partial_update_mask) { - brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter); } else { cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); @@ -856,8 +883,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos); /* detect if we need to recreate image brush buffer */ - if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) || - do_random || update_color) { + if (diameter != cache->lastdiameter || (tex_rotation != cache->last_tex_rotation) || do_random || + update_color) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); cache->ibuf = NULL; @@ -865,11 +892,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, if (do_partial_update) { /* do partial update of texture */ - brush_painter_imbuf_partial_update(painter, pos, diameter); + brush_painter_imbuf_partial_update(painter, tile, pos, diameter); } else { /* create brush from scratch */ - cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); + cache->ibuf = brush_painter_imbuf_new(painter, tile, diameter, pressure, distance); } cache->lastdiameter = diameter; @@ -878,11 +905,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } else if (do_partial_update) { /* do only partial update of texture */ - int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); - int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); + int dx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]); + int dy = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]); if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos, diameter); + brush_painter_imbuf_partial_update(painter, tile, pos, diameter); } } @@ -890,6 +917,56 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, painter->pool = NULL; } +static bool paint_2d_ensure_tile_canvas(ImagePaintState *s, int i) +{ + if (i == 0) { + return true; + } + if (i >= s->num_tiles) { + return false; + } + + if (s->tiles[i].state == PAINT2D_TILE_READY) { + return true; + } + if (s->tiles[i].state == PAINT2D_TILE_MISSING) { + return false; + } + + s->tiles[i].cache.lastdiameter = -1; + + s->tiles[i].iuser.ok = true; + + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); + if (ibuf != NULL) { + if (ibuf->channels != 4) { + s->tiles[i].state = PAINT2D_TILE_MISSING; + } + else if ((s->tiles[0].canvas->rect && !ibuf->rect) || + (s->tiles[0].canvas->rect_float && !ibuf->rect_float)) { + s->tiles[i].state = PAINT2D_TILE_MISSING; + } + else { + s->tiles[i].size[0] = ibuf->x; + s->tiles[i].size[1] = ibuf->y; + s->tiles[i].radius_fac = sqrtf(((float)ibuf->x * (float)ibuf->y) / + (s->tiles[0].size[0] * s->tiles[0].size[1])); + s->tiles[i].state = PAINT2D_TILE_READY; + } + } + else { + s->tiles[i].state = PAINT2D_TILE_MISSING; + } + + if (s->tiles[i].state == PAINT2D_TILE_MISSING) { + BKE_image_release_ibuf(s->image, ibuf, NULL); + return false; + } + + s->tiles[i].canvas = ibuf; + return true; +} + /* keep these functions in sync */ static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4]) { @@ -935,15 +1012,15 @@ static void paint_2d_ibuf_rgb_set( } } -static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile) +static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile) { - if (tile & PAINT_TILE_X) { + if (paint_tile & PAINT_TILE_X) { *x %= ibuf->x; if (*x < 0) { *x += ibuf->x; } } - if (tile & PAINT_TILE_Y) { + if (paint_tile & PAINT_TILE_Y) { *y %= ibuf->y; if (*y < 0) { *y += ibuf->y; @@ -951,12 +1028,13 @@ static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile) } } -static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w) +static float paint_2d_ibuf_add_if( + ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w) { float inrgb[4]; - if (tile) { - paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); + if (paint_tile) { + paint_2d_ibuf_tile_convert(ibuf, &x, &y, paint_tile); } /* need to also do clipping here always since tiled coordinates * are not always within bounds */ @@ -973,10 +1051,14 @@ static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, shor return w; } -static void paint_2d_lift_soften( - ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) +static void paint_2d_lift_soften(ImagePaintState *s, + ImagePaintTile *tile, + ImBuf *ibuf, + ImBuf *ibufb, + int *pos, + const short paint_tile) { - bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); float threshold = s->brush->sharp_threshold; int x, y, xi, yi, xo, yo, xk, yk; float count; @@ -992,7 +1074,7 @@ static void paint_2d_lift_soften( in_off[1] = pos[1]; out_off[0] = out_off[1] = 0; - if (!tile) { + if (!paint_tile) { IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); if ((dim[0] == 0) || (dim[1] == 0)) { @@ -1010,8 +1092,8 @@ static void paint_2d_lift_soften( yi = in_off[1] + y; count = 0.0; - if (tile) { - paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); + if (paint_tile) { + paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, paint_tile); if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) { paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); } @@ -1031,7 +1113,7 @@ static void paint_2d_lift_soften( xi + xk - kernel->pixel_len, yi + yk - kernel->pixel_len, outrgb, - tile, + paint_tile, kernel->wdata[xk + yk * kernel->side]); } } @@ -1085,7 +1167,7 @@ static void paint_2d_set_region( static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, - short tile) + short paint_tile) { int destx = region->destx; int desty = region->desty; @@ -1096,7 +1178,7 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], int origw, origh, w, h, tot = 0; /* convert destination and source coordinates to be within image */ - if (tile & PAINT_TILE_X) { + if (paint_tile & PAINT_TILE_X) { destx = destx % dbuf->x; if (destx < 0) { destx += dbuf->x; @@ -1106,7 +1188,7 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], srcx += sbuf->x; } } - if (tile & PAINT_TILE_Y) { + if (paint_tile & PAINT_TILE_Y) { desty = desty % dbuf->y; if (desty < 0) { desty += dbuf->y; @@ -1126,15 +1208,15 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); /* do 3 other rects if needed */ - if ((tile & PAINT_TILE_X) && w < origw) { + if ((paint_tile & PAINT_TILE_X) && w < origw) { paint_2d_set_region( ®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); } - if ((tile & PAINT_TILE_Y) && h < origh) { + if ((paint_tile & PAINT_TILE_Y) && h < origh) { paint_2d_set_region( ®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); } - if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) { + if ((paint_tile & PAINT_TILE_X) && (paint_tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) { paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, @@ -1147,13 +1229,13 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], return tot; } -static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile) +static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile) { ImagePaintRegion region[4]; int a, tot; paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); - tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); + tot = paint_2d_torus_split_region(region, ibufb, ibuf, paint_tile); for (a = 0; a < tot; a++) { IMB_rectblend(ibufb, @@ -1229,9 +1311,8 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ } static void paint_2d_do_making_brush(ImagePaintState *s, + ImagePaintTile *tile, ImagePaintRegion *region, - unsigned short *curveb, - unsigned short *texmaskb, ImBuf *frombuf, float mask_max, short blend, @@ -1252,21 +1333,21 @@ static void paint_2d_do_making_brush(ImagePaintState *s, int origx = region->destx - tx * ED_IMAGE_UNDO_TILE_SIZE; int origy = region->desty - ty * ED_IMAGE_UNDO_TILE_SIZE; - if (s->canvas->rect_float) { + if (tile->canvas->rect_float) { tmpbuf.rect_float = ED_image_paint_tile_find( - undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + undo_tiles, s->image, tile->canvas, tile->iuser.tile, tx, ty, &mask, false); } else { tmpbuf.rect = ED_image_paint_tile_find( - undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + undo_tiles, s->image, tile->canvas, tile->iuser.tile, tx, ty, &mask, false); } - IMB_rectblend(s->canvas, + IMB_rectblend(tile->canvas, &tmpbuf, frombuf, mask, - curveb, - texmaskb, + tile->cache.curve_mask, + tile->cache.tex_mask, mask_max, region->destx, region->desty, @@ -1284,9 +1365,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s, typedef struct Paint2DForeachData { ImagePaintState *s; + ImagePaintTile *tile; ImagePaintRegion *region; - unsigned short *curveb; - unsigned short *texmaskb; ImBuf *frombuf; float mask_max; short blend; @@ -1300,9 +1380,8 @@ static void paint_2d_op_foreach_do(void *__restrict data_v, { Paint2DForeachData *data = (Paint2DForeachData *)data_v; paint_2d_do_making_brush(data->s, + data->tile, data->region, - data->curveb, - data->texmaskb, data->frombuf, data->mask_max, data->blend, @@ -1313,16 +1392,16 @@ static void paint_2d_op_foreach_do(void *__restrict data_v, } static int paint_2d_op(void *state, - ImBuf *ibufb, - unsigned short *curveb, - unsigned short *texmaskb, + ImagePaintTile *tile, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); ImBuf *clonebuf = NULL, *frombuf; + ImBuf *canvas = tile->canvas; + ImBuf *ibufb = tile->cache.ibuf; ImagePaintRegion region[4]; - short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); + short paint_tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); short blend = s->blend; const float *offset = s->brush->clone.offset; float liftpos[2]; @@ -1334,7 +1413,7 @@ static int paint_2d_op(void *state, /* lift from canvas */ if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); + paint_2d_lift_soften(s, tile, canvas, ibufb, bpos, paint_tile); blend = IMB_BLEND_INTERPOLATE; } else if (s->tool == PAINT_TOOL_SMEAR) { @@ -1343,12 +1422,12 @@ static int paint_2d_op(void *state, } paint_2d_convert_brushco(ibufb, lastpos, blastpos); - paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); + paint_2d_lift_smear(canvas, ibufb, blastpos, paint_tile); blend = IMB_BLEND_INTERPOLATE; } else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { - liftpos[0] = pos[0] - offset[0] * s->canvas->x; - liftpos[1] = pos[1] - offset[1] * s->canvas->y; + liftpos[0] = pos[0] - offset[0] * canvas->x; + liftpos[1] = pos[1] - offset[1] * canvas->y; paint_2d_convert_brushco(ibufb, liftpos, bliftpos); clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); @@ -1356,9 +1435,9 @@ static int paint_2d_op(void *state, frombuf = (clonebuf) ? clonebuf : ibufb; - if (tile) { + if (paint_tile) { paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); + tot = paint_2d_torus_split_region(region, canvas, frombuf, paint_tile); } else { paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); @@ -1368,7 +1447,8 @@ static int paint_2d_op(void *state, /* blend into canvas */ for (a = 0; a < tot; a++) { ED_imapaint_dirty_region(s->image, - s->canvas, + canvas, + tile->iuser.tile, region[a].destx, region[a].desty, region[a].width, @@ -1379,7 +1459,7 @@ static int paint_2d_op(void *state, /* masking, find original pixels tiles from undo buffer to composite over */ int tilex, tiley, tilew, tileh; - imapaint_region_tiles(s->canvas, + imapaint_region_tiles(canvas, region[a].destx, region[a].desty, region[a].width, @@ -1391,14 +1471,13 @@ static int paint_2d_op(void *state, if (tiley == tileh) { paint_2d_do_making_brush( - s, ®ion[a], curveb, texmaskb, frombuf, mask_max, blend, tilex, tiley, tilew, tileh); + s, tile, ®ion[a], frombuf, mask_max, blend, tilex, tiley, tilew, tileh); } else { Paint2DForeachData data; data.s = s; + data.tile = tile; data.region = ®ion[a]; - data.curveb = curveb; - data.texmaskb = texmaskb; data.frombuf = frombuf; data.mask_max = mask_max; data.blend = blend; @@ -1412,12 +1491,12 @@ static int paint_2d_op(void *state, } else { /* no masking, composite brush directly onto canvas */ - IMB_rectblend_threaded(s->canvas, - s->canvas, + IMB_rectblend_threaded(canvas, + canvas, frombuf, NULL, - curveb, - texmaskb, + tile->cache.curve_mask, + tile->cache.tex_mask, mask_max, region[a].destx, region[a].desty, @@ -1439,47 +1518,25 @@ static int paint_2d_op(void *state, return 1; } -static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) +static int paint_2d_canvas_set(ImagePaintState *s) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - /* verify that we can paint and set canvas */ - if (ima == NULL) { - return 0; - } - else if (BKE_image_has_packedfile(ima) && ima->rr) { - s->warnpackedfile = ima->id.name + 2; - return 0; - } - else if (ibuf && ibuf->channels != 4) { - s->warnmultifile = ima->id.name + 2; - return 0; - } - else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) { - return 0; - } - - s->image = ima; - s->canvas = ibuf; - /* set clone canvas */ if (s->tool == PAINT_TOOL_CLONE) { - ima = s->brush->clone.image; - ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + Image *ima = s->brush->clone.image; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { BKE_image_release_ibuf(ima, ibuf, NULL); - BKE_image_release_ibuf(s->image, s->canvas, NULL); return 0; } s->clonecanvas = ibuf; /* temporarily add float rect for cloning */ - if (s->canvas->rect_float && !s->clonecanvas->rect_float) { + if (s->tiles[0].canvas->rect_float && !s->clonecanvas->rect_float) { IMB_float_from_rect(s->clonecanvas); } - else if (!s->canvas->rect_float && !s->clonecanvas->rect) { + else if (!s->tiles[0].canvas->rect_float && !s->clonecanvas->rect) { IMB_rect_from_float(s->clonecanvas); } } @@ -1492,7 +1549,9 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) static void paint_2d_canvas_free(ImagePaintState *s) { - BKE_image_release_ibuf(s->image, s->canvas, NULL); + for (int i = 0; i < s->num_tiles; i++) { + BKE_image_release_ibuf(s->image, s->tiles[i].canvas, NULL); + } BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); if (s->blurkernel) { @@ -1501,71 +1560,107 @@ static void paint_2d_canvas_free(ImagePaintState *s) } } +static void paint_2d_transform_mouse(ImagePaintState *s, const float in[2], float out[2]) +{ + UI_view2d_region_to_view(s->v2d, in[0], in[1], &out[0], &out[1]); +} + +static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2]) +{ + return (pos[0] >= -brush[0]) && (pos[0] < size[0] + brush[0]) && (pos[1] >= -brush[1]) && + (pos[1] < size[1] + brush[1]); +} + +static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2]) +{ + coord[0] = (uv[0] - tile->uv_origin[0]) * tile->size[0]; + coord[1] = (uv[1] - tile->uv_origin[1]) * tile->size[1]; +} + void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, - float size) + float base_size) { - float newuv[2], olduv[2]; + float new_uv[2], old_uv[2]; ImagePaintState *s = ps; BrushPainter *painter = s->painter; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); - if (!ibuf) { - return; - } + const bool is_data = s->tiles[0].canvas->colormanage_flag & IMB_COLORMANAGE_IS_DATA; s->blend = s->brush->blend; if (eraser) { s->blend = IMB_BLEND_ERASE_ALPHA; } - UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); - UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); - - newuv[0] *= ibuf->x; - newuv[1] *= ibuf->y; - - olduv[0] *= ibuf->x; - olduv[1] *= ibuf->y; + UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &new_uv[0], &new_uv[1]); + UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &old_uv[0], &old_uv[1]); + float last_uv[2], start_uv[2]; + UI_view2d_region_to_view(s->v2d, 0.0f, 0.0f, &start_uv[0], &start_uv[1]); if (painter->firsttouch) { - float startuv[2]; - - UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); - /* paint exactly once on first touch */ - painter->startpaintpos[0] = startuv[0] * ibuf->x; - painter->startpaintpos[1] = startuv[1] * ibuf->y; - - painter->firsttouch = 0; - copy_v2_v2(painter->lastpaintpos, newuv); + copy_v2_v2(last_uv, new_uv); } else { - copy_v2_v2(painter->lastpaintpos, olduv); + copy_v2_v2(last_uv, old_uv); } - /* OCIO_TODO: float buffers are now always linear, so always use color correction - * this should probably be changed when texture painting color space is supported - */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); + float uv_brush_size[2] = {base_size / s->tiles[0].size[0], base_size / s->tiles[0].size[1]}; + + for (int i = 0; i < s->num_tiles; i++) { + ImagePaintTile *tile = &s->tiles[i]; + + /* First test: Project brush into UV space, clip against tile. */ + const int uv_size[2] = {1, 1}; + float local_new_uv[2], local_old_uv[2]; + sub_v2_v2v2(local_new_uv, new_uv, tile->uv_origin); + sub_v2_v2v2(local_old_uv, old_uv, tile->uv_origin); + if (!(is_inside_tile(uv_size, local_new_uv, uv_brush_size) || + is_inside_tile(uv_size, local_old_uv, uv_brush_size))) { + continue; + } + + /* Lazy tile loading to get size in pixels. */ + if (!paint_2d_ensure_tile_canvas(s, i)) { + continue; + } + + float size = base_size * tile->radius_fac; + + float new_coord[2], old_coord[2]; + paint_2d_uv_to_coord(tile, new_uv, new_coord); + paint_2d_uv_to_coord(tile, old_uv, old_coord); + if (painter->firsttouch) { + paint_2d_uv_to_coord(tile, start_uv, tile->start_paintpos); + } + paint_2d_uv_to_coord(tile, last_uv, tile->last_paintpos); + + /* Second check in pixel coordinates. */ + const float pixel_brush_size[] = {size, size}; + if (!(is_inside_tile(tile->size, new_coord, pixel_brush_size) || + is_inside_tile(tile->size, old_coord, pixel_brush_size))) { + continue; + } + + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &tile->iuser, NULL); - brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); + /* OCIO_TODO: float buffers are now always linear, so always use color correction + * this should probably be changed when texture painting color space is supported + */ + brush_painter_2d_require_imbuf( + painter->brush, tile, (ibuf->rect_float != NULL), !is_data, painter->cache_invert); - if (paint_2d_op(s, - painter->cache.ibuf, - painter->cache.curve_mask, - painter->cache.tex_mask, - olduv, - newuv)) { - s->need_redraw = true; + brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size); + + if (paint_2d_op(s, tile, old_coord, new_coord)) + tile->need_redraw = true; } - BKE_image_release_ibuf(s->image, ibuf, NULL); + painter->firsttouch = 0; } void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) @@ -1588,13 +1683,57 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) s->image = s->sima->image; s->symmetry = settings->imapaint.paint.symmetry_flags; - if (!paint_2d_canvas_set(s, s->image)) { - if (s->warnmultifile) { - BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); - } - if (s->warnpackedfile) { - BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); - } + if (s->image == NULL) { + MEM_freeN(s); + return NULL; + } + if (BKE_image_has_packedfile(s->image) && s->image->rr != NULL) { + BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); + MEM_freeN(s); + return 0; + } + + s->num_tiles = BLI_listbase_count(&s->image->tiles); + s->tiles = MEM_callocN(sizeof(ImagePaintTile) * s->num_tiles, "ImagePaintTile"); + for (int i = 0; i < s->num_tiles; i++) { + BKE_imageuser_default(&s->tiles[i].iuser); + } + s->tiles[0].iuser.ok = true; + + zero_v2(s->tiles[0].uv_origin); + + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[0].iuser, NULL); + if (ibuf == NULL) { + MEM_freeN(s->tiles); + MEM_freeN(s); + return NULL; + } + + if (ibuf->channels != 4) { + BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); + MEM_freeN(s->tiles); + MEM_freeN(s); + return NULL; + } + + s->tiles[0].size[0] = ibuf->x; + s->tiles[0].size[1] = ibuf->y; + s->tiles[0].radius_fac = 1.0f; + + s->tiles[0].canvas = ibuf; + s->tiles[0].state = PAINT2D_TILE_READY; + + /* Initialize offsets here, they're needed for the uv space clip test before lazy-loading the + * tile properly. */ + int tile_idx = 0; + for (ImageTile *tile = s->image->tiles.first; tile; tile = tile->next, tile_idx++) { + s->tiles[tile_idx].iuser.tile = tile->tile_number; + s->tiles[tile_idx].uv_origin[0] = ((tile->tile_number - 1001) % 10); + s->tiles[tile_idx].uv_origin[1] = ((tile->tile_number - 1001) / 10); + } + + if (!paint_2d_canvas_set(s)) { + MEM_freeN(s->tiles); MEM_freeN(s); return NULL; @@ -1616,18 +1755,28 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) { ImagePaintState *s = ps; - if (s->need_redraw) { - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + bool had_redraw = false; + for (int i = 0; i < s->num_tiles; i++) { + if (s->tiles[i].need_redraw) { + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); - imapaint_image_update(s->sima, s->image, ibuf, false); - ED_imapaint_clear_partial_redraw(); + imapaint_image_update(s->sima, s->image, ibuf, &s->tiles[i].iuser, false); - BKE_image_release_ibuf(s->image, ibuf, NULL); + BKE_image_release_ibuf(s->image, ibuf, NULL); - s->need_redraw = false; + s->tiles[i].need_redraw = false; + had_redraw = true; + } } - else if (!final) { - return; + + if (had_redraw) { + ED_imapaint_clear_partial_redraw(); + if (s->sima == NULL || !s->sima->lock) { + ED_region_tag_redraw(CTX_wm_region(C)); + } + else { + WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); + } } if (final) { @@ -1639,14 +1788,6 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); DEG_id_tag_update(&s->image->id, 0); } - else { - if (!s->sima || !s->sima->lock) { - ED_region_tag_redraw(CTX_wm_region(C)); - } - else { - WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); - } - } } void paint_2d_stroke_done(void *ps) @@ -1654,7 +1795,11 @@ void paint_2d_stroke_done(void *ps) ImagePaintState *s = ps; paint_2d_canvas_free(s); - brush_painter_2d_free(s->painter); + for (int i = 0; i < s->num_tiles; i++) { + brush_painter_cache_2d_free(&s->tiles[i].cache); + } + MEM_freeN(s->painter); + MEM_freeN(s->tiles); paint_brush_exit_tex(s->brush); MEM_freeN(s); @@ -1713,9 +1858,29 @@ static void paint_2d_fill_add_pixel_float(const int x_px, } } +static ImageUser *paint_2d_get_tile_iuser(ImagePaintState *s, int tile_number) +{ + ImageUser *iuser = &s->tiles[0].iuser; + for (int i = 0; i < s->num_tiles; i++) { + if (s->tiles[i].iuser.tile == tile_number) { + if (!paint_2d_ensure_tile_canvas(s, i)) { + return NULL; + } + iuser = &s->tiles[i].iuser; + break; + } + } + + return iuser; +} + /* this function expects linear space color values */ -void paint_2d_bucket_fill( - const bContext *C, const float color[3], Brush *br, const float mouse_init[2], void *ps) +void paint_2d_bucket_fill(const bContext *C, + const float color[3], + Brush *br, + const float mouse_init[2], + const float mouse_final[2], + void *ps) { SpaceImage *sima = CTX_wm_space_image(C); Image *ima = sima->image; @@ -1734,8 +1899,17 @@ void paint_2d_bucket_fill( return; } - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + float uv_origin[2]; + float image_init[2]; + paint_2d_transform_mouse(s, mouse_init, image_init); + int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin); + ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number); + if (!iuser) { + return; + } + + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (!ibuf) { return; } @@ -1753,9 +1927,9 @@ void paint_2d_bucket_fill( color_f[3] = strength; } - if (!mouse_init || !br) { + if (!mouse_final || !br) { /* first case, no image UV, fill the whole image */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false); if (do_float) { for (x_px = 0; x_px < ibuf->x; x_px++) { @@ -1783,15 +1957,12 @@ void paint_2d_bucket_fill( BLI_bitmap *touched; size_t coordinate; int width = ibuf->x; - float image_init[2]; int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; float pixel_color[4]; /* We are comparing to sum of three squared values * (assumed in range [0,1]), so need to multiply... */ float threshold_sq = br->fill_threshold * br->fill_threshold * 3; - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - x_px = image_init[0] * ibuf->x; y_px = image_init[1] * ibuf->y; @@ -1801,7 +1972,7 @@ void paint_2d_bucket_fill( } /* change image invalidation method later */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false); stack = BLI_stack_new(sizeof(size_t), __func__); touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); @@ -1913,7 +2084,7 @@ void paint_2d_bucket_fill( BLI_stack_free(stack); } - imapaint_image_update(sima, ima, ibuf, false); + imapaint_image_update(sima, ima, ibuf, iuser, false); ED_imapaint_clear_partial_redraw(); BKE_image_release_ibuf(ima, ibuf, NULL); @@ -1938,19 +2109,26 @@ void paint_2d_gradient_fill( bool do_float; - if (!ima) { + if (ima == NULL) { return; } - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + float uv_origin[2]; + int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_origin); + ImageUser *iuser = paint_2d_get_tile_iuser(s, tile_number); + if (!iuser) { + return; + } - if (!ibuf) { + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf == NULL) { return; } - UI_view2d_region_to_view( - s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + paint_2d_transform_mouse(s, mouse_final, image_final); + paint_2d_transform_mouse(s, mouse_init, image_init); + sub_v2_v2(image_init, uv_origin); + sub_v2_v2(image_final, uv_origin); image_final[0] *= ibuf->x; image_final[1] *= ibuf->y; @@ -1967,7 +2145,7 @@ void paint_2d_gradient_fill( do_float = (ibuf->rect_float != NULL); /* this will be substituted by something else when selection is available */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + ED_imapaint_dirty_region(ima, ibuf, tile_number, 0, 0, ibuf->x, ibuf->y, false); if (do_float) { for (x_px = 0; x_px < ibuf->x; x_px++) { @@ -2027,7 +2205,7 @@ void paint_2d_gradient_fill( } } - imapaint_image_update(sima, ima, ibuf, false); + imapaint_image_update(sima, ima, ibuf, iuser, false); ED_imapaint_clear_partial_redraw(); BKE_image_release_ibuf(ima, ibuf, NULL); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index c57490041bc..6a67c469955 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -197,6 +197,7 @@ BLI_INLINE unsigned char f_to_char(const float val) */ typedef struct ProjPaintImage { Image *ima; + ImageUser iuser; ImBuf *ibuf; ImagePaintPartialRedraw *partRedrawRect; /** Only used to build undo tiles during painting. */ @@ -530,6 +531,18 @@ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_ /* Finish projection painting structs */ +static int project_paint_face_paint_tile(Image *ima, const float *uv) +{ + if (ima == NULL || ima->source != IMA_SRC_TILED) { + return 0; + } + + /* Currently, faces are assumed to belong to one tile, so checking the first loop is enough. */ + int tx = (int)uv[0]; + int ty = (int)uv[1]; + return 1001 + 10 * ty + tx; +} + static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index) { const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); @@ -729,9 +742,17 @@ static bool project_paint_PickColor(const ProjPaintState *ps, ima = project_paint_face_paint_image(ps, tri_index); /** we must have got the imbuf before getting here. */ - ibuf = BKE_image_get_first_ibuf(ima); - if (!ibuf) { - return 0; + int tile_number = project_paint_face_paint_tile(ima, lt_tri_uv[0]); + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile_number; + ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (ibuf == NULL) { + iuser.tile = 0; + ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (ibuf == NULL) { + return 0; + } } if (interp) { @@ -1154,6 +1175,8 @@ static bool check_seam(const ProjPaintState *ps, const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; Image *tpage = project_paint_face_paint_image(ps, tri_index); Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); + int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]); + int orig_tile = project_paint_face_paint_tile(orig_tpage, orig_lt_tri_uv[0]); BLI_assert(i1_fidx != -1); @@ -1171,7 +1194,8 @@ static bool check_seam(const ProjPaintState *ps, } /* first test if they have the same image */ - if ((orig_tpage == tpage) && cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && + if ((orig_tpage == tpage) && (orig_tile == tile) && + cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) { /* if faces don't have the same winding in uv space, * they are on the same side so edge is boundary */ @@ -1817,6 +1841,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) pjIma->ima, pjIma->ibuf, tinf->tmpibuf, + pjIma->iuser.tile, tx, ty, &pjIma->maskRect[tile_index], @@ -1829,6 +1854,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) pjIma->ima, pjIma->ibuf, tinf->tmpibuf, + pjIma->iuser.tile, tx, ty, NULL, @@ -3486,6 +3512,7 @@ static void project_bucket_init(const ProjPaintState *ps, ImBuf *ibuf = NULL; Image *tpage_last = NULL, *tpage; ImBuf *tmpibuf = NULL; + int tile_last = 0; if (ps->image_tot == 1) { /* Simple loop, no context switching */ @@ -3509,17 +3536,34 @@ static void project_bucket_init(const ProjPaintState *ps, for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { tri_index = POINTER_AS_INT(node->link); + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + /* Image context switching */ tpage = project_paint_face_paint_image(ps, tri_index); - if (tpage_last != tpage) { + int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]); + if (tpage_last != tpage || tile_last != tile) { tpage_last = tpage; + tile_last = tile; + ibuf = NULL; for (image_index = 0; image_index < ps->image_tot; image_index++) { - if (ps->projImages[image_index].ima == tpage_last) { - ibuf = ps->projImages[image_index].ibuf; + ProjPaintImage *projIma = &ps->projImages[image_index]; + if ((projIma->ima == tpage) && (projIma->iuser.tile == tile)) { + ibuf = projIma->ibuf; break; } } + if (ibuf == NULL) { + /* Failed to find the specific tile, fall back to the primary tile. */ + for (image_index = 0; image_index < ps->image_tot; image_index++) { + ProjPaintImage *projIma = &ps->projImages[image_index]; + if ((projIma->ima == tpage) && (projIma->iuser.tile == 0)) { + ibuf = projIma->ibuf; + break; + } + } + } } /* context switching done */ @@ -4232,22 +4276,36 @@ static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceC } #endif // PROJ_DEBUG_WINCLIP +typedef struct PrepareImageEntry { + struct PrepareImageEntry *next, *prev; + Image *ima; + int tile; +} PrepareImageEntry; + static void project_paint_build_proj_ima(ProjPaintState *ps, MemArena *arena, - LinkNode *image_LinkList) + ListBase *used_images) { ProjPaintImage *projIma; - LinkNode *node; + PrepareImageEntry *entry; int i; /* build an array of images we use */ projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); - for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + for (entry = used_images->first, i = 0; entry; entry = entry->next, i++, projIma++) { + memset(&projIma->iuser, 0, sizeof(ImageUser)); + BKE_imageuser_default(&projIma->iuser); + projIma->iuser.tile = entry->tile; int size; - projIma->ima = node->link; + projIma->ima = entry->ima; projIma->touch = 0; - projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL); + if (projIma->ibuf == NULL) { + projIma->iuser.tile = 0; + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL); + BLI_assert(projIma->ibuf != NULL); + } size = sizeof(void **) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->x) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->y); projIma->partRedrawRect = BLI_memarena_alloc( @@ -4270,15 +4328,18 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, const bool is_multi_view) { /* Image Vars - keep track of images we have used */ - LinkNodePair image_LinkList = {NULL, NULL}; + ListBase used_images = {NULL}; Image *tpage_last = NULL, *tpage; TexPaintSlot *slot_last = NULL; TexPaintSlot *slot = NULL; + int tile_last = -1, tile; const MLoopTri *lt; int image_index = -1, tri_index; int prev_poly = -1; + BLI_assert(ps->image_tot == 0); + for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { bool is_face_sel; bool skip_tri = false; @@ -4321,6 +4382,8 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, ps->poly_to_loop_uv[lt->poly] = mloopuv_base; + tile = project_paint_face_paint_tile(tpage, mloopuv_base[lt->tri[0]].uv); + #ifndef PROJ_DEBUG_NOSEAMBLEED project_paint_bleed_add_face_user(ps, arena, lt, tri_index); #endif @@ -4382,18 +4445,24 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, } } - if (tpage_last != tpage) { - - image_index = BLI_linklist_index(image_LinkList.list, tpage); + if (tpage_last != tpage || tile_last != tile) { + image_index = 0; + for (PrepareImageEntry *e = used_images.first; e; e = e->next, image_index++) { + if (e->ima == tpage && e->tile == tile) { + break; + } + } - if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { - /* MemArena doesn't have an append func */ - BLI_linklist_append(&image_LinkList, tpage); - image_index = ps->image_tot; + if (image_index == ps->image_tot) { + PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry"); + e->ima = tpage; + e->tile = tile; + BLI_addtail(&used_images, e); ps->image_tot++; } tpage_last = tpage; + tile_last = tile; } if (image_index != -1) { @@ -4406,11 +4475,11 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, /* build an array of images we use*/ if (ps->is_shared_user == false) { - project_paint_build_proj_ima(ps, arena, image_LinkList.list); + project_paint_build_proj_ima(ps, arena, &used_images); } /* we have built the array, discard the linked list */ - BLI_linklist_free(image_LinkList.list, NULL); + BLI_freelistN(&used_images); } /* run once per stroke before projection painting */ @@ -4675,7 +4744,7 @@ static bool project_image_refresh_tagged(ProjPaintState *ps) pr = &(projIma->partRedrawRect[i]); if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ set_imapaintpartial(pr); - imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); + imapaint_image_update(NULL, projIma->ima, projIma->ibuf, &projIma->iuser, true); redraw = 1; } @@ -6441,7 +6510,8 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) gen_type, color, false, - is_data); + is_data, + false); /* TODO(lukas): Add option */ return ima; } diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 84665728e17..53beb981522 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -186,6 +186,7 @@ bool image_texture_paint_poll(struct bContext *C); void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, + struct ImageUser *iuser, short texpaint); struct ImagePaintPartialRedraw *get_imapaintpartial(void); void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr); @@ -206,6 +207,7 @@ void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], + const float mouse_final[2], void *ps); void paint_2d_gradient_fill(const struct bContext *C, struct Brush *br, |