diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2013-04-30 10:07:42 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2013-04-30 10:07:42 +0400 |
commit | 63f05576b840c597f91414fd2ca6db8ca869e3e9 (patch) | |
tree | e6d0bbd3dc57e67a0799a5fc066e72c8f630abc7 /source/blender/editors/sculpt_paint | |
parent | b735402c19782402e4a9970280cd3a3c8457296f (diff) |
More image painting fixes:
* 2D image painting support for masking to limit the max contribution of a stroke
to a pixel, to get it working compatible with projection painting. Not strictly
a bugfix, but the inconsistency here was annoying.
* Fix python errors in Texture Mask panel in image editor, was missing overlay
options.
* Clamp paint mask to 0..1 in case some texture exceeds it, this could give black
pixels due to integer overflow.
Diffstat (limited to 'source/blender/editors/sculpt_paint')
4 files changed, 137 insertions, 31 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index e3adf40a7eb..f059a0b1a0a 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -101,9 +101,6 @@ #include "paint_intern.h" -#define IMAPAINT_TILE_BITS 6 -#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) - typedef struct UndoImageTile { struct UndoImageTile *next, *prev; @@ -115,6 +112,9 @@ typedef struct UndoImageTile { unsigned int *uint; void *pt; } rect; + + unsigned short *mask; + int x, y; short source, use_float; @@ -156,18 +156,45 @@ 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); } +void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; + short use_float = ibuf->rect_float ? 1 : 0; + + for (tile = lb->first; tile; tile = tile->next) { + if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) { + if (tile->use_float == use_float) { + if (strcmp(tile->idname, ima->id.name) == 0 && strcmp(tile->ibufname, ibuf->name) == 0) { + if (mask) { + /* allocate mask if requested */ + if (!tile->mask) + tile->mask = MEM_callocN(sizeof(unsigned short)*IMAPAINT_TILE_SIZE*IMAPAINT_TILE_SIZE, "UndoImageTile.mask"); + + *mask = tile->mask; + } + + return tile->rect.pt; + } + } + } + } + + return NULL; +} + 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; + void *data; - 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; + /* check if tile is already pushed */ + data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, NULL); + if (data) + return data; if (*tmpibuf == NULL) *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); @@ -195,6 +222,19 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, return tile->rect.pt; } +void image_undo_remove_masks(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; + + for (tile = lb->first; tile; tile = tile->next) { + if (tile->mask) { + MEM_freeN(tile->mask); + tile->mask = NULL; + } + } +} + void image_undo_restore(bContext *C, ListBase *lb) { Main *bmain = CTX_data_main(C); @@ -276,10 +316,23 @@ void imapaint_clear_partial_redraw(void) memset(&imapaintpartial, 0, sizeof(imapaintpartial)); } +void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) +{ + int srcx = 0, srcy = 0; + + IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + + *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS); + *th = ((y + h - 1) >> IMAPAINT_TILE_BITS); + *tx = (x >> IMAPAINT_TILE_BITS); + *ty = (y >> IMAPAINT_TILE_BITS); +} + 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; + int tilex, tiley, tilew, tileh, tx, ty; + int srcx = 0, srcy = 0; IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); @@ -300,14 +353,11 @@ void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) 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); + imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); + + for (ty = tiley; ty <= tileh; ty++) + for (tx = tilex; tx <= tilew; tx++) + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty); ibuf->userflags |= IB_BITMAPDIRTY; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 1c50b048fce..d6a634ec404 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -133,6 +133,8 @@ typedef struct ImagePaintState { char *warnpackedfile; char *warnmultifile; + bool do_masking; + /* viewport texture paint only, but _not_ project paint */ Object *ob; int faceindex; @@ -327,7 +329,7 @@ static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, con brush_painter_2d_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); } -static void brush_painter_2d_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], bool use_color_correction, bool use_brush_alpha) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -347,7 +349,7 @@ static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float po } if (diameter != cache->lastsize || - alpha != cache->lastalpha || + (use_brush_alpha && alpha != cache->lastalpha) || brush->jitter != cache->lastjitter || rotation != cache->last_rotation || do_random) @@ -365,11 +367,13 @@ static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float po size = (cache->size) ? cache->size : diameter; if (do_tiled) { - BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, use_color_correction); + BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, + use_color_correction, use_brush_alpha); brush_painter_2d_tiled_tex_partial_update(painter, pos); } else - BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf, use_color_correction); + BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf, + use_color_correction, use_brush_alpha); cache->lastsize = diameter; cache->lastalpha = alpha; @@ -552,7 +556,8 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos) 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, + IMB_rectblend(ibufb, ibufb, ibuf, NULL, 0, region[a].destx, region[a].desty, + region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, region[a].width, region[a].height, IMB_BLEND_COPY_RGB); } @@ -565,9 +570,9 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) 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_rectblend(clonebuf, clonebuf, ibuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, IMB_BLEND_COPY_RGB); - IMB_rectblend(clonebuf, ibufb, destx, desty, destx, desty, w, h, + IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, IMB_BLEND_COPY_ALPHA); return clonebuf; @@ -582,12 +587,14 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ 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; + ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL; ImagePaintRegion region[4]; short torus = s->brush->flag & BRUSH_TORUS; short blend = s->blend; float *offset = s->brush->clone.offset; float liftpos[2]; + float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush); + unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f); int bpos[2], blastpos[2], bliftpos[2]; int a, tot; @@ -623,19 +630,55 @@ static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const tot = 1; } + if (s->do_masking) + tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + /* 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 (s->do_masking) { + /* masking, find original pixels tiles from undo buffer to composite over */ + int tilex, tiley, tilew, tileh, tx, ty; + + imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty, + region[a].width, region[a].height, + &tilex, &tiley, &tilew, &tileh); + + for (ty = tiley; ty <= tileh; ty++) { + for (tx = tilex; tx <= tilew; tx++) { + /* retrieve original pixels + mask from undo buffer */ + unsigned short *mask; + int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE; + int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE; + + if (s->canvas->rect_float) + tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + else + tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + + IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, mask_max, + region[a].destx, region[a].desty, + origx, origy, + region[a].srcx, region[a].srcy, + region[a].width, region[a].height, blend); + } + } + } + else { + /* no masking, composite brush directly onto canvas */ + IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, 0, + region[a].destx, region[a].desty, + region[a].destx, region[a].desty, + region[a].srcx, region[a].srcy, + region[a].width, region[a].height, blend); + } } if (clonebuf) IMB_freeImBuf(clonebuf); + if (tmpbuf) IMB_freeImBuf(tmpbuf); return 1; } @@ -684,6 +727,11 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) IMB_rect_from_float(s->clonecanvas); } + /* set masking */ + s->do_masking = (s->brush->flag & BRUSH_AIRBRUSH || (s->brush->mtex.tex && + !ELEM(s->brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL))) + ? false : true; + return 1; } @@ -691,6 +739,9 @@ 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); + + if (s->do_masking) + image_undo_remove_masks(); } int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int eraser) @@ -734,7 +785,7 @@ int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int era */ brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0); - brush_painter_2d_refresh_cache(painter, newuv, is_data == false); + brush_painter_2d_refresh_cache(painter, newuv, is_data == false, s->do_masking); if (paint_2d_op(s, painter->cache.ibuf, olduv, newuv)) { imapaint_image_update(s->sima, s->image, ibuf, false); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 0088e5aa3cb..13b3629518f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -3887,6 +3887,8 @@ static void *do_projectpaint_thread(void *ph_v) mask *= BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); } + CLAMP(mask, 0.0f, 1.0f); + if (ps->do_masking) { /* masking to keep brush contribution to a pixel limited. note we do not do * a simple max(mask, mask_accum), as this is very sensitive to spacing and diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f77d0ac4144..0085998bd58 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -126,7 +126,9 @@ typedef struct ImagePaintPartialRedraw { #define IMAPAINT_CHAR_TO_FLOAT(c) ((c) / 255.0f) int image_texture_paint_poll(struct bContext *C); +void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask); void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile); +void image_undo_remove_masks(void); void image_undo_restore(struct bContext *C, struct ListBase *lb); void image_undo_free(struct ListBase *lb); void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); @@ -134,6 +136,7 @@ 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 imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy); void *paint_2d_new_stroke(struct bContext *, struct wmOperator *); void paint_2d_redraw(const bContext *C, void *ps, int final); |