diff options
-rw-r--r-- | source/blender/blenkernel/BKE_brush.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 116 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_2d.c | 518 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_proj.c | 2 | ||||
-rw-r--r-- | source/blender/imbuf/IMB_imbuf.h | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rectop.c | 62 | ||||
-rw-r--r-- | source/gameengine/VideoTexture/ImageBuff.cpp | 4 |
7 files changed, 401 insertions, 309 deletions
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 60c50b989fb..9990aeda92b 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -81,12 +81,6 @@ float BKE_brush_sample_tex_3D(const Scene *scene, struct Brush *br, const float float BKE_brush_sample_masktex(const Scene *scene, struct Brush *br, const float point[3], const int thread, struct ImagePool *pool); -enum BrushImBufFill { BRUSH_IMBUF_MASK, BRUSH_IMBUF_TEX, BRUSH_IMBUF_TEX_MASK }; -void BKE_brush_imbuf_new(const struct Scene *scene, struct Brush *brush, bool use_float, - enum BrushImBufFill fill, int size, struct ImBuf **imbuf, - bool use_color_correction, bool use_brush_alpha, - struct ImagePool *pool, struct rctf *mapping); - /* 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 621c41c3df7..a8c78b430be 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -758,122 +758,6 @@ float BKE_brush_sample_masktex(const Scene *scene, Brush *br, return intensity; } -static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) -{ - texco[0] = mapping->xmin + x * mapping->xmax; - texco[1] = mapping->ymin + y * mapping->ymax; - texco[2] = 0.0f; -} - -/* NOTE: only used for 2d brushes currently! and needs to stay in sync - * with brush_painter_2d_do_partial */ -void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, bool use_float, - enum BrushImBufFill fill, int bufsize, - ImBuf **outbuf, bool use_color_correction, bool use_brush_alpha, - struct ImagePool *pool, rctf *mapping) -{ - ImBuf *ibuf; - float xy[2], texco[3], 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 = (use_brush_alpha)? BKE_brush_alpha_get(scene, brush): 1.0f; - float brush_rgb[3] = {1.0f, 1.0f, 1.0f}; - int thread = 0; - - imbflag = (use_float) ? IB_rectfloat : IB_rect; - xoff = -bufsize / 2.0f + 0.5f; - yoff = -bufsize / 2.0f + 0.5f; - rowbytes = bufsize * 4; - - if (*outbuf) - ibuf = *outbuf; - else - ibuf = IMB_allocImBuf(bufsize, bufsize, 32, imbflag); - - if (use_float) { - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - copy_v3_v3(brush_rgb, brush->rgb); - if (use_color_correction) { - srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb); - } - } - - for (y = 0; y < ibuf->y; y++) { - dstf = ibuf->rect_float + y * rowbytes; - - for (x = 0; x < ibuf->x; x++, dstf += 4) { - xy[0] = x + xoff; - xy[1] = y + yoff; - - if (fill == BRUSH_IMBUF_MASK) { - copy_v3_v3(dstf, brush_rgb); - dstf[3] = alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); - } - else if (fill == BRUSH_IMBUF_TEX) { - brush_imbuf_tex_co(mapping, x, y, texco); - BKE_brush_sample_tex_3D(scene, brush, texco, dstf, thread, pool); - } - else { /* if (fill == BRUSH_IMBUF_TEX_MASK) */ - brush_imbuf_tex_co(mapping, x, y, texco); - BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); - - mul_v3_v3v3(dstf, rgba, brush_rgb); - dstf[3] = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); - } - - /* output premultiplied alpha image */ - dstf[0] *= dstf[3]; - dstf[1] *= dstf[3]; - dstf[2] *= dstf[3]; - } - } - } - else { - float alpha_f; /* final float alpha to convert to char */ - - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) - copy_v3_v3(brush_rgb, brush->rgb); - - rgb_float_to_uchar(crgb, brush->rgb); - - for (y = 0; y < ibuf->y; y++) { - dst = (unsigned char *)ibuf->rect + y * rowbytes; - - for (x = 0; x < ibuf->x; x++, dst += 4) { - xy[0] = x + xoff; - xy[1] = y + yoff; - - if (fill == BRUSH_IMBUF_MASK) { - alpha_f = alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); - - dst[0] = crgb[0]; - dst[1] = crgb[1]; - dst[2] = crgb[2]; - dst[3] = FTOCHAR(alpha_f); - } - else if (fill == BRUSH_IMBUF_TEX) { - brush_imbuf_tex_co(mapping, x, y, texco); - BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); - rgba_float_to_uchar(dst, rgba); - } - else { /* if (fill == BRUSH_IMBUF_TEX_MASK) */ - brush_imbuf_tex_co(mapping, x, y, texco); - BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); - - mul_v3_v3(rgba, brush_rgb); - alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); - - rgb_float_to_uchar(dst, rgba); - dst[3] = FTOCHAR(alpha_f); - } - } - } - } - - *outbuf = ibuf; -} - /* Unified Size and Strength */ /* XXX: be careful about setting size and unprojected radius diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index efb810455b7..55dd96eff36 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -88,17 +88,24 @@ BLI_INLINE unsigned char f_to_char(const float val) #define IMAPAINT_FLOAT_RGB_COPY(a, b) copy_v3_v3(a, b) typedef struct BrushPainterCache { - int size; /* size override, if 0 uses 2*BKE_brush_size_get(brush) */ - short flt; /* need float imbuf? */ + int size; /* size override, if 0 uses 2*BKE_brush_size_get(brush) */ + + bool use_float; /* need float imbuf? */ + bool use_color_correction; /* use color correction for float */ + bool use_masking; /* use masking? */ + + bool is_texbrush; + bool is_maskbrush; int lastsize; float lastalpha; float lastjitter; - float last_rotation; + float last_tex_rotation; + float last_mask_rotation; ImBuf *ibuf; ImBuf *texibuf; - ImBuf *maskibuf; + unsigned short *mask; } BrushPainterCache; typedef struct BrushPainter { @@ -111,7 +118,8 @@ typedef struct BrushPainter { short firsttouch; /* first paint op */ struct ImagePool *pool; /* image pool */ - rctf mapping; /* texture coordinate mapping */ + rctf tex_mapping; /* texture coordinate mapping */ + rctf mask_mapping; /* mask texture coordinate mapping */ BrushPainterCache cache; } BrushPainter; @@ -161,144 +169,314 @@ static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush) } -static void brush_painter_2d_require_imbuf(BrushPainter *painter, short flt, int size) +static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction, bool use_masking) { - if ((painter->cache.flt != flt) || (painter->cache.size != size)) { + Brush *brush = painter->brush; + + if ((painter->cache.use_float != use_float)) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.maskibuf) IMB_freeImBuf(painter->cache.maskibuf); - painter->cache.ibuf = painter->cache.maskibuf = NULL; + if (painter->cache.mask) MEM_freeN(painter->cache.mask); + painter->cache.ibuf = NULL; + painter->cache.mask = NULL; painter->cache.lastsize = -1; /* force ibuf create in refresh */ } - if (painter->cache.flt != flt) { + if (painter->cache.use_float != use_float) { if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); painter->cache.texibuf = NULL; painter->cache.lastsize = -1; /* force ibuf create in refresh */ } - painter->cache.size = size; - painter->cache.flt = flt; + painter->cache.use_float = use_float; + painter->cache.use_color_correction = use_float && use_color_correction; + painter->cache.use_masking = use_masking; + painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; + painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; } static void brush_painter_2d_free(BrushPainter *painter) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.maskibuf) IMB_freeImBuf(painter->cache.maskibuf); + if (painter->cache.mask) MEM_freeN(painter->cache.mask); MEM_freeN(painter); } -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]) +static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) +{ + texco[0] = mapping->xmin + x * mapping->xmax; + texco[1] = mapping->ymin + y * mapping->ymax; + texco[2] = 0.0f; +} + +/* create a mask with the falloff strength and optionally brush alpha */ +static unsigned short *brush_painter_mask_new(BrushPainter *painter, int size) { Scene *scene = painter->scene; Brush *brush = painter->brush; - ImBuf *ibuf, *maskibuf, *texibuf; - float *bf, *mf, *tf, *otf = NULL, texco[3], rgba[4]; - unsigned char *b, *m, *t, *ot = NULL; - int dotexold, origx = x, origy = y; - int thread = 0; - rctf mapping = painter->mapping; - - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) { - mapping.xmin += (int)pos[0] - (int)painter->startpaintpos[0]; - mapping.ymin += (int)pos[1] - (int)painter->startpaintpos[1]; - } - - ibuf = painter->cache.ibuf; - texibuf = painter->cache.texibuf; - maskibuf = painter->cache.maskibuf; - - dotexold = (oldtexibuf != NULL); - - /* not sure if it's actually needed or it's a mistake in coords/sizes - * calculation in brush_painter_fixed_tex_partial_update(), but without this - * limitation memory gets corrupted at fast strokes with quite big spacing (sergey) */ - w = min_ii(w, ibuf->x); - h = min_ii(h, ibuf->y); - - if (painter->cache.flt) { - for (; y < h; y++) { - bf = ibuf->rect_float + (y * ibuf->x + origx) * 4; - tf = texibuf->rect_float + (y * texibuf->x + origx) * 4; - mf = maskibuf->rect_float + (y * maskibuf->x + origx) * 4; - - if (dotexold) - otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + xt) * 4; - - for (x = origx; x < w; x++, bf += 4, mf += 4, tf += 4) { - if (dotexold) { - copy_v4_v4(tf, otf); - otf += 4; - } - else { - texco[0] = mapping.xmin + x * mapping.xmax; - texco[1] = mapping.ymin + y * mapping.ymax; - texco[2] = 0.0f; + bool use_masking = painter->cache.use_masking; - BKE_brush_sample_tex_3D(scene, brush, texco, tf, thread, painter->pool); - } + float alpha = (use_masking)? 1.0f: BKE_brush_alpha_get(scene, brush); + int radius = BKE_brush_size_get(scene, brush); + int xoff = -size * 0.5f + 0.5f; + int yoff = -size * 0.5f + 0.5f; - /* output premultiplied float image, mf was already premultiplied */ - bf[0] = tf[0] * tf[3] * mf[0]; - bf[1] = tf[1] * tf[3] * mf[1]; - bf[2] = tf[2] * tf[3] * mf[2]; - bf[3] = tf[3] * tf[3] * mf[3]; + unsigned short *mask, *m; + int x, y; + + mask = MEM_callocN(sizeof(unsigned short)*size*size, "brush_painter_mask"); + m = mask; + + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++, m++) { + float xy[2] = {x + xoff, y + yoff}; + float len = len_v2(xy); + float strength = alpha; + + strength *= BKE_brush_curve_strength_clamp(brush, len, radius); + + *m = (unsigned short)(65535.0f * strength); + } + } + + return mask; +} + +/* create imbuf with brush color */ +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) +{ + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + rctf tex_mapping = painter->tex_mapping; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + + bool use_masking = painter->cache.use_masking; + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + bool is_maskbrush = painter->cache.is_maskbrush; + + float alpha = (use_masking)? 1.0f: BKE_brush_alpha_get(scene, brush); + int radius = BKE_brush_size_get(scene, brush); + int xoff = -size * 0.5f + 0.5f; + int yoff = -size * 0.5f + 0.5f; + + int x, y, thread = 0; + float brush_rgb[3]; + + /* allocate image buffer */ + ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + copy_v3_v3(brush_rgb, brush->rgb); + + if (use_color_correction) + srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb); + } + else { + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill image buffer */ + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); + mul_v3_v3(rgba, brush_rgb); + } + else { + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } + + if (is_maskbrush) { + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); + } + + /* when not using masking, multiply in falloff and strength */ + if (!use_masking) { + float xy[2] = {x + xoff, y + yoff}; + float len = len_v2(xy); + + rgba[3] *= alpha * BKE_brush_curve_strength_clamp(brush, len, radius); + } + + if (use_float) { + /* write to float pixel */ + float *dstf = ibuf->rect_float + (y * size + x) * 4; + mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ + dstf[3] = rgba[3]; + } + else { + /* write to byte pixel */ + unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; + + rgb_float_to_uchar(dst, rgba); + dst[3] = FTOCHAR(rgba[3]); } } } + + return ibuf; +} + +/* update rectangular section of the brush image */ +static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, + int origx, int origy, int w, int h, int xt, int yt) +{ + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + rctf tex_mapping = painter->tex_mapping; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + + bool use_masking = painter->cache.use_masking; + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + bool is_maskbrush = painter->cache.is_maskbrush; + bool use_texture_old = (oldtexibuf != NULL); + + int x, y, thread = 0; + float brush_rgb[3]; + + ImBuf *ibuf = painter->cache.ibuf; + ImBuf *texibuf = painter->cache.texibuf; + unsigned short *mask = painter->cache.mask; + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + copy_v3_v3(brush_rgb, brush->rgb); + + if (use_color_correction) + srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb); + } else { - for (; y < h; y++) { - b = (unsigned char *)ibuf->rect + (y * ibuf->x + origx) * 4; - t = (unsigned char *)texibuf->rect + (y * texibuf->x + origx) * 4; - m = (unsigned char *)maskibuf->rect + (y * maskibuf->x + origx) * 4; - - if (dotexold) - ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + xt) * 4; - - for (x = origx; x < w; x++, b += 4, m += 4, t += 4) { - if (dotexold) { - t[0] = ot[0]; - t[1] = ot[1]; - t[2] = ot[2]; - t[3] = ot[3]; - ot += 4; + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill pixes */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (!use_texture_old) { + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); + mul_v3_v3(rgba, brush_rgb); } else { - texco[0] = mapping.xmin + x * mapping.xmax; - texco[1] = mapping.ymin + y * mapping.ymax; - texco[2] = 0.0f; + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } - BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, painter->pool); - rgba_float_to_uchar(t, rgba); + if (is_maskbrush) { + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); } + } + + if (use_float) { + /* handle float pixel */ + float *bf = ibuf->rect_float + (y * ibuf->x + x) * 4; + float *tf = texibuf->rect_float + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + float *otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + copy_v4_v4(rgba, otf); + } + + /* write to new texture buffer */ + copy_v4_v4(tf, rgba); - b[0] = t[0] * m[0] / 255; - b[1] = t[1] * m[1] / 255; - b[2] = t[2] * m[2] / 255; - b[3] = t[3] * m[3] / 255; + /* if not using masking, multiply in the mask now */ + if (!use_masking) { + unsigned short *m = mask + (y * ibuf->x + x); + rgba[3] *= *m * (1.0f / 65535.0f); + } + + /* output premultiplied float image, mf was already premultiplied */ + mul_v3_v3fl(bf, rgba, rgba[3]); + bf[3] = rgba[3]; + } + else { + unsigned char crgba[4]; + + /* handle byte pixel */ + unsigned char *b = (unsigned char *)ibuf->rect + (y * ibuf->x + x) * 4; + unsigned char *t = (unsigned char *)texibuf->rect + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + unsigned char *ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + crgba[0] = ot[0]; + crgba[1] = ot[1]; + crgba[2] = ot[2]; + crgba[3] = ot[3]; + } + else + rgba_float_to_uchar(crgba, rgba); + + /* write to new texture buffer */ + t[0] = crgba[0]; + t[1] = crgba[1]; + t[2] = crgba[2]; + t[3] = crgba[3]; + + /* if not using masking, multiply in the mask now */ + if (!use_masking) { + unsigned short *m = mask + (y * ibuf->x + x); + crgba[3] = (crgba[3] * (*m)) / 65535; + } + + /* write to brush image buffer */ + b[0] = crgba[0]; + b[1] = crgba[1]; + b[2] = crgba[2]; + b[3] = crgba[3]; } } } } -static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, const float pos[2]) +/* update the brush image by trying to reuse the cached texture result. this + * can be considerably faster for brushes that change size due to pressure or + * textures that stick to the surface where only part of the pixels are new */ +static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2]) { const Scene *scene = painter->scene; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - const int diameter = 2 * BKE_brush_size_get(scene, brush); + int diameter = 2 * BKE_brush_size_get(scene, brush); - imbflag = (cache->flt) ? IB_rectfloat : IB_rect; + /* create brush image buffer if it didn't exist yet */ + imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; if (!cache->ibuf) cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); ibuf = cache->ibuf; + /* create new texture image buffer with coordinates relative to old */ oldtexibuf = cache->texibuf; cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + if (oldtexibuf) { srcx = srcy = 0; destx = (int)painter->lastpaintpos[0] - (int)pos[0]; @@ -316,28 +494,28 @@ static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, con x1 = destx; y1 = desty; - x2 = destx + w; - y2 = desty + h; + x2 = min_ii(destx + w, ibuf->x); + y2 = min_ii(desty + h, ibuf->y); /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) - brush_painter_2d_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos); + brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); if (oldtexibuf) IMB_freeImBuf(oldtexibuf); /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) - brush_painter_2d_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos); + brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); if ((x2 < ibuf->x) && (0 < ibuf->y)) - brush_painter_2d_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos); + brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); if ((x1 < x2) && (0 < y1)) - brush_painter_2d_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos); + brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); if ((x1 < x2) && (y2 < ibuf->y)) - brush_painter_2d_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); + brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int bufsize, const float pos[2], bool do_stencil, bool do_3D, bool do_view, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const float startpos[2], const float pos[2], int mapmode, rctf *mapping) { float invw = 1.0f / (float)s->canvas->x; float invh = 1.0f / (float)s->canvas->y; @@ -345,104 +523,136 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int bufsize, const int ipos[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - bufsize / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - bufsize / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - size / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - size / 2) + 1.0f); - if (do_stencil || do_view) { + if (ELEM(mapmode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) { /* map from view coordinates of brush to region coordinates */ UI_view2d_to_region_no_clip(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_to_region_no_clip(s->v2d, (ipos[0] + bufsize) * invw, (ipos[1] + bufsize) * invh, &xmax, &ymax); + UI_view2d_to_region_no_clip(s->v2d, (ipos[0] + size) * invw, (ipos[1] + size) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ mapping->xmin = xmin; mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)bufsize; - mapping->ymax = (ymax - ymin) / (float)bufsize; + mapping->xmax = (xmax - xmin) / (float)size; + mapping->ymax = (ymax - ymin) / (float)size; } - else if (do_3D) { + else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1 */ mapping->xmin = ipos[0] * invw; mapping->ymin = ipos[1] * invh; - mapping->xmax = bufsize * invw / (float)bufsize; - mapping->ymax = bufsize * invh / (float)bufsize; + mapping->xmax = size * invw / (float)size; + mapping->ymax = size * invh / (float)size; } else { /* other mapping */ - mapping->xmin = -bufsize * 0.5f + 0.5f; - mapping->ymin = -bufsize * 0.5f + 0.5f; + mapping->xmin = -size * 0.5f + 0.5f; + mapping->ymin = -size * 0.5f + 0.5f; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } + + if (mapmode == MTEX_MAP_MODE_TILED) { + /* offset relative to start position for tiled */ + mapping->xmin += (int)pos[0] - (int)startpos[0]; + mapping->ymin += (int)pos[1] - (int)startpos[1]; + } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], bool use_color_correction) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2]) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; - MTex *mtex = &brush->mtex; const int diameter = 2 * BKE_brush_size_get(scene, brush); const int size = (cache->size) ? cache->size : diameter; - const short flt = cache->flt; const float alpha = BKE_brush_alpha_get(scene, brush); - const bool do_3D = brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D; - const bool do_tiled = brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED; - const bool do_stencil = brush->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL; - const bool do_random = brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM; - const bool do_view = brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW; - const bool use_brush_alpha = !s->do_masking; - float rotation = -mtex->rot; + const bool use_masking = painter->cache.use_masking; + + bool do_random = false; + bool do_partial_update = false; + bool do_view = false; + float tex_rotation = -brush->mtex.rot; + float mask_rotation = -brush->mask_mtex.rot; + + /* determine how can update based on textures used */ + if (painter->cache.is_texbrush) { + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + do_view = true; + tex_rotation += ups->brush_rotation; + } + else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + do_random = true; + else + do_partial_update = true; + + brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, + brush->mtex.brush_map_mode, &painter->tex_mapping); + } + + if (painter->cache.is_maskbrush) { + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + do_view = true; + mask_rotation += ups->brush_rotation; + } + else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + do_random = true; + else + do_partial_update = true; - if (do_view) { - rotation += ups->brush_rotation; + brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, + pos, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); } - brush_painter_2d_tex_mapping(s, size, pos, do_stencil, do_3D, do_view, &painter->mapping); + if (do_view || do_random) + do_partial_update = false; painter->pool = BKE_image_pool_new(); + /* detect if we need to recreate image brush buffer */ if (diameter != cache->lastsize || - (use_brush_alpha && alpha != cache->lastalpha) || + alpha != cache->lastalpha || brush->jitter != cache->lastjitter || - rotation != cache->last_rotation || + tex_rotation != cache->last_tex_rotation || + mask_rotation != cache->last_mask_rotation || do_random) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); cache->ibuf = NULL; } - if (cache->maskibuf) { - IMB_freeImBuf(cache->maskibuf); - cache->maskibuf = NULL; + if (cache->mask) { + MEM_freeN(cache->mask); + cache->mask = NULL; } - if (do_tiled || do_3D || do_stencil) { - BKE_brush_imbuf_new(scene, brush, flt, BRUSH_IMBUF_MASK, - size, &cache->maskibuf, - use_color_correction, use_brush_alpha, - painter->pool, &painter->mapping); - - brush_painter_2d_tiled_tex_partial_update(painter, pos); + if (do_partial_update) { + /* do partial update of texture + recreate mask */ + cache->mask = brush_painter_mask_new(painter, size); + brush_painter_imbuf_partial_update(painter, pos); } else { - BKE_brush_imbuf_new(scene, brush, flt, BRUSH_IMBUF_TEX_MASK, - size, &cache->ibuf, - use_color_correction, use_brush_alpha, - painter->pool, &painter->mapping); + /* create brush and mask from scratch */ + if (use_masking) + cache->mask = brush_painter_mask_new(painter, size); + cache->ibuf = brush_painter_imbuf_new(painter, size); } cache->lastsize = diameter; cache->lastalpha = alpha; cache->lastjitter = brush->jitter; - cache->last_rotation = rotation; + cache->last_tex_rotation = tex_rotation; + cache->last_mask_rotation = mask_rotation; } - else if ((do_tiled || do_3D || do_stencil) && mtex && mtex->tex) { + else if (do_partial_update) { + /* do only partial update of texture */ int dx = (int)painter->lastpaintpos[0] - (int)pos[0]; int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; - if ((dx != 0) || (dy != 0)) - brush_painter_2d_tiled_tex_partial_update(painter, pos); + if ((dx != 0) || (dy != 0)) { + brush_painter_imbuf_partial_update(painter, pos); + } } BKE_image_pool_free(painter->pool); @@ -616,7 +826,7 @@ 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, ibufb, ibuf, NULL, 0, region[a].destx, region[a].desty, + IMB_rectblend(ibufb, ibufb, ibuf, NULL, 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); @@ -630,9 +840,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, clonebuf, ibuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, + IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, IMB_BLEND_COPY_RGB); - IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, + IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, IMB_BLEND_COPY_ALPHA); return clonebuf; @@ -644,7 +854,7 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ 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]) +static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL; @@ -719,7 +929,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const else tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); - IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, mask_max, + IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, + maskb, mask_max, region[a].destx, region[a].desty, origx, origy, region[a].srcx, region[a].srcy, @@ -729,7 +940,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const } else { /* no masking, composite brush directly onto canvas */ - IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, 0, + IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, NULL, 0, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, @@ -845,14 +1056,15 @@ int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int era else { copy_v2_v2(painter->lastpaintpos, olduv); } + /* OCIO_TODO: float buffers are now always linear, so always use color correction * this should probably be changed when texture painting color space is supported */ - brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0); + brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data, s->do_masking); - brush_painter_2d_refresh_cache(s, painter, newuv, is_data == false); + brush_painter_2d_refresh_cache(s, painter, newuv); - if (paint_2d_op(s, painter->cache.ibuf, olduv, newuv)) { + if (paint_2d_op(s, painter->cache.ibuf, painter->cache.mask, olduv, newuv)) { imapaint_image_update(s->sima, s->image, ibuf, false); BKE_image_release_ibuf(s->image, ibuf, NULL); redraw |= 1; diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 7523d10996a..0aef1ccf9fe 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -3669,7 +3669,7 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const } rgb_float_to_uchar(rgba_ub, rgb); - rgba_ub[3] = 255 * mask; + rgba_ub[3] = FTOCHAR(mask); if (ps->do_masking) { IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, rgba_ub, ps->blend); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 7253a61092c..819026facc6 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -179,7 +179,7 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, 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 *obuf, struct ImBuf *sbuf, - unsigned short *mask, unsigned short mask_max, + unsigned short *dmask, unsigned short *smask, 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 3d80be93ded..71f81239547 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -226,20 +226,22 @@ static void imb_rectclip3(ImBuf *dbuf, ImBuf *obuf, 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, dbuf, sbuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, width, height, IMB_BLEND_COPY); + IMB_rectblend(dbuf, dbuf, sbuf, NULL, 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(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskrect, unsigned short mask_max, +void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, + unsigned short *smask, 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, *orect, *srect = NULL, *dr, *or, *sr; float *drectf = NULL, *orectf, *srectf = NULL, *drf, *orf, *srf; - unsigned short *mr; + unsigned short *smaskrect = smask, *smr; + unsigned short *dmaskrect = dmask, *dmr; int do_float, do_char, srcskip, destskip, origskip, x; IMB_blend_func func = NULL; IMB_blend_func_float func_float = NULL; @@ -264,8 +266,8 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskre orectf = obuf->rect_float + (origy * obuf->x + origx) * 4; } - if (maskrect) - maskrect += origy * obuf->x + origx; + if (dmaskrect) + dmaskrect += origy * obuf->x + origx; destskip = dbuf->x; origskip = obuf->x; @@ -274,6 +276,9 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskre if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx; if (do_float) srectf = sbuf->rect_float + (srcy * sbuf->x + srcx) * 4; srcskip = sbuf->x; + + if (smaskrect) + smaskrect += srcy * sbuf->x + srcx; } else { srect = drect; @@ -392,32 +397,34 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskre or = orect; sr = srect; - if (maskrect) { + if (dmaskrect && smaskrect) { /* mask accumulation for painting */ - mr = maskrect; + dmr = dmaskrect; + smr = smaskrect; - for (x = width; x > 0; x--, dr++, or++, sr++, mr++) { + for (x = width; x > 0; x--, dr++, or++, sr++, dmr++, smr++) { unsigned char *src = (unsigned char *)sr; - if (src[3]) { - unsigned short mask = *mr + divide_round_i((mask_max - *mr) * src[3], 255); + if (src[3] && *smr) { + unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535); - if (mask > *mr) { + if (mask > *dmr) { unsigned char mask_src[4]; - *mr = mask; + *dmr = mask; mask_src[0] = src[0]; mask_src[1] = src[1]; mask_src[2] = src[2]; - mask_src[3] = mask >> 8; + mask_src[3] = divide_round_i(src[3] * mask, 65535); func((unsigned char *)dr, (unsigned char *)or, mask_src); } } } - maskrect += origskip; + dmaskrect += origskip; + smaskrect += srcskip; } else { /* regular blending */ @@ -437,33 +444,28 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskre orf = orectf; srf = srectf; - if (maskrect) { + if (dmaskrect && smaskrect) { /* mask accumulation for painting */ - mr = maskrect; + dmr = dmaskrect; + smr = smaskrect; - 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); + for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, smr++) { + if (srf[3] != 0 && *smr) { + unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535); - if (mask > *mr) { + if (mask > *dmr) { 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; + *dmr = mask; + mul_v4_v4fl(mask_srf, srf, mask * (1.0f / 65535.0f)); func_float(drf, orf, mask_srf); } } } - maskrect += origskip; + dmaskrect += origskip; + smaskrect += srcskip; } else { /* regular blending */ diff --git a/source/gameengine/VideoTexture/ImageBuff.cpp b/source/gameengine/VideoTexture/ImageBuff.cpp index eb68ea2425d..6cc8d287e66 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, m_imbuf, tmpbuf, NULL, 0, x, y, x, y, 0, 0, width, height, (IMB_BlendMode)mode); + IMB_rectblend(m_imbuf, m_imbuf, tmpbuf, NULL, 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, m_imbuf, img->m_imbuf, NULL, 0, x, y, 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, 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; |