Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_brush.h6
-rw-r--r--source/blender/blenkernel/intern/brush.c116
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c518
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/imbuf/IMB_imbuf.h2
-rw-r--r--source/blender/imbuf/intern/rectop.c62
-rw-r--r--source/gameengine/VideoTexture/ImageBuff.cpp4
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;