diff options
-rw-r--r-- | source/blender/blenkernel/BKE_brush.h | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_paint.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 44 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 47 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_2d.c | 561 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_proj.c | 667 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 26 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 6 |
8 files changed, 642 insertions, 721 deletions
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index cfae15961d7..95c39bad344 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -79,13 +79,13 @@ struct BrushPainter; typedef struct BrushPainter BrushPainter; typedef int (*BrushFunc)(void *user, struct ImBuf *ibuf, const float lastpos[2], const float pos[2]); -BrushPainter *BKE_brush_painter_new(struct Scene *scene, struct Brush *brush); -void BKE_brush_painter_require_imbuf(BrushPainter *painter, short flt, +BrushPainter *brush_painter_2d_new(struct Scene *scene, struct Brush *brush); +void brush_painter_2d_require_imbuf(BrushPainter *painter, short flt, short texonly, int size); -int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float pos[2], +int brush_painter_2d_paint(BrushPainter *painter, BrushFunc func, const float pos[2], double time, float pressure, void *user, int use_color_correction); -void BKE_brush_painter_break_stroke(BrushPainter *painter); -void BKE_brush_painter_free(BrushPainter *painter); +void brush_painter_2d_break_stroke(BrushPainter *painter); +void brush_painter_2d_free(BrushPainter *painter); /* texture */ unsigned int *BKE_brush_gen_texture_cache(struct Brush *br, int half_side); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 98184bfa65d..c46bc1dada6 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -54,7 +54,7 @@ extern const char PAINT_CURSOR_VERTEX_PAINT[3]; extern const char PAINT_CURSOR_WEIGHT_PAINT[3]; extern const char PAINT_CURSOR_TEXTURE_PAINT[3]; -typedef enum { +typedef enum PaintMode{ PAINT_SCULPT, PAINT_VERTEX, PAINT_WEIGHT, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index ba69fa4336e..798dde9f985 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -53,6 +53,7 @@ #include "IMB_imbuf_types.h" #include "RE_render_ext.h" /* externtex */ +#include "RE_shader_ext.h" static void brush_defaults(Brush *brush) { @@ -841,6 +842,49 @@ float BKE_brush_curve_strength(Brush *br, float p, const float len) return curvemapping_evaluateF(br->curve, 0, p); } +/* TODO: should probably be unified with BrushPainter stuff? */ +unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side) +{ + unsigned int *texcache = NULL; + MTex *mtex = &br->mtex; + TexResult texres = {0}; + int hasrgb, ix, iy; + int side = half_side * 2; + + if (mtex->tex) { + float x, y, step = 2.0 / side, co[3]; + + texcache = MEM_callocN(sizeof(int) * side * side, "Brush texture cache"); + + /*do normalized cannonical view coords for texture*/ + for (y = -1.0, iy = 0; iy < side; iy++, y += step) { + for (x = -1.0, ix = 0; ix < side; ix++, x += step) { + co[0] = x; + co[1] = y; + co[2] = 0.0f; + + /* This is copied from displace modifier code */ + hasrgb = multitex_ext(mtex->tex, co, NULL, NULL, 0, &texres, NULL); + + /* if the texture gave an RGB value, we assume it didn't give a valid + * intensity, so calculate one (formula from do_material_tex). + * if the texture didn't give an RGB value, copy the intensity across + */ + if (hasrgb & TEX_RGB) + texres.tin = rgb_to_grayscale(&texres.tr); + + ((char *)texcache)[(iy * side + ix) * 4] = + ((char *)texcache)[(iy * side + ix) * 4 + 1] = + ((char *)texcache)[(iy * side + ix) * 4 + 2] = + ((char *)texcache)[(iy * side + ix) * 4 + 3] = (char)(texres.tin * 255.0f); + } + } + } + + return texcache; +} + + /**** Radial Control ****/ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br) { diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 63a111c9165..87f08207d6c 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -140,9 +140,6 @@ BLI_INLINE unsigned char f_to_char(const float val) #define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) #define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint); - - typedef struct ImagePaintState { SpaceImage *sima; View2D *v2d; @@ -171,11 +168,6 @@ typedef struct ImagePaintState { MTFace *dm_mtface; } ImagePaintState; -typedef struct ImagePaintPartialRedraw { - int x1, y1, x2, y2; /* XXX, could use 'rcti' */ - int enabled; -} ImagePaintPartialRedraw; - typedef struct ImagePaintRegion { int destx, desty; int srcx, srcy; @@ -411,8 +403,19 @@ typedef struct UndoImageTile { char gen_type; } 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}; +ImagePaintPartialRedraw *get_imapaintpartial(void) { + return &imapaintpartial; +} + +void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) { + imapaintpartial = *ippr; +} + /* UNDO */ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) @@ -433,7 +436,7 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); } -static void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -4349,7 +4352,7 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, c // we may want to use this later // BKE_brush_painter_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); - if (BKE_brush_painter_paint(painter, project_paint_op, pos, time, pressure, ps, 0)) { + if (brush_painter_2d_paint(painter, project_paint_op, pos, time, pressure, ps, 0)) { return 1; } else return 0; @@ -4373,12 +4376,12 @@ static int project_paint_stroke(ProjPaintState *ps, BrushPainter *painter, const /* Imagepaint Partial Redraw & Dirty Region */ -static void imapaint_clear_partial_redraw(void) +void imapaint_clear_partial_redraw(void) { memset(&imapaintpartial, 0, sizeof(imapaintpartial)); } -static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) +void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) { ImBuf *tmpibuf = NULL; int srcx = 0, srcy = 0, origx; @@ -4417,7 +4420,7 @@ static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, IMB_freeImBuf(tmpibuf); } -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) +void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) { if (imapaintpartial.x1 != imapaintpartial.x2 && imapaintpartial.y1 != imapaintpartial.y2) @@ -4717,7 +4720,7 @@ static int texpaint_break_stroke(float *prevuv, float *fwuv, float *bkuv, float static int imapaint_canvas_set(ImagePaintState *s, Image *ima) { 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; @@ -4740,7 +4743,7 @@ static int imapaint_canvas_set(ImagePaintState *s, Image *ima) if (s->tool == PAINT_TOOL_CLONE) { ima = s->brush->clone.image; ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : 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); @@ -4780,12 +4783,12 @@ static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter, pos[0] = uv[0] * ibuf->x; pos[1] = uv[1] * ibuf->y; - BKE_brush_painter_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); + brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); /* 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 */ - if (BKE_brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s, is_data == FALSE)) { + if (brush_painter_2d_paint(painter, imapaint_paint_op, pos, time, pressure, s, is_data == FALSE)) { if (update) imapaint_image_update(s->sima, image, ibuf, texpaint); BKE_image_release_ibuf(image, ibuf, NULL); @@ -4844,7 +4847,7 @@ static int imapaint_paint_stroke(ViewContext *vc, ImagePaintState *s, BrushPaint redraw |= imapaint_paint_sub_stroke(s, painter, s->image, texpaint, fwuv, time, 1, pressure); imapaint_clear_partial_redraw(); - BKE_brush_painter_break_stroke(painter); + brush_painter_2d_break_stroke(painter); } /* set new canvas */ @@ -5044,7 +5047,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps) ps->do_mask_normal = FALSE; /* no need to do blending */ } -static void paint_brush_init_tex(Brush *brush) +void paint_brush_init_tex(Brush *brush) { /* init mtex nodes */ if (brush) { @@ -5167,7 +5170,7 @@ static int texture_paint_init(bContext *C, wmOperator *op) image_undo_restore, image_undo_free); /* create painter */ - pop->painter = BKE_brush_painter_new(scene, pop->s.brush); + pop->painter = brush_painter_2d_new(scene, pop->s.brush); { UnifiedPaintSettings *ups = &settings->unified_paint_settings; @@ -5214,7 +5217,7 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) pop->first = 0; } -static void paint_brush_exit_tex(Brush *brush) +void paint_brush_exit_tex(Brush *brush) { if (brush) { MTex *mtex = &brush->mtex; @@ -5234,7 +5237,7 @@ static void paint_exit(bContext *C, wmOperator *op) settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; imapaint_canvas_free(&pop->s); - BKE_brush_painter_free(pop->painter); + brush_painter_2d_free(pop->painter); if (pop->mode == PAINT_MODE_3D_PROJECT) { BKE_brush_size_set(scene, pop->ps.brush, pop->orig_brush_size); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 84250853f38..194b36ac099 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -35,17 +35,84 @@ #include "DNA_brush_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_object_types.h" +#include "BKE_context.h" #include "BKE_brush.h" +#include "BKE_main.h" +#include "BKE_image.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "ED_screen.h" #include "BLI_math.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" #include "RE_shader_ext.h" - /* Brush Painting for 2D image editor */ +#include "GPU_draw.h" + +#include "paint_intern.h" + +/* Brush Painting for 2D image editor */ + +/* Defines and Structs */ +/* FTOCHAR as inline function */ +BLI_INLINE unsigned char f_to_char(const float val) +{ + return FTOCHAR(val); +} +#define IMAPAINT_FLOAT_RGB_TO_CHAR(c, f) { \ + (c)[0] = f_to_char((f)[0]); \ + (c)[1] = f_to_char((f)[1]); \ + (c)[2] = f_to_char((f)[2]); \ +} (void)0 + +#define IMAPAINT_CHAR_RGB_TO_FLOAT(f, c) { \ + (f)[0] = IMAPAINT_CHAR_TO_FLOAT((c)[0]); \ + (f)[1] = IMAPAINT_CHAR_TO_FLOAT((c)[1]); \ + (f)[2] = IMAPAINT_CHAR_TO_FLOAT((c)[2]); \ +} (void)0 + +#define IMAPAINT_FLOAT_RGB_COPY(a, b) copy_v3_v3(a, b) + +typedef struct ImagePaintRegion { + int destx, desty; + int srcx, srcy; + int width, height; +} ImagePaintRegion; + +typedef struct ImagePaintState { + BrushPainter *painter; + SpaceImage *sima; + View2D *v2d; + Scene *scene; + bScreen *screen; + + Brush *brush; + short tool, blend; + Image *image; + ImBuf *canvas; + ImBuf *clonecanvas; + char *warnpackedfile; + char *warnmultifile; + + /* viewport texture paint only, but _not_ project paint */ + Object *ob; + int faceindex; + float uv[2]; + int do_facesel; +} ImagePaintState; typedef struct BrushPainterCache { short enabled; @@ -88,7 +155,7 @@ struct BrushPainter { BrushPainterCache cache; }; -BrushPainter *BKE_brush_painter_new(Scene *scene, Brush *brush) +BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush) { BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); @@ -119,7 +186,7 @@ static void brush_pressure_apply(BrushPainter *painter, Brush *brush, float pres } -void BKE_brush_painter_require_imbuf(BrushPainter *painter, short flt, short texonly, int size) +void brush_painter_2d_require_imbuf(BrushPainter *painter, short flt, short texonly, int size) { if ((painter->cache.flt != flt) || (painter->cache.size != size) || ((painter->cache.texonly != texonly) && texonly)) @@ -142,7 +209,7 @@ void BKE_brush_painter_require_imbuf(BrushPainter *painter, short flt, short tex painter->cache.enabled = 1; } -void BKE_brush_painter_free(BrushPainter *painter) +void brush_painter_2d_free(BrushPainter *painter) { Brush *brush = painter->brush; @@ -157,7 +224,7 @@ void BKE_brush_painter_free(BrushPainter *painter) MEM_freeN(painter); } -static void brush_painter_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, +static void brush_painter_2d_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, int x, int y, int w, int h, int xt, int yt, const float pos[2]) { @@ -248,7 +315,7 @@ static void brush_painter_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, } } -static void brush_painter_tiled_tex_partial_update(BrushPainter *painter, const float pos[2]) +static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, const float pos[2]) { const Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -286,23 +353,23 @@ static void brush_painter_tiled_tex_partial_update(BrushPainter *painter, const /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) - brush_painter_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos); + brush_painter_2d_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos); if (oldtexibuf) IMB_freeImBuf(oldtexibuf); /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) - brush_painter_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos); + brush_painter_2d_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos); if ((x2 < ibuf->x) && (0 < ibuf->y)) - brush_painter_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos); + brush_painter_2d_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos); if ((x1 < x2) && (0 < y1)) - brush_painter_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos); + brush_painter_2d_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos); if ((x1 < x2) && (y2 < ibuf->y)) - brush_painter_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); + brush_painter_2d_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); } -static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2], int use_color_correction) +static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float pos[2], int use_color_correction) { const Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -332,7 +399,7 @@ static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2 if (do_tiled) { BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, use_color_correction); - brush_painter_tiled_tex_partial_update(painter, pos); + brush_painter_2d_tiled_tex_partial_update(painter, pos); } else BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf, use_color_correction); @@ -346,17 +413,17 @@ static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2 int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; if ((dx != 0) || (dy != 0)) - brush_painter_tiled_tex_partial_update(painter, pos); + brush_painter_2d_tiled_tex_partial_update(painter, pos); } } -void BKE_brush_painter_break_stroke(BrushPainter *painter) +void brush_painter_2d_break_stroke(BrushPainter *painter) { painter->firsttouch = 1; } -int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float pos[2], double time, float pressure, +int brush_painter_2d_paint(BrushPainter *painter, BrushFunc func, const float pos[2], double time, float pressure, void *user, int use_color_correction) { Scene *scene = painter->scene; @@ -376,7 +443,7 @@ int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float p brush_pressure_apply(painter, brush, pressure); if (painter->cache.enabled) - brush_painter_refresh_cache(painter, pos, use_color_correction); + brush_painter_2d_refresh_cache(painter, pos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, pos); painter->lasttime = time; @@ -449,7 +516,7 @@ int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float p BKE_brush_jitter_pos(scene, brush, paintpos, finalpos); if (painter->cache.enabled) - brush_painter_refresh_cache(painter, finalpos, use_color_correction); + brush_painter_2d_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, finalpos); @@ -464,7 +531,7 @@ int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float p BKE_brush_jitter_pos(scene, brush, pos, finalpos); if (painter->cache.enabled) - brush_painter_refresh_cache(painter, finalpos, use_color_correction); + brush_painter_2d_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, finalpos); @@ -492,7 +559,7 @@ int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float p BKE_brush_jitter_pos(scene, brush, pos, finalpos); if (painter->cache.enabled) - brush_painter_refresh_cache(painter, finalpos, use_color_correction); + brush_painter_2d_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastmousepos, finalpos); @@ -515,46 +582,428 @@ int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float p return totpaintops; } +/* Image Paint Operations */ -/* TODO: should probably be unified with BrushPainter stuff? */ -unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side) +/* keep these functions in sync */ +static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, const short is_torus, float r_rgb[3]) { - unsigned int *texcache = NULL; - MTex *mtex = &br->mtex; - TexResult texres = {0}; - int hasrgb, ix, iy; - int side = half_side * 2; - - if (mtex->tex) { - float x, y, step = 2.0 / side, co[3]; - - texcache = MEM_callocN(sizeof(int) * side * side, "Brush texture cache"); - - /*do normalized cannonical view coords for texture*/ - for (y = -1.0, iy = 0; iy < side; iy++, y += step) { - for (x = -1.0, ix = 0; ix < side; ix++, x += step) { - co[0] = x; - co[1] = y; - co[2] = 0.0f; - - /* This is copied from displace modifier code */ - hasrgb = multitex_ext(mtex->tex, co, NULL, NULL, 0, &texres, NULL); - - /* if the texture gave an RGB value, we assume it didn't give a valid - * intensity, so calculate one (formula from do_material_tex). - * if the texture didn't give an RGB value, copy the intensity across - */ - if (hasrgb & TEX_RGB) - texres.tin = rgb_to_grayscale(&texres.tr); - - ((char *)texcache)[(iy * side + ix) * 4] = - ((char *)texcache)[(iy * side + ix) * 4 + 1] = - ((char *)texcache)[(iy * side + ix) * 4 + 2] = - ((char *)texcache)[(iy * side + ix) * 4 + 3] = (char)(texres.tin * 255.0f); - } + if (is_torus) { + x %= ibuf->x; + if (x < 0) x += ibuf->x; + y %= ibuf->y; + if (y < 0) y += ibuf->y; + } + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + IMAPAINT_FLOAT_RGB_COPY(r_rgb, rrgbf); + } + else { + char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4; + IMAPAINT_CHAR_RGB_TO_FLOAT(r_rgb, rrgb); + } +} +static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const short is_torus, const float rgb[3]) +{ + if (is_torus) { + x %= ibuf->x; + if (x < 0) x += ibuf->x; + y %= ibuf->y; + if (y < 0) y += ibuf->y; + } + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + IMAPAINT_FLOAT_RGB_COPY(rrgbf, rgb); + } + else { + char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4; + IMAPAINT_FLOAT_RGB_TO_CHAR(rrgb, rgb); + } +} + +static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus) +{ + float inrgb[3]; + + // XXX: signed unsigned mismatch + if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) { + if (torus) paint_2d_ibuf_rgb_get(ibuf, x, y, 1, inrgb); + else return 0; + } + else { + paint_2d_ibuf_rgb_get(ibuf, x, y, 0, inrgb); + } + + outrgb[0] += inrgb[0]; + outrgb[1] += inrgb[1]; + outrgb[2] += inrgb[2]; + + return 1; +} + +static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const short is_torus) +{ + int x, y, count, xi, yi, xo, yo; + int out_off[2], in_off[2], dim[2]; + float outrgb[3]; + + dim[0] = ibufb->x; + dim[1] = ibufb->y; + in_off[0] = pos[0]; + in_off[1] = pos[1]; + out_off[0] = out_off[1] = 0; + + if (!is_torus) { + 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)) + return; + } + + for (y = 0; y < dim[1]; y++) { + for (x = 0; x < dim[0]; x++) { + /* get input pixel */ + xi = in_off[0] + x; + yi = in_off[1] + y; + + count = 1; + paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); + + count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); + count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); + count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); + + count += paint_2d_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); + count += paint_2d_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); + + count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); + count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); + count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); + + mul_v3_fl(outrgb, 1.0f / (float)count); + + /* write into brush buffer */ + xo = out_off[0] + x; + yo = out_off[1] + y; + paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); + } + } +} + +static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) +{ + region->destx = destx; + region->desty = desty; + region->srcx = srcx; + region->srcy = srcy; + region->width = width; + region->height = height; +} + +static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf) +{ + int destx = region->destx; + int desty = region->desty; + int srcx = region->srcx; + int srcy = region->srcy; + int width = region->width; + int height = region->height; + int origw, origh, w, h, tot = 0; + + /* convert destination and source coordinates to be within image */ + destx = destx % dbuf->x; + if (destx < 0) destx += dbuf->x; + desty = desty % dbuf->y; + if (desty < 0) desty += dbuf->y; + srcx = srcx % sbuf->x; + if (srcx < 0) srcx += sbuf->x; + srcy = srcy % sbuf->y; + if (srcy < 0) srcy += sbuf->y; + + /* clip width of blending area to destination imbuf, to avoid writing the + * same pixel twice */ + origw = w = (width > dbuf->x) ? dbuf->x : width; + origh = h = (height > dbuf->y) ? dbuf->y : height; + + /* clip within image */ + IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); + paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); + + /* do 3 other rects if needed */ + if (w < origw) + paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); + if (h < origh) + paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); + if ((w < origw) && (h < origh)) + paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); + + return tot; +} + +static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos) +{ + 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); + + for (a = 0; a < tot; a++) + IMB_rectblend(ibufb, ibuf, region[a].destx, region[a].desty, + region[a].srcx, region[a].srcy, + region[a].width, region[a].height, IMB_BLEND_COPY_RGB); +} + +static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) +{ + /* note: allocImbuf returns zero'd memory, so regions outside image will + * have zero alpha, and hence not be blended onto the image */ + int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; + ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); + + IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); + IMB_rectblend(clonebuf, ibuf, destx, desty, srcx, srcy, w, h, + IMB_BLEND_COPY_RGB); + IMB_rectblend(clonebuf, ibufb, destx, desty, destx, desty, w, h, + IMB_BLEND_COPY_ALPHA); + + return clonebuf; +} + +static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) +{ + ipos[0] = (int)floorf((pos[0] - ibufb->x / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); +} + +static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const float pos[2]) +{ + ImagePaintState *s = ((ImagePaintState *)state); + ImBuf *clonebuf = NULL, *frombuf; + ImagePaintRegion region[4]; + short torus = s->brush->flag & BRUSH_TORUS; + short blend = s->blend; + float *offset = s->brush->clone.offset; + float liftpos[2]; + int bpos[2], blastpos[2], bliftpos[2]; + int a, tot; + + paint_2d_convert_brushco(ibufb, pos, bpos); + + /* lift from canvas */ + if (s->tool == PAINT_TOOL_SOFTEN) { + paint_2d_lift_soften(s->canvas, ibufb, bpos, torus); + } + else if (s->tool == PAINT_TOOL_SMEAR) { + if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) + return 0; + + paint_2d_convert_brushco(ibufb, lastpos, blastpos); + paint_2d_lift_smear(s->canvas, ibufb, blastpos); + } + 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; + + paint_2d_convert_brushco(ibufb, liftpos, bliftpos); + clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); + } + + frombuf = (clonebuf) ? clonebuf : ibufb; + + if (torus) { + 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); + } + else { + paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); + tot = 1; + } + + /* blend into canvas */ + for (a = 0; a < tot; a++) { + imapaint_dirty_region(s->image, s->canvas, + region[a].destx, region[a].desty, + region[a].width, region[a].height); + + IMB_rectblend(s->canvas, frombuf, + region[a].destx, region[a].desty, + region[a].srcx, region[a].srcy, + region[a].width, region[a].height, blend); + } + + if (clonebuf) IMB_freeImBuf(clonebuf); + + return 1; +} + + +static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) +{ + 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 (ima->packedfile && 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); + + 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) { + IMB_float_from_rect(s->clonecanvas); + } + else if (!s->canvas->rect_float && !s->clonecanvas->rect) + IMB_rect_from_float(s->clonecanvas); + } + + return 1; +} + +static void paint_2d_canvas_free(ImagePaintState *s) +{ + BKE_image_release_ibuf(s->image, s->canvas, NULL); + BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); +} + +static int paint_2d_sub_stroke(ImagePaintState *s, BrushPainter *painter, Image *image, float *uv, int update, float pressure) +{ + ImBuf *ibuf = BKE_image_acquire_ibuf(image, s->sima ? &s->sima->iuser : NULL, NULL); + float pos[2]; + int is_data; + + if (!ibuf) + return 0; + + is_data = ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA; + + pos[0] = uv[0] * ibuf->x; + pos[1] = uv[1] * ibuf->y; + + brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); + + /* 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 + */ + if (brush_painter_2d_paint(painter, paint_2d_op, pos, 0, pressure, s, is_data == FALSE)) { + if (update) + imapaint_image_update(s->sima, image, ibuf, false); + BKE_image_release_ibuf(image, ibuf, NULL); + return 1; } + else { + BKE_image_release_ibuf(image, ibuf, NULL); + return 0; + } +} + +int paint_2d_stroke(void *ps, const int mval[2], float pressure, int eraser) +{ + float newuv[2]; + int redraw = 0; + ImagePaintState *s = ps; + BrushPainter *painter = s->painter; - return texcache; + 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]); + redraw |= paint_2d_sub_stroke(s, painter, s->image, newuv, + 1, pressure); + + if (redraw) + imapaint_clear_partial_redraw(); + + return redraw; } +void *paint_2d_new_stroke(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + Brush *brush = paint_brush(&settings->imapaint.paint); + + ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState"); + + s->sima = CTX_wm_space_image(C); + s->v2d = &CTX_wm_region(C)->v2d; + s->scene = scene; + s->screen = CTX_wm_screen(C); + + s->brush = brush; + s->tool = brush->imagepaint_tool; + s->blend = brush->blend; + + s->image = s->sima->image; + + 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"); + + MEM_freeN(s); + return NULL; + } + + paint_brush_init_tex(s->brush); + + /* create painter */ + s->painter = brush_painter_2d_new(scene, s->brush); + + return s; +} + +void paint_2d_redraw (const bContext *C, void *ps, int final) { + ImagePaintState *s = ps; + + if (final) { + if (s->image && !(s->sima && s->sima->lock)) + GPU_free_image(s->image); + + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + } + else { + if (!s->sima || !s->sima->lock) + ED_region_tag_redraw(CTX_wm_region(C)); + else + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + } +} + +void paint_2d_stroke_done(void *ps) { + ImagePaintState *s = ps; + + paint_2d_canvas_free(s); + brush_painter_2d_free(s->painter); + paint_brush_exit_tex(s->brush); + + MEM_freeN(s); +} diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 69383d78614..e594bf25d0c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -108,25 +108,13 @@ BLI_INLINE unsigned char f_to_char(const float val) return FTOCHAR(val); } - -#define IMAPAINT_CHAR_TO_FLOAT(c) ((c) / 255.0f) - -#define IMAPAINT_FLOAT_RGB_TO_CHAR(c, f) { \ - (c)[0] = f_to_char((f)[0]); \ - (c)[1] = f_to_char((f)[1]); \ - (c)[2] = f_to_char((f)[2]); \ -} (void)0 #define IMAPAINT_FLOAT_RGBA_TO_CHAR(c, f) { \ (c)[0] = f_to_char((f)[0]); \ (c)[1] = f_to_char((f)[1]); \ (c)[2] = f_to_char((f)[2]); \ (c)[3] = f_to_char((f)[3]); \ } (void)0 -#define IMAPAINT_CHAR_RGB_TO_FLOAT(f, c) { \ - (f)[0] = IMAPAINT_CHAR_TO_FLOAT((c)[0]); \ - (f)[1] = IMAPAINT_CHAR_TO_FLOAT((c)[1]); \ - (f)[2] = IMAPAINT_CHAR_TO_FLOAT((c)[2]); \ -} (void)0 + #define IMAPAINT_CHAR_RGBA_TO_FLOAT(f, c) { \ (f)[0] = IMAPAINT_CHAR_TO_FLOAT((c)[0]); \ (f)[1] = IMAPAINT_CHAR_TO_FLOAT((c)[1]); \ @@ -134,53 +122,11 @@ BLI_INLINE unsigned char f_to_char(const float val) (f)[3] = IMAPAINT_CHAR_TO_FLOAT((c)[3]); \ } (void)0 -#define IMAPAINT_FLOAT_RGB_COPY(a, b) copy_v3_v3(a, b) - -#define IMAPAINT_TILE_BITS 6 -#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) -#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) - -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint); - - -typedef struct ImagePaintState { - SpaceImage *sima; - View2D *v2d; - Scene *scene; - bScreen *screen; - - Brush *brush; - short tool, blend; - Image *image; - ImBuf *canvas; - ImBuf *clonecanvas; - char *warnpackedfile; - char *warnmultifile; - - /* viewport texture paint only, but _not_ project paint */ - Object *ob; - int faceindex; - float uv[2]; - int do_facesel; - - DerivedMesh *dm; - int dm_totface; - int dm_release; - - MFace *dm_mface; - MTFace *dm_mtface; -} ImagePaintState; - -typedef struct ImagePaintPartialRedraw { - int x1, y1, x2, y2; /* XXX, could use 'rcti' */ - int enabled; -} ImagePaintPartialRedraw; - -typedef struct ImagePaintRegion { - int destx, desty; - int srcx, srcy; - int width, height; -} ImagePaintRegion; +#define IMAPAINT_FLOAT_RGB_TO_CHAR(c, f) { \ + (c)[0] = f_to_char((f)[0]); \ + (c)[1] = f_to_char((f)[1]); \ + (c)[2] = f_to_char((f)[2]); \ +} (void)0 /* ProjectionPaint defines */ @@ -411,8 +357,6 @@ typedef struct UndoImageTile { char gen_type; } UndoImageTile; -static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; - /* UNDO */ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) @@ -433,45 +377,6 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); } -static void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) -{ - ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); - UndoImageTile *tile; - int allocsize; - 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 (strcmp(tile->idname, ima->id.name) == 0 && strcmp(tile->ibufname, ibuf->name) == 0) - return tile->rect.pt; - - 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; - - 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; - - undo_copy_tile(tile, *tmpibuf, ibuf, 0); - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); - - BLI_addtail(lb, tile); - - return tile->rect.pt; -} - static void image_undo_restore(bContext *C, ListBase *lb) { Main *bmain = CTX_data_main(C); @@ -3637,7 +3542,7 @@ static int project_image_refresh_tagged(ProjPaintState *ps) for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) { pr = &(projIma->partRedrawRect[i]); if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ - imapaintpartial = *pr; + set_imapaintpartial(pr); imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); redraw = 1; } @@ -4319,7 +4224,7 @@ static int project_paint_op(void *state, const float lastpos[2], const float pos } -static int project_paint_stroke(ProjPaintState *ps, const int prevmval_i[2], const int mval_i[2], float UNUSED(pressure)) +static int project_paint_stroke(ProjPaintState *ps, const int prevmval_i[2], const int mval_i[2]) { int a, redraw; float pos[2], prev_pos[2]; @@ -4343,428 +4248,6 @@ static int project_paint_stroke(ProjPaintState *ps, const int prevmval_i[2], con /* Imagepaint Partial Redraw & Dirty Region */ -static void imapaint_clear_partial_redraw(void) -{ - memset(&imapaintpartial, 0, sizeof(imapaintpartial)); -} - -static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) -{ - ImBuf *tmpibuf = NULL; - int srcx = 0, srcy = 0, origx; - - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); - - if (w == 0 || h == 0) - return; - - if (!imapaintpartial.enabled) { - imapaintpartial.x1 = x; - imapaintpartial.y1 = y; - imapaintpartial.x2 = x + w; - imapaintpartial.y2 = y + h; - imapaintpartial.enabled = 1; - } - else { - imapaintpartial.x1 = min_ii(imapaintpartial.x1, x); - imapaintpartial.y1 = min_ii(imapaintpartial.y1, y); - imapaintpartial.x2 = max_ii(imapaintpartial.x2, x + w); - imapaintpartial.y2 = max_ii(imapaintpartial.y2, y + h); - } - - w = ((x + w - 1) >> IMAPAINT_TILE_BITS); - h = ((y + h - 1) >> IMAPAINT_TILE_BITS); - origx = (x >> IMAPAINT_TILE_BITS); - y = (y >> IMAPAINT_TILE_BITS); - - for (; y <= h; y++) - for (x = origx; x <= w; x++) - image_undo_push_tile(ima, ibuf, &tmpibuf, x, y); - - ibuf->userflags |= IB_BITMAPDIRTY; - - if (tmpibuf) - IMB_freeImBuf(tmpibuf); -} - -static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) -{ - if (imapaintpartial.x1 != imapaintpartial.x2 && - imapaintpartial.y1 != imapaintpartial.y2) - { - IMB_partial_display_buffer_update_delayed(ibuf, imapaintpartial.x1, imapaintpartial.y1, - imapaintpartial.x2, imapaintpartial.y2); - } - - if (ibuf->mipmap[0]) - ibuf->userflags |= IB_MIPMAP_INVALID; - - /* todo: should set_tpage create ->rect? */ - if (texpaint || (sima && sima->lock)) { - int w = imapaintpartial.x2 - imapaintpartial.x1; - int h = imapaintpartial.y2 - imapaintpartial.y1; - /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); - } -} - -/* Image Paint Operations */ - -/* keep these functions in sync */ -static void imapaint_ibuf_rgb_get(ImBuf *ibuf, int x, int y, const short is_torus, float r_rgb[3]) -{ - if (is_torus) { - x %= ibuf->x; - if (x < 0) x += ibuf->x; - y %= ibuf->y; - if (y < 0) y += ibuf->y; - } - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - IMAPAINT_FLOAT_RGB_COPY(r_rgb, rrgbf); - } - else { - char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4; - IMAPAINT_CHAR_RGB_TO_FLOAT(r_rgb, rrgb); - } -} -static void imapaint_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const short is_torus, const float rgb[3]) -{ - if (is_torus) { - x %= ibuf->x; - if (x < 0) x += ibuf->x; - y %= ibuf->y; - if (y < 0) y += ibuf->y; - } - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - IMAPAINT_FLOAT_RGB_COPY(rrgbf, rgb); - } - else { - char *rrgb = (char *)ibuf->rect + (ibuf->x * y + x) * 4; - IMAPAINT_FLOAT_RGB_TO_CHAR(rrgb, rgb); - } -} - -static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus) -{ - float inrgb[3]; - - // XXX: signed unsigned mismatch - if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) { - if (torus) imapaint_ibuf_rgb_get(ibuf, x, y, 1, inrgb); - else return 0; - } - else { - imapaint_ibuf_rgb_get(ibuf, x, y, 0, inrgb); - } - - outrgb[0] += inrgb[0]; - outrgb[1] += inrgb[1]; - outrgb[2] += inrgb[2]; - - return 1; -} - -static void imapaint_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const short is_torus) -{ - int x, y, count, xi, yi, xo, yo; - int out_off[2], in_off[2], dim[2]; - float outrgb[3]; - - dim[0] = ibufb->x; - dim[1] = ibufb->y; - in_off[0] = pos[0]; - in_off[1] = pos[1]; - out_off[0] = out_off[1] = 0; - - if (!is_torus) { - 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)) - return; - } - - for (y = 0; y < dim[1]; y++) { - for (x = 0; x < dim[0]; x++) { - /* get input pixel */ - xi = in_off[0] + x; - yi = in_off[1] + y; - - count = 1; - imapaint_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); - - count += imapaint_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); - count += imapaint_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); - count += imapaint_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); - - count += imapaint_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); - count += imapaint_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); - - count += imapaint_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); - count += imapaint_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); - count += imapaint_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); - - mul_v3_fl(outrgb, 1.0f / (float)count); - - /* write into brush buffer */ - xo = out_off[0] + x; - yo = out_off[1] + y; - imapaint_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); - } - } -} - -static void imapaint_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) -{ - region->destx = destx; - region->desty = desty; - region->srcx = srcx; - region->srcy = srcy; - region->width = width; - region->height = height; -} - -static int imapaint_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf) -{ - int destx = region->destx; - int desty = region->desty; - int srcx = region->srcx; - int srcy = region->srcy; - int width = region->width; - int height = region->height; - int origw, origh, w, h, tot = 0; - - /* convert destination and source coordinates to be within image */ - destx = destx % dbuf->x; - if (destx < 0) destx += dbuf->x; - desty = desty % dbuf->y; - if (desty < 0) desty += dbuf->y; - srcx = srcx % sbuf->x; - if (srcx < 0) srcx += sbuf->x; - srcy = srcy % sbuf->y; - if (srcy < 0) srcy += sbuf->y; - - /* clip width of blending area to destination imbuf, to avoid writing the - * same pixel twice */ - origw = w = (width > dbuf->x) ? dbuf->x : width; - origh = h = (height > dbuf->y) ? dbuf->y : height; - - /* clip within image */ - IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); - imapaint_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); - - /* do 3 other rects if needed */ - if (w < origw) - imapaint_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); - if (h < origh) - imapaint_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); - if ((w < origw) && (h < origh)) - imapaint_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); - - return tot; -} - -static void imapaint_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos) -{ - ImagePaintRegion region[4]; - int a, tot; - - imapaint_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); - tot = imapaint_torus_split_region(region, ibufb, ibuf); - - for (a = 0; a < tot; a++) - IMB_rectblend(ibufb, ibuf, region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, IMB_BLEND_COPY_RGB); -} - -static ImBuf *imapaint_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) -{ - /* note: allocImbuf returns zero'd memory, so regions outside image will - * have zero alpha, and hence not be blended onto the image */ - int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; - ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); - - IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); - IMB_rectblend(clonebuf, ibuf, destx, desty, srcx, srcy, w, h, - IMB_BLEND_COPY_RGB); - IMB_rectblend(clonebuf, ibufb, destx, desty, destx, desty, w, h, - IMB_BLEND_COPY_ALPHA); - - return clonebuf; -} - -static void imapaint_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) -{ - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); -} - -/* dosnt run for projection painting - * only the old style painting in the 3d view */ -static int imapaint_paint_op(void *state, ImBuf *ibufb, const float lastpos[2], const float pos[2]) -{ - ImagePaintState *s = ((ImagePaintState *)state); - ImBuf *clonebuf = NULL, *frombuf; - ImagePaintRegion region[4]; - short torus = s->brush->flag & BRUSH_TORUS; - short blend = s->blend; - float *offset = s->brush->clone.offset; - float liftpos[2]; - int bpos[2], blastpos[2], bliftpos[2]; - int a, tot; - - imapaint_convert_brushco(ibufb, pos, bpos); - - /* lift from canvas */ - if (s->tool == PAINT_TOOL_SOFTEN) { - imapaint_lift_soften(s->canvas, ibufb, bpos, torus); - } - else if (s->tool == PAINT_TOOL_SMEAR) { - if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) - return 0; - - imapaint_convert_brushco(ibufb, lastpos, blastpos); - imapaint_lift_smear(s->canvas, ibufb, blastpos); - } - 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; - - imapaint_convert_brushco(ibufb, liftpos, bliftpos); - clonebuf = imapaint_lift_clone(s->clonecanvas, ibufb, bliftpos); - } - - frombuf = (clonebuf) ? clonebuf : ibufb; - - if (torus) { - imapaint_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = imapaint_torus_split_region(region, s->canvas, frombuf); - } - else { - imapaint_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = 1; - } - - /* blend into canvas */ - for (a = 0; a < tot; a++) { - imapaint_dirty_region(s->image, s->canvas, - region[a].destx, region[a].desty, - region[a].width, region[a].height); - - IMB_rectblend(s->canvas, frombuf, - region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); - } - - if (clonebuf) IMB_freeImBuf(clonebuf); - - return 1; -} - - -static int imapaint_canvas_set(ImagePaintState *s, Image *ima) -{ - 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 (ima->packedfile && 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); - - 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) { - IMB_float_from_rect(s->clonecanvas); - } - else if (!s->canvas->rect_float && !s->clonecanvas->rect) - IMB_rect_from_float(s->clonecanvas); - } - - return 1; -} - -static void imapaint_canvas_free(ImagePaintState *s) -{ - BKE_image_release_ibuf(s->image, s->canvas, NULL); - BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); -} - -static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter, Image *image, float *uv, int update, float pressure) -{ - ImBuf *ibuf = BKE_image_acquire_ibuf(image, s->sima ? &s->sima->iuser : NULL, NULL); - float pos[2]; - int is_data; - - if (!ibuf) - return 0; - - is_data = ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA; - - pos[0] = uv[0] * ibuf->x; - pos[1] = uv[1] * ibuf->y; - - BKE_brush_painter_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0, 0); - - /* 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 - */ - if (BKE_brush_painter_paint(painter, imapaint_paint_op, pos, 0, pressure, s, is_data == FALSE)) { - if (update) - imapaint_image_update(s->sima, image, ibuf, false); - BKE_image_release_ibuf(image, ibuf, NULL); - return 1; - } - else { - BKE_image_release_ibuf(image, ibuf, NULL); - return 0; - } -} - -static int imapaint_paint_stroke(ImagePaintState *s, BrushPainter *painter, const int mval[2], float pressure) -{ - float newuv[2]; - int redraw = 0; - - UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); - redraw |= imapaint_paint_sub_stroke(s, painter, s->image, newuv, - 1, pressure); - - if (redraw) - imapaint_clear_partial_redraw(); - - return redraw; -} /************************ image paint poll ************************/ @@ -4811,8 +4294,7 @@ typedef enum TexPaintMode { typedef struct PaintOperation { TexPaintMode mode; - BrushPainter *painter; - ImagePaintState s; + void *custom_paint; ProjPaintState ps; int first; @@ -4824,20 +4306,18 @@ typedef struct PaintOperation { wmTimer *timer; } PaintOperation; -static void paint_redraw(const bContext *C, ImagePaintState *s, int final) +static void paint_redraw(const bContext *C, PaintOperation *pop, int final) { - if (final) { - if (s->image && !(s->sima && s->sima->lock)) - GPU_free_image(s->image); - - /* compositor listener deals with updating */ - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); - } - else { - if (!s->sima || !s->sima->lock) + if (pop->mode == PAINT_MODE_2D) { + paint_2d_redraw(C, pop->custom_paint, final); + } else { + if (final) { + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL); + } + else { ED_region_tag_redraw(CTX_wm_region(C)); - else - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + } } } @@ -4910,17 +4390,6 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps) ps->do_mask_normal = FALSE; /* no need to do blending */ } -static void paint_brush_init_tex(Brush *brush) -{ - /* init mtex nodes */ - if (brush) { - MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexBeginExecTree(mtex->tex->nodetree, 1); /* has internal flag to detect it only does it once */ - } - -} - static PaintOperation * texture_paint_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -4937,67 +4406,15 @@ static PaintOperation * texture_paint_init(bContext *C, wmOperator *op) pop->mode = PAINT_MODE_3D_PROJECT; } else { - pop->s.sima = CTX_wm_space_image(C); - pop->s.v2d = &CTX_wm_region(C)->v2d; - } - - pop->s.scene = scene; - pop->s.screen = CTX_wm_screen(C); - - pop->s.brush = brush; - pop->s.tool = brush->imagepaint_tool; - pop->s.blend = brush->blend; - pop->orig_brush_size = BKE_brush_size_get(scene, brush); - - if (pop->mode != PAINT_MODE_2D) { - Object *ob = OBACT; - Mesh *me = BKE_mesh_from_object(ob); - - if (!me) { - return 0; - } - - pop->s.ob = ob; - pop->s.do_facesel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - /* for non prohect paint we need */ - /* fill in derived mesh */ - if (ob->derivedFinal && CustomData_has_layer(&ob->derivedFinal->faceData, CD_MTFACE)) { - pop->s.dm = ob->derivedFinal; - pop->s.dm_release = FALSE; - } - else { - pop->s.dm = mesh_get_derived_final(pop->s.scene, ob, pop->s.scene->customdata_mask | CD_MASK_MTFACE); - pop->s.dm_release = TRUE; - } - - if (!CustomData_has_layer(&pop->s.dm->faceData, CD_MTFACE)) { - - if (pop->s.dm_release) - pop->s.dm->release(pop->s.dm); - - pop->s.dm = NULL; + pop->mode = PAINT_MODE_2D; + pop->custom_paint = paint_2d_new_stroke(C, op); + if (!pop->custom_paint) { MEM_freeN(pop); return NULL; } - - pop->s.dm_mface = pop->s.dm->getTessFaceArray(pop->s.dm); - pop->s.dm_mtface = pop->s.dm->getTessFaceDataArray(pop->s.dm, CD_MTFACE); - pop->s.dm_totface = pop->s.dm->getNumTessFaces(pop->s.dm); } - else { - pop->s.image = pop->s.sima->image; - if (!imapaint_canvas_set(&pop->s, pop->s.image)) { - if (pop->s.warnmultifile) - BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); - if (pop->s.warnpackedfile) - BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); - - MEM_freeN(pop); - return NULL; - } - } + pop->orig_brush_size = BKE_brush_size_get(scene, brush); /* note, if we have no UVs on the derived mesh, then we must return here */ if (pop->mode == PAINT_MODE_3D_PROJECT) { @@ -5029,17 +4446,11 @@ static PaintOperation * texture_paint_init(bContext *C, wmOperator *op) return NULL; } } - else { - paint_brush_init_tex(pop->s.brush); - } settings->imapaint.flag |= IMAGEPAINT_DRAWING; undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, image_undo_restore, image_undo_free); - /* create painter */ - pop->painter = BKE_brush_painter_new(scene, pop->s.brush); - { UnifiedPaintSettings *ups = &settings->unified_paint_settings; ups->draw_pressure = true; @@ -5053,43 +4464,35 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po PaintOperation *pop = paint_stroke_mode_data(stroke); float mousef[2]; float pressure; - int mouse[2], redraw; + int mouse[2], redraw, eraser; RNA_float_get_array(itemptr, "mouse", mousef); mouse[0] = (int)(mousef[0]); mouse[1] = (int)(mousef[1]); pressure = RNA_float_get(itemptr, "pressure"); + eraser = RNA_boolean_get(itemptr, "pen_flip"); if (pop->mode == PAINT_MODE_3D_PROJECT) { if (pop->first) project_paint_begin_clone(&pop->ps, mouse); - redraw = project_paint_stroke(&pop->ps, pop->prevmouse, mouse, pressure); + redraw = project_paint_stroke(&pop->ps, pop->prevmouse, mouse); pop->prevmouse[0] = mouse[0]; pop->prevmouse[1] = mouse[1]; } else { - redraw = imapaint_paint_stroke(&pop->s, pop->painter, mouse, pressure); + redraw = paint_2d_stroke(pop->custom_paint, mouse, pressure, eraser); pop->prevmouse[0] = mouse[0]; pop->prevmouse[1] = mouse[1]; } if (redraw) - paint_redraw(C, &pop->s, 0); + paint_redraw(C, pop, 0); pop->first = 0; } -static void paint_brush_exit_tex(Brush *brush) -{ - if (brush) { - MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata, 1); - } -} - static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) { Scene *scene = CTX_data_scene(C); @@ -5100,24 +4503,16 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), pop->timer); settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - imapaint_canvas_free(&pop->s); - BKE_brush_painter_free(pop->painter); if (pop->mode == PAINT_MODE_3D_PROJECT) { BKE_brush_size_set(scene, pop->ps.brush, pop->orig_brush_size); paint_brush_exit_tex(pop->ps.brush); project_paint_end(&pop->ps); - } - else { - paint_brush_exit_tex(pop->s.brush); - - /* non projection 3d paint, could move into own function of more needs adding */ - if (pop->s.dm_release) - pop->s.dm->release(pop->s.dm); - } + } else + paint_2d_stroke_done(pop->custom_paint); - paint_redraw(C, &pop->s, 1); + paint_redraw(C, pop, 1); undo_paint_push_end(UNDO_PAINT_IMAGE); /* duplicate warning, see texpaint_init diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 36b1e56557f..91ef6b83b36 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -50,6 +50,8 @@ struct ViewContext; struct wmEvent; struct wmOperator; struct wmOperatorType; +struct ImagePaintState; +enum PaintMode; /* paint_stroke.c */ typedef int (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); @@ -64,6 +66,7 @@ void paint_stroke_data_free(struct wmOperator *op); bool paint_space_stroke_enabled(struct Brush *br); bool paint_supports_dynamic_size(struct Brush *br); +bool paint_supports_jitter(enum PaintMode mode); struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf); int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event); @@ -103,7 +106,30 @@ void PAINT_OT_vertex_paint(struct wmOperatorType *ot); unsigned int vpaint_get_current_col(struct VPaint *vp); /* paint_image.c */ +typedef struct ImagePaintPartialRedraw { + int x1, y1, x2, y2; /* XXX, could use 'rcti' */ + int enabled; +} ImagePaintPartialRedraw; + +#define IMAPAINT_TILE_BITS 6 +#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) +#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) + +#define IMAPAINT_CHAR_TO_FLOAT(c) ((c) / 255.0f) + int image_texture_paint_poll(struct bContext *C); +void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile); +void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); +struct ImagePaintPartialRedraw *get_imapaintpartial(void); +void set_imapaintpartial(struct ImagePaintPartialRedraw * ippr); +void imapaint_clear_partial_redraw(void); +void imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); +void *paint_2d_new_stroke(struct bContext *, struct wmOperator *); +void paint_2d_redraw(const bContext *C, void *ps, int final); +void paint_2d_stroke_done(void *ps); +int paint_2d_stroke(void *ps, const int mval[2], float pressure, int eraser); +void paint_brush_init_tex(struct Brush *brush); +void paint_brush_exit_tex(struct Brush *brush); void PAINT_OT_image_paint(struct wmOperatorType *ot); void PAINT_OT_grab_clone(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 287bd723534..2146c74fb6d 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -157,7 +157,7 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev /* TODO: as sculpt and other paint modes are unified, this * separation will go away */ - if (ELEM(mode, PAINT_SCULPT, PAINT_TEXTURE_PROJECTIVE)) { + if (paint_supports_jitter(mode)) { float delta[2]; BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out); @@ -338,6 +338,10 @@ bool paint_supports_dynamic_size(Brush *br) !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); } +bool paint_supports_jitter(PaintMode mode) { + return ELEM(mode, PAINT_SCULPT, PAINT_TEXTURE_PROJECTIVE); +} + #define PAINT_STROKE_MODAL_CANCEL 1 /* called in paint_ops.c, on each regeneration of keymaps */ |