diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 54a788f3e46..3acab7200ea 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -763,6 +763,12 @@ class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel): col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8) brush_mask_texture_settings(col, brush) + + col = layout.column(align=True) + col.active = brush.brush_capabilities.has_overlay + col.label(text="Overlay:") + + row = col.row() if tex_slot_alpha.map_mode != 'STENCIL': if brush.use_secondary_overlay: row.prop(brush, "use_secondary_overlay", toggle=True, text="", icon='RESTRICT_VIEW_OFF') diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 452030d05e6..863d7b05693 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -82,7 +82,7 @@ float BKE_brush_sample_tex_2D(const struct Scene *scene, struct Brush *brush, co float BKE_brush_sample_masktex(const Scene *scene, struct Brush *br, const float point[3], const int thread, struct ImagePool *pool); void BKE_brush_imbuf_new(const struct Scene *scene, struct Brush *brush, short flt, short texfalloff, int size, - struct ImBuf **imbuf, int use_color_correction); + struct ImBuf **imbuf, bool use_color_correction, bool use_brush_alpha); /* texture */ unsigned int *BKE_brush_gen_texture_cache(struct Brush *br, int half_side); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 517180964a5..c24aab1629c 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -828,14 +828,15 @@ float BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2 /* TODO, use define for 'texfall' arg * NOTE: only used for 2d brushes currently! */ -void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction) +void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, + ImBuf **outbuf, bool use_color_correction, bool use_brush_alpha) { ImBuf *ibuf; float xy[2], rgba[4], *dstf; int x, y, rowbytes, xoff, yoff, imbflag; const int radius = BKE_brush_size_get(scene, brush); unsigned char *dst, crgb[3]; - const float alpha = BKE_brush_alpha_get(scene, brush); + const float alpha = (use_brush_alpha)? BKE_brush_alpha_get(scene, brush): 1.0f; float brush_rgb[3]; imbflag = (flt) ? IB_rectfloat : IB_rect; diff --git a/source/blender/blenlib/intern/math_color_blend_inline.c b/source/blender/blenlib/intern/math_color_blend_inline.c index 94a474da681..121750fe919 100644 --- a/source/blender/blenlib/intern/math_color_blend_inline.c +++ b/source/blender/blenlib/intern/math_color_blend_inline.c @@ -372,12 +372,17 @@ MINLINE void blend_color_erase_alpha_float(float dst[4], const float src1[4], co { if (src2[3] != 0.0f && src1[3] > 0.0f) { /* subtract alpha and remap RGB channels to match */ - const float alpha = max_ff(src1[3] - src2[3], 0.0f); - const float map_alpha = alpha / src1[3]; + float alpha = max_ff(src1[3] - src2[3], 0.0f); + float map_alpha; - dst[0] *= map_alpha; - dst[1] *= map_alpha; - dst[2] *= map_alpha; + if (alpha <= 0.0005f) + alpha = 0.0f; + + map_alpha = alpha / src1[3]; + + dst[0] = src1[0] * map_alpha; + dst[1] = src1[1] * map_alpha; + dst[2] = src1[2] * map_alpha; dst[3] = alpha; } else { @@ -393,12 +398,17 @@ MINLINE void blend_color_add_alpha_float(float dst[4], const float src1[4], cons { if (src2[3] != 0.0f && src1[3] < 1.0f) { /* add alpha and remap RGB channels to match */ - const float alpha = min_ff(src1[3] + src2[3], 1.0f); - const float map_alpha = (src1[3] > 0.0f) ? alpha / src1[3] : 1.0f; + float alpha = min_ff(src1[3] + src2[3], 1.0f); + float map_alpha; - dst[0] *= map_alpha; - dst[1] *= map_alpha; - dst[2] *= map_alpha; + if (alpha >= 1.0f - 0.0005f) + alpha = 1.0f; + + map_alpha = (src1[3] > 0.0f) ? alpha / src1[3] : 1.0f; + + dst[0] = src1[0] * map_alpha; + dst[1] = src1[1] * map_alpha; + dst[2] = src1[2] * map_alpha; dst[3] = alpha; } else { 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); + + if (s->do_masking) { + /* masking, find original pixels tiles from undo buffer to composite over */ + int tilex, tiley, tilew, tileh, tx, ty; - IMB_rectblend(s->canvas, frombuf, - region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); + 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); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 04179b28e56..7253a61092c 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -178,8 +178,10 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, int *desty, int *srcx, int *srcy, int *width, int *height); void IMB_rectcpy(struct ImBuf *drect, struct ImBuf *srect, int destx, int desty, int srcx, int srcy, int width, int height); -void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, - int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode); +void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf, + unsigned short *mask, unsigned short mask_max, + int destx, int desty, int origx, int origy, int srcx, int srcy, + int width, int height, IMB_BlendMode mode); /** * diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 95426c573db..d9fc4fe172a 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -104,7 +104,7 @@ void IMB_blend_color_float(float dst[4], float src1[4], float src2[4], IMB_Blend /* clipping */ -void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, +void IMB_rectclip(ImBuf *dbuf, ImBuf *sbuf, int *destx, int *desty, int *srcx, int *srcy, int *width, int *height) { int tmp; @@ -150,43 +150,125 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, } } +static void imb_rectclip3(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, int *destx, + int *desty, int *origx, int *origy, int *srcx, int *srcy, + int *width, int *height) +{ + int tmp; + + if (dbuf == NULL) return; + + if (*destx < 0) { + *srcx -= *destx; + *origx -= *destx; + *width += *destx; + *destx = 0; + } + if (*origx < 0) { + *destx -= *origx; + *srcx -= *origx; + *width += *origx; + *origx = 0; + } + if (*srcx < 0) { + *destx -= *srcx; + *origx -= *srcx; + *width += *srcx; + *srcx = 0; + } + + if (*desty < 0) { + *srcy -= *desty; + *origy -= *desty; + *height += *desty; + *desty = 0; + } + if (*origy < 0) { + *desty -= *origy; + *srcy -= *origy; + *height += *origy; + *origy = 0; + } + if (*srcy < 0) { + *desty -= *srcy; + *origy -= *srcy; + *height += *srcy; + *srcy = 0; + } + + tmp = dbuf->x - *destx; + if (*width > tmp) *width = tmp; + tmp = dbuf->y - *desty; + if (*height > tmp) *height = tmp; + + if (obuf) { + tmp = obuf->x - *origx; + if (*width > tmp) *width = tmp; + tmp = obuf->y - *origy; + if (*height > tmp) *height = tmp; + } + + if (sbuf) { + tmp = sbuf->x - *srcx; + if (*width > tmp) *width = tmp; + tmp = sbuf->y - *srcy; + if (*height > tmp) *height = tmp; + } + + if ((*height <= 0) || (*width <= 0)) { + *width = 0; + *height = 0; + } +} + /* copy and blend */ -void IMB_rectcpy(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, +void IMB_rectcpy(ImBuf *dbuf, ImBuf *sbuf, int destx, int desty, int srcx, int srcy, int width, int height) { - IMB_rectblend(dbuf, sbuf, destx, desty, srcx, srcy, width, height, - IMB_BLEND_COPY); + IMB_rectblend(dbuf, dbuf, sbuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, width, height, IMB_BLEND_COPY); } typedef void (*IMB_blend_func)(unsigned char *dst, const unsigned char *src1, const unsigned char *src2); typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2); -void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, - int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode) +void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskrect, unsigned short mask_max, + int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, + IMB_BlendMode mode) { - unsigned int *drect = NULL, *srect = NULL, *dr, *sr; - float *drectf = NULL, *srectf = NULL, *drf, *srf; - int do_float, do_char, srcskip, destskip, x; + unsigned int *drect = NULL, *orect, *srect = NULL, *dr, *or, *sr; + float *drectf = NULL, *orectf, *srectf = NULL, *drf, *orf, *srf; + unsigned short *mr; + int do_float, do_char, srcskip, destskip, origskip, x; IMB_blend_func func = NULL; IMB_blend_func_float func_float = NULL; - if (dbuf == NULL) return; + if (dbuf == NULL || obuf == NULL) return; - IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &width, &height); + imb_rectclip3(dbuf, obuf, sbuf, &destx, &desty, &origx, &origy, &srcx, &srcy, &width, &height); if (width == 0 || height == 0) return; if (sbuf && sbuf->channels != 4) return; if (dbuf->channels != 4) return; - - do_char = (sbuf && sbuf->rect && dbuf->rect); - do_float = (sbuf && sbuf->rect_float && dbuf->rect_float); - if (do_char) drect = dbuf->rect + desty * dbuf->x + destx; - if (do_float) drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4; + do_char = (sbuf && sbuf->rect && dbuf->rect && obuf->rect); + do_float = (sbuf && sbuf->rect_float && dbuf->rect_float && obuf->rect_float); + + if (do_char) { + drect = dbuf->rect + desty * dbuf->x + destx; + orect = obuf->rect + origy * obuf->x + origx; + } + if (do_float) { + drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4; + orectf = obuf->rect_float + (origy * obuf->x + origx) * 4; + } + + if (maskrect) + maskrect += origy * obuf->x + origx; destskip = dbuf->x; + origskip = obuf->x; if (sbuf) { if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx; @@ -307,24 +389,92 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, for (; height > 0; height--) { if (do_char) { dr = drect; + or = orect; sr = srect; - for (x = width; x > 0; x--, dr++, sr++) { - if (((unsigned char *)sr)[3]) - func((unsigned char *)dr, (unsigned char *)dr, (unsigned char *)sr); + + if (maskrect) { + /* mask accumulation for painting */ + mr = maskrect; + + for (x = width; x > 0; x--, dr++, or++, sr++, mr++) { + unsigned char *src = (unsigned char*)sr; + + if (src[3]) { + unsigned short mask = *mr + divide_round_i((mask_max - *mr) * src[3], 255); + + if (mask > *mr) { + unsigned char mask_src[4]; + + *mr = mask; + + mask_src[0] = src[0]; + mask_src[1] = src[1]; + mask_src[2] = src[2]; + mask_src[3] = mask >> 8; + + func((unsigned char *)dr, (unsigned char *)or, mask_src); + } + } + } + + maskrect += origskip; + } + else { + /* regular blending */ + for (x = width; x > 0; x--, dr++, or++, sr++) { + if (((unsigned char *)sr)[3]) + func((unsigned char *)dr, (unsigned char *)or, (unsigned char *)sr); + } } drect += destskip; + orect += origskip; srect += srcskip; } if (do_float) { drf = drectf; + orf = orectf; srf = srectf; - for (x = width; x > 0; x--, drf += 4, srf += 4) { - if (srf[3] != 0) - func_float(drf, drf, srf); + + if (maskrect) { + /* mask accumulation for painting */ + mr = maskrect; + + for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, mr++) { + if (srf[3] != 0) { + float alpha = CLAMPIS(srf[3], 0.0f, 1.0f); + unsigned short mask = (unsigned short)(*mr + (mask_max - *mr) * alpha); + + if (mask > *mr) { + float mask_srf[4]; + float new_alpha = mask * (1.0f/65535.0f); + float map_alpha = new_alpha / srf[3]; + + *mr = mask; + + mask_srf[0] = map_alpha * srf[0]; + mask_srf[1] = map_alpha * srf[1]; + mask_srf[2] = map_alpha * srf[2]; + mask_srf[3] = new_alpha; + + func_float(drf, orf, mask_srf); + } + } + } + + maskrect += origskip; } + else { + /* regular blending */ + for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4) { + if (srf[3] != 0) + func_float(drf, orf, srf); + } + } + drectf += destskip * 4; + orectf += origskip * 4; srectf += srcskip * 4; } } @@ -333,7 +483,7 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, /* fill */ -void IMB_rectfill(struct ImBuf *drect, const float col[4]) +void IMB_rectfill(ImBuf *drect, const float col[4]) { int num; @@ -462,7 +612,7 @@ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height, } } -void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display) +void IMB_rectfill_area(ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display) { if (!ibuf) return; buf_rectfill_area((unsigned char *) ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, display, diff --git a/source/gameengine/VideoTexture/ImageBuff.cpp b/source/gameengine/VideoTexture/ImageBuff.cpp index 8f547aa8b57..eb68ea2425d 100644 --- a/source/gameengine/VideoTexture/ImageBuff.cpp +++ b/source/gameengine/VideoTexture/ImageBuff.cpp @@ -163,7 +163,7 @@ void ImageBuff::plot(unsigned char *img, short width, short height, short x, sho // assign temporarily our buffer to the ImBuf buffer, we use the same format tmpbuf->rect = (unsigned int*)img; m_imbuf->rect = m_image; - IMB_rectblend(m_imbuf, tmpbuf, x, y, 0, 0, width, height, (IMB_BlendMode)mode); + IMB_rectblend(m_imbuf, m_imbuf, tmpbuf, NULL, 0, x, y, x, y, 0, 0, width, height, (IMB_BlendMode)mode); // remove so that MB_freeImBuf will free our buffer m_imbuf->rect = NULL; tmpbuf->rect = NULL; @@ -186,7 +186,7 @@ void ImageBuff::plot(ImageBuff *img, short x, short y, short mode) // assign temporarily our buffer to the ImBuf buffer, we use the same format img->m_imbuf->rect = img->m_image; m_imbuf->rect = m_image; - IMB_rectblend(m_imbuf, img->m_imbuf, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode); + IMB_rectblend(m_imbuf, m_imbuf, img->m_imbuf, NULL, 0, x, y, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode); // remove so that MB_freeImBuf will free our buffer m_imbuf->rect = NULL; img->m_imbuf->rect = NULL;