diff options
-rw-r--r-- | source/blender/blenkernel/BKE_brush.h | 13 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 391 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rectop.c | 21 | ||||
-rw-r--r-- | source/blender/include/BDR_drawmesh.h | 2 | ||||
-rw-r--r-- | source/blender/include/BDR_imagepaint.h | 5 | ||||
-rw-r--r-- | source/blender/include/butspace.h | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 1 | ||||
-rw-r--r-- | source/blender/src/buttons_editing.c | 76 | ||||
-rw-r--r-- | source/blender/src/buttons_shading.c | 2 | ||||
-rw-r--r-- | source/blender/src/drawimage.c | 1 | ||||
-rw-r--r-- | source/blender/src/drawmesh.c | 28 | ||||
-rw-r--r-- | source/blender/src/drawview.c | 11 | ||||
-rw-r--r-- | source/blender/src/editface.c | 2 | ||||
-rw-r--r-- | source/blender/src/header_view3d.c | 8 | ||||
-rw-r--r-- | source/blender/src/imagepaint.c | 631 | ||||
-rw-r--r-- | source/blender/src/space.c | 16 | ||||
-rw-r--r-- | source/blender/src/usiblender.c | 2 |
17 files changed, 806 insertions, 405 deletions
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index a55c8913c7f..52f86ef9c41 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -55,8 +55,10 @@ int brush_clone_image_set_nr(struct Brush *brush, int nr); int brush_clone_image_delete(struct Brush *brush); /* sampling */ -void brush_sample(struct Brush *brush, float *xy, float dist, float *rgb, float *alpha, short texonly); -struct ImBuf *brush_imbuf_new(struct Brush *brush, short flt, short texonly, int size); +float brush_sample_falloff(struct Brush *brush, float dist); +void brush_sample_tex(struct Brush *brush, float *xy, float *rgba); +void brush_imbuf_new(struct Brush *brush, short flt, short texfalloff, int size, + struct ImBuf **imbuf); /* painting */ struct BrushPainter; @@ -64,8 +66,11 @@ typedef struct BrushPainter BrushPainter; typedef int (*BrushFunc)(void *user, struct ImBuf *ibuf, float *lastpos, float *pos); BrushPainter *brush_painter_new(struct Brush *brush); -void brush_painter_require_imbuf(BrushPainter *painter, short flt, short texonly, int size); -int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, void *user); +void brush_painter_require_imbuf(BrushPainter *painter, short flt, + short texonly, int size); +int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, + double time, void *user); +void brush_painter_break_stroke(BrushPainter *painter); void brush_painter_free(BrushPainter *painter); #endif diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 61d0b42d30d..6abe66be36b 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -299,7 +299,7 @@ void brush_check_exists(Brush **brush) /* Brush Sampling */ -static float brush_sample_falloff(Brush *brush, float dist) +float brush_sample_falloff(Brush *brush, float dist) { float a, outer, inner; @@ -319,14 +319,9 @@ static float brush_sample_falloff(Brush *brush, float dist) return 0.0f; } -void brush_sample(Brush *brush, float *xy, float dist, float *rgb, float *alpha, short texonly) +void brush_sample_tex(Brush *brush, float *xy, float *rgba) { - if (alpha) { - if (texonly) *alpha= 1.0; - else *alpha= brush_sample_falloff(brush, dist); - } - - if (xy && brush->mtex[0] && brush->mtex[0]->tex) { + if (brush->mtex[0] && brush->mtex[0]->tex) { float co[3], tin, tr, tg, tb, ta; int hasrgb; @@ -336,81 +331,133 @@ void brush_sample(Brush *brush, float *xy, float dist, float *rgb, float *alpha, hasrgb= externtex(brush->mtex[0], co, &tin, &tr, &tg, &tb, &ta); - if (rgb) { - if (hasrgb) { - rgb[0]= tr*brush->rgb[0]; - rgb[1]= tg*brush->rgb[1]; - rgb[2]= tb*brush->rgb[2]; - } - else { - rgb[0]= tin*brush->rgb[0]; - rgb[1]= tin*brush->rgb[1]; - rgb[2]= tin*brush->rgb[2]; - } + if (hasrgb) { + rgba[0]= tr; + rgba[1]= tg; + rgba[2]= tb; + rgba[3]= ta; + } + else { + rgba[0]= tin; + rgba[1]= tin; + rgba[2]= tin; + rgba[3]= 1.0f; } - if (alpha && hasrgb) - *alpha *= ta; } - else if (rgb) - VECCOPY(rgb, brush->rgb) + else if (rgba) + rgba[0]= rgba[1]= rgba[2]= rgba[3]= 1.0f; } #define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) -ImBuf *brush_imbuf_new(Brush *brush, short flt, short texonly, int size) +void brush_imbuf_new(Brush *brush, short flt, short texfall, int size, ImBuf **outbuf) { ImBuf *ibuf; - float w_2, h_2, xy[2], dist, rgba[3], *dstf; - unsigned int x, y, rowbytes; - char *dst; + float xy[2], dist, rgba[3], *dstf; + int x, y, rowbytes, xoff, yoff, imbflag; + char *dst, crgb[3]; - if (texonly && !(brush->mtex[0] && brush->mtex[0]->tex)) - return NULL; - - w_2 = size/2.0f; - h_2 = size/2.0f; + imbflag= (flt)? IB_rectfloat: IB_rect; + xoff = -size/2.0f + 0.5f; + yoff = -size/2.0f + 0.5f; rowbytes= size*4; - if (flt) { - ibuf= IMB_allocImBuf(size, size, 32, IB_rectfloat, 0); + if (*outbuf) + ibuf= *outbuf; + else + ibuf= IMB_allocImBuf(size, size, 32, imbflag, 0); + if (flt) { 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 + 0.5f - w_2; - xy[1] = y + 0.5f - h_2; - dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); + xy[0] = x + xoff; + xy[1] = y + yoff; + + if (texfall == 0) { + dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); - brush_sample(brush, xy, dist, dstf, dstf+3, texonly); + VECCOPY(dstf, brush->rgb); + dstf[3]= brush_sample_falloff(brush, dist); + } + else if (texfall == 1) { + brush_sample_tex(brush, xy, dstf); + } + else { + dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); + + brush_sample_tex(brush, xy, rgba); + + dstf[0] = rgba[0]*brush->rgb[0]; + dstf[1] = rgba[1]*brush->rgb[1]; + dstf[2] = rgba[2]*brush->rgb[2]; + dstf[3] = rgba[3]*brush_sample_falloff(brush, dist); + } } } } else { - ibuf= IMB_allocImBuf(size, size, 32, IB_rect, 0); + crgb[0]= FTOCHAR(brush->rgb[0]); + crgb[1]= FTOCHAR(brush->rgb[1]); + crgb[2]= FTOCHAR(brush->rgb[2]); for (y=0; y < ibuf->y; y++) { dst = (char*)ibuf->rect + y*rowbytes; for (x=0; x < ibuf->x; x++, dst+=4) { - xy[0] = x + 0.5f - w_2; - xy[1] = y + 0.5f - h_2; - dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); - - brush_sample(brush, xy, dist, rgba, rgba+3, texonly); - dst[0]= FTOCHAR(rgba[0]); - dst[1]= FTOCHAR(rgba[1]); - dst[2]= FTOCHAR(rgba[2]); - dst[3]= FTOCHAR(rgba[3]); + xy[0] = x + xoff; + xy[1] = y + yoff; + + if (texfall == 0) { + dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); + + dst[0]= crgb[0]; + dst[1]= crgb[1]; + dst[2]= crgb[2]; + dst[3]= FTOCHAR(brush_sample_falloff(brush, dist)); + } + else if (texfall == 1) { + brush_sample_tex(brush, xy, rgba); + dst[0]= FTOCHAR(rgba[0]); + dst[1]= FTOCHAR(rgba[1]); + dst[2]= FTOCHAR(rgba[2]); + dst[3]= FTOCHAR(rgba[3]); + } + else { + dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); + + brush_sample_tex(brush, xy, rgba); + dst[0] = FTOCHAR(rgba[0]*brush->rgb[0]); + dst[1] = FTOCHAR(rgba[1]*brush->rgb[1]); + dst[2] = FTOCHAR(rgba[2]*brush->rgb[2]); + dst[3] = FTOCHAR(rgba[3]*brush_sample_falloff(brush, dist)); + } } } } - return ibuf; + *outbuf= ibuf; } /* Brush Painting */ +typedef struct BrushPainterCache { + short enabled; + + int size; /* size override, if 0 uses brush->size */ + short flt; /* need float imbuf? */ + short texonly; /* no alpha, color or fallof, only texture in imbuf */ + + int lastsize; + float lastalpha; + float lastinnerradius; + + ImBuf *ibuf; + ImBuf *texibuf; + ImBuf *maskibuf; +} BrushPainterCache; + struct BrushPainter { Brush *brush; @@ -418,24 +465,14 @@ struct BrushPainter { float accumdistance; /* accumulated distance of brush since last paint op */ float lastpaintpos[2]; /* position of last paint op */ + float startpaintpos[2]; /* position of first paint */ double accumtime; /* accumulated time since last paint op (airbrush) */ double lasttime; /* time of last update */ short firsttouch; /* first paint op */ - struct BrushPainterImbufCache { - int size; /* size override, if 0 uses brush->size */ - short flt; /* need float imbuf? */ - short texonly; /* no alpha, color or fallof, only texture in imbuf */ - short enabled; - - int lastsize; - float lastalpha; - float lastinnerradius; - - ImBuf *ibuf; - } cache; + BrushPainterCache cache; }; BrushPainter *brush_painter_new(Brush *brush) @@ -444,42 +481,226 @@ BrushPainter *brush_painter_new(Brush *brush) painter->brush= brush; painter->firsttouch= 1; + painter->cache.lastsize= -1; /* force ibuf create in refresh */ return painter; } void brush_painter_require_imbuf(BrushPainter *painter, short flt, short texonly, int size) { - painter->cache.size = size; - painter->cache.flt = flt; - painter->cache.texonly = texonly; - painter->cache.enabled = 1; + if ((painter->cache.flt != flt) || (painter->cache.size != size) || + ((painter->cache.texonly != texonly) && texonly)) { + 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; + painter->cache.lastsize= -1; /* force ibuf create in refresh */ + } + + if (painter->cache.flt != flt) { + 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.texonly= texonly; + painter->cache.enabled= 1; } void brush_painter_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); MEM_freeN(painter); } -static void brush_painter_refresh_cache(BrushPainter *painter) +static void brush_painter_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, int x, int y, int w, int h, int xt, int yt, float *pos) { Brush *brush= painter->brush; + ImBuf *ibuf, *maskibuf, *texibuf; + float *bf, *mf, *tf, *otf=NULL, xoff, yoff, xy[2], rgba[4]; + char *b, *m, *t, *ot= NULL; + int dotexold, origx= x, origy= y; + + xoff = -brush->size/2.0f + 0.5f; + yoff = -brush->size/2.0f + 0.5f; + xoff += (int)pos[0] - (int)painter->startpaintpos[0]; + yoff += (int)pos[1] - (int)painter->startpaintpos[1]; + + ibuf = painter->cache.ibuf; + texibuf = painter->cache.texibuf; + maskibuf = painter->cache.maskibuf; + + dotexold = (oldtexibuf != NULL); + + 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) { + VECCOPY(tf, otf); + tf[3] = otf[3]; + otf += 4; + } + else { + xy[0] = x + xoff; + xy[1] = y + yoff; - if ((brush->size != painter->cache.lastsize) - || (brush->alpha != painter->cache.lastalpha) - || (brush->innerradius != painter->cache.lastinnerradius)) { + brush_sample_tex(brush, xy, tf); + } - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); + bf[0] = tf[0]*mf[0]; + bf[1] = tf[1]*mf[1]; + bf[2] = tf[2]*mf[2]; + bf[3] = tf[3]*mf[3]; + } + } + } + else { + for (; y < h; y++) { + b = (char*)ibuf->rect + (y*ibuf->x + origx)*4; + t = (char*)texibuf->rect + (y*texibuf->x + origx)*4; + m = (char*)maskibuf->rect + (y*maskibuf->x + origx)*4; + + if (dotexold) + ot = (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; + } + else { + xy[0] = x + xoff; + xy[1] = y + yoff; + + brush_sample_tex(brush, xy, rgba); + t[0]= FTOCHAR(rgba[0]); + t[1]= FTOCHAR(rgba[1]); + t[2]= FTOCHAR(rgba[2]); + t[3]= FTOCHAR(rgba[3]); + } - painter->cache.ibuf= brush_imbuf_new(brush, - painter->cache.flt, painter->cache.texonly, - painter->cache.size? painter->cache.size: brush->size); + 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; + } + } + } +} - painter->cache.lastsize= brush->size; - painter->cache.lastalpha= brush->alpha; - painter->cache.lastinnerradius= brush->innerradius; +void brush_painter_fixed_tex_partial_update(BrushPainter *painter, float *pos) +{ + Brush *brush= painter->brush; + BrushPainterCache *cache= &painter->cache; + ImBuf *oldtexibuf, *ibuf; + int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + imbflag= (cache->flt)? IB_rectfloat: IB_rect; + if (!cache->ibuf) + cache->ibuf= IMB_allocImBuf(brush->size, brush->size, 32, imbflag, 0); + ibuf= cache->ibuf; + + oldtexibuf= cache->texibuf; + cache->texibuf= IMB_allocImBuf(brush->size, brush->size, 32, imbflag, 0); + + if (oldtexibuf) { + srcx= srcy= 0; + destx= (int)painter->lastpaintpos[0] - (int)pos[0]; + desty= (int)painter->lastpaintpos[1] - (int)pos[1]; + w= oldtexibuf->x; + h= oldtexibuf->y; + + IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } + else { + srcx= srcy= 0; + destx= desty= 0; + w= h= 0; + } + + x1= destx; + y1= desty; + x2= destx+w; + y2= desty+h; + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_do_partial(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy, pos); + + if (oldtexibuf) + IMB_freeImBuf(oldtexibuf); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < ibuf->y)) + brush_painter_do_partial(painter, NULL, 0, 0, x1, ibuf->y, 0, 0, pos); + if ((x2 < ibuf->x) && (0 < ibuf->y)) + brush_painter_do_partial(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0, pos); + if ((x1 < x2) && (0 < y1)) + brush_painter_do_partial(painter, NULL, x1, 0, x2, y1, 0, 0, pos); + if ((x1 < x2) && (y2 < ibuf->y)) + brush_painter_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos); +} + +static void brush_painter_refresh_cache(BrushPainter *painter, float *pos) +{ + Brush *brush= painter->brush; + BrushPainterCache *cache= &painter->cache; + int size; + short flt; + + if ((brush->size != cache->lastsize) || (brush->alpha != cache->lastalpha) + || (brush->innerradius != cache->lastinnerradius)) { + if (cache->ibuf) { + IMB_freeImBuf(cache->ibuf); + cache->ibuf= NULL; + } + if (cache->maskibuf) { + IMB_freeImBuf(cache->maskibuf); + cache->maskibuf= NULL; + } + + flt= cache->flt; + size= (cache->size)? cache->size: brush->size; + + if (!(brush->mtex[0] && brush->mtex[0]->tex) || (brush->mtex[0]->tex->type==0)) { + brush_imbuf_new(brush, flt, 0, size, &cache->ibuf); + } + else if (brush->flag & BRUSH_FIXED_TEX) { + brush_imbuf_new(brush, flt, 0, size, &cache->maskibuf); + brush_painter_fixed_tex_partial_update(painter, pos); + } + else + brush_imbuf_new(brush, flt, 2, size, &cache->ibuf); + + cache->lastsize= brush->size; + cache->lastalpha= brush->alpha; + cache->lastinnerradius= brush->innerradius; + } + else if ((brush->flag & BRUSH_FIXED_TEX) && brush->mtex[0] && brush->mtex[0]->tex) { + 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_fixed_tex_partial_update(painter, pos); + } +} + +void brush_painter_break_stroke(BrushPainter *painter) +{ + painter->firsttouch= 1; } int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, void *user) @@ -489,7 +710,11 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl if (painter->firsttouch) { /* paint exactly once on first touch */ - if (painter->cache.enabled) brush_painter_refresh_cache(painter); + painter->startpaintpos[0]= pos[0]; + painter->startpaintpos[1]= pos[1]; + + if (painter->cache.enabled) + brush_painter_refresh_cache(painter, pos); totpaintops += func(user, painter->cache.ibuf, pos, pos); painter->lastpaintpos[0]= pos[0]; @@ -547,8 +772,10 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl paintpos[0]= painter->lastmousepos[0] + dmousepos[0]*step; paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step; - if (painter->cache.enabled) brush_painter_refresh_cache(painter); - totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); + if (painter->cache.enabled) + brush_painter_refresh_cache(painter, paintpos); + totpaintops += + func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); painter->lastpaintpos[0]= paintpos[0]; painter->lastpaintpos[1]= paintpos[1]; @@ -570,8 +797,10 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl painter->accumtime -= painttime; while (painter->accumtime >= brush->rate) { - if (painter->cache.enabled) brush_painter_refresh_cache(painter); - totpaintops += func(user, painter->cache.ibuf, painter->lastmousepos, pos); + if (painter->cache.enabled) + brush_painter_refresh_cache(painter, paintpos); + totpaintops += + func(user, painter->cache.ibuf, painter->lastmousepos, pos); painter->accumtime -= brush->rate; } diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 59fcd3e25e2..b56c0df0105 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -250,23 +250,23 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, if (dbuf == NULL) return; if (*destx < 0) { - *srcx -= *destx ; - *width += *destx ; + *srcx -= *destx; + *width += *destx; *destx = 0; } if (*srcx < 0) { - *destx -= *srcx ; - *width += *destx ; + *destx -= *srcx; + *width += *destx; *srcx = 0; } if (*desty < 0) { - *srcy -= *desty ; - *height += *desty ; + *srcy -= *desty; + *height += *desty; *desty = 0; } if (*srcy < 0) { - *desty -= *srcy ; - *height += *desty ; + *desty -= *srcy; + *height += *desty; *srcy = 0; } @@ -281,6 +281,11 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, tmp = sbuf->y - *srcy; if (*height > tmp) *height = tmp; } + + if ((*height <= 0) || (*width <= 0)) { + *width = 0; + *height = 0; + } } /* copy and blend */ diff --git a/source/blender/include/BDR_drawmesh.h b/source/blender/include/BDR_drawmesh.h index 7463cffa6ef..b2b68b25d60 100644 --- a/source/blender/include/BDR_drawmesh.h +++ b/source/blender/include/BDR_drawmesh.h @@ -70,6 +70,8 @@ int get_linear_mipmap(void); */ void clear_realtime_image_cache(void); + +void update_realtime_image(struct Image *ima, int x, int y, int w, int h); void free_realtime_image(struct Image *ima); void free_all_realtime_images(void); void make_repbind(struct Image *ima); diff --git a/source/blender/include/BDR_imagepaint.h b/source/blender/include/BDR_imagepaint.h index 9ea9c9a107a..e687d220906 100644 --- a/source/blender/include/BDR_imagepaint.h +++ b/source/blender/include/BDR_imagepaint.h @@ -35,9 +35,10 @@ void imagepaint_redraw_tool(void); void imagepaint_pick(short mousebutton); +void imagepaint_paint(short mousebutton, short texturepaint); -void imagepaint_paint(short mousebutton); -void texturepaint_paint(short mousebutton); +void imagepaint_undo(); +void free_imagepaint(); #endif /* BDR_IMAGEPAINT_H */ diff --git a/source/blender/include/butspace.h b/source/blender/include/butspace.h index 63a5face413..a73c92cc2f5 100644 --- a/source/blender/include/butspace.h +++ b/source/blender/include/butspace.h @@ -544,6 +544,7 @@ void curvemap_buttons(struct uiBlock *block, struct CurveMapping *cumap, char la #define B_BRUSHLOCAL 2853 #define B_BRUSHCHANGE 2854 #define B_BTEXBROWSE 2855 +#define B_BTEXDELETE 2856 /* *********************** */ #define B_RADIOBUTS 3000 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 1d620de8979..a57b3d3d687 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -64,6 +64,7 @@ typedef struct Brush { /* Brush.flag */ #define BRUSH_AIRBRUSH 1 #define BRUSH_TORUS 2 +#define BRUSH_FIXED_TEX 4 /* Brush.blend */ #define BRUSH_BLEND_MIX 0 diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index baa3d27be90..c3e45f49123 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -3904,26 +3904,42 @@ void do_fpaintbuts(unsigned short event) } break; case B_BTEXBROWSE: - if(G.scene->toolsettings->imapaint.brush==0) return; - if(G.buts->menunr==-2) { - MTex *mtex= G.scene->toolsettings->imapaint.brush->mtex[0]; - ID *id= (ID*)((mtex)? mtex->tex: NULL); - activate_databrowse(id, ID_TE, 0, B_BTEXBROWSE, &G.buts->menunr, do_global_buttons); - break; + if(settings->imapaint.brush) { + Brush *brush= settings->imapaint.brush; + + if(G.buts->menunr==-2) { + MTex *mtex= brush->mtex[brush->texact]; + ID *id= (ID*)((mtex)? mtex->tex: NULL); + activate_databrowse(id, ID_TE, 0, B_BTEXBROWSE, &G.buts->menunr, do_global_buttons); + break; + } + else if(G.buts->menunr < 0) break; + + if(brush_texture_set_nr(brush, G.buts->menunr)) { + BIF_undo_push("Browse Brush Texture"); + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } } - else if(G.buts->menunr < 0) break; - - if(brush_texture_set_nr(G.scene->toolsettings->imapaint.brush, G.buts->menunr)) { - BIF_undo_push("Browse Brush Texture"); - allqueue(REDRAWBUTSSHADING, 0); - allqueue(REDRAWBUTSEDIT, 0); - allqueue(REDRAWIMAGE, 0); + break; + case B_BTEXDELETE: + if(settings->imapaint.brush) { + if (brush_texture_delete(settings->imapaint.brush)) { + BIF_undo_push("Unlink Brush Texture"); + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } } break; + case B_BRUSHCHANGE: + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); + break; } } - /* -------------------- MODE: vpaint ------------------- */ static void editing_panel_mesh_paint(void) @@ -3986,7 +4002,7 @@ static void editing_panel_mesh_paint(void) uiBlockEndAlign(block); } } - else { // if(G.f & G_VERTEXPAINT) { + else if(G.f & G_VERTEXPAINT) { extern VPaint Gvp; /* from vpaint */ uiBlockBeginAlign(block); @@ -4025,7 +4041,6 @@ static void editing_panel_mesh_paint(void) uiDefButF(block, NUM, B_DIFF, "Gamma:", 1174,0,102,19, &Gvp.gamma, 0.1, 5.0, 10, 0, "Change the clarity of the vertex colors"); uiBlockEndAlign(block); } -#if 0 else { /* texture paint */ ToolSettings *settings= G.scene->toolsettings; Brush *brush= settings->imapaint.brush; @@ -4035,9 +4050,9 @@ static void editing_panel_mesh_paint(void) yco= 160; uiBlockBeginAlign(block); - uiDefButS(block, ROW, B_BRUSHCHANGE, "Draw", 0 ,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_DRAW, 0, 0, "Draw brush"); - uiDefButS(block, ROW, B_BRUSHCHANGE, "Soften", 80 ,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SOFTEN, 0, 0, "Soften brush"); - uiDefButS(block, ROW, B_BRUSHCHANGE, "Smear", 160,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SMEAR, 0, 0, "Smear brush"); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Draw", 0 ,yco,108,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_DRAW, 0, 0, "Draw brush"); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Soften", 108 ,yco,106,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SOFTEN, 0, 0, "Soften brush"); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Smear", 214,yco,106,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SMEAR, 0, 0, "Smear brush"); uiBlockEndAlign(block); yco -= 30; @@ -4049,13 +4064,13 @@ static void editing_panel_mesh_paint(void) if(brush && !brush->id.lib) { butw= 320-(xco+10); - uiDefButS(block, MENU, B_SIMANOTHING, "Mix %x0|Add %x1|Subtract %x2|Multiply %x3|Lighten %x4|Darken %x5", xco+10,yco,butw,19, &brush->blend, 0, 0, 0, 0, "Blending method for applying brushes"); + uiDefButS(block, MENU, B_NOP, "Mix %x0|Add %x1|Subtract %x2|Multiply %x3|Lighten %x4|Darken %x5", xco+10,yco,butw,19, &brush->blend, 0, 0, 0, 0, "Blending method for applying brushes"); - uiDefButBitS(block, TOG|BIT, BRUSH_TORUS, B_SIMABRUSHCHANGE, "Wrap", xco+10,yco-25,butw,19, &brush->flag, 0, 0, 0, 0, "Enables torus wrapping"); + uiDefButBitS(block, TOG|BIT, BRUSH_TORUS, B_BRUSHCHANGE, "Wrap", xco+10,yco-25,butw,19, &brush->flag, 0, 0, 0, 0, "Enables torus wrapping"); uiBlockBeginAlign(block); - uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_SIMABRUSHCHANGE, "Airbrush", xco+10,yco-50,butw,19, &brush->flag, 0, 0, 0, 0, "Keep applying paint effect while holding mouse (spray)"); - uiDefButF(block, NUM, B_SIMANOTHING, "Rate ", xco+10,yco-70,butw,19, &brush->rate, 0.01, 1.0, 0, 0, "Number of paints per second for Airbrush"); + uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_BRUSHCHANGE, "Airbrush", xco+10,yco-50,butw,19, &brush->flag, 0, 0, 0, 0, "Keep applying paint effect while holding mouse (spray)"); + uiDefButF(block, NUM, B_NOP, "Rate ", xco+10,yco-70,butw,19, &brush->rate, 0.01, 1.0, 0, 0, "Number of paints per second for Airbrush"); uiBlockEndAlign(block); yco -= 25; @@ -4065,15 +4080,18 @@ static void editing_panel_mesh_paint(void) uiDefButF(block, NUMSLI, B_NOP, "Opacity ", 0,yco-20,200,19, &brush->alpha, 0.0, 1.0, 0, 0, "The amount of pressure on the brush"); uiDefButI(block, NUMSLI, B_NOP, "Size ", 0,yco-40,200,19, &brush->size, 1, 200, 0, 0, "The size of the brush"); uiDefButF(block, NUMSLI, B_NOP, "Falloff ", 0,yco-60,200,19, &brush->innerradius, 0.0, 1.0, 0, 0, "The fall off radius of the brush"); - - if(brush->flag & BRUSH_AIRBRUSH) - uiDefButF(block, NUMSLI, B_NOP, "Flow ", 0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Paint Flow for Air Brush"); - else - uiDefButF(block, NUMSLI, B_NOP, "Stepsize ",0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating Paint On %% of Brush diameter"); + uiDefButF(block, NUMSLI, B_NOP, "Spacing ",0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating paint on %% of brush diameter"); uiBlockEndAlign(block); + + yco -= 110; + + uiBlockSetCol(block, TH_BUT_SETTING2); + id= (brush->mtex[0])? (ID*)brush->mtex[0]->tex: NULL; + xco= std_libbuttons(block, 0, yco, 0, NULL, B_BTEXBROWSE, ID_TE, 0, id, NULL, &(G.buts->menunr), 0, 0, B_BTEXDELETE, 0, 0); + /*uiDefButBitS(block, TOG|BIT, BRUSH_FIXED_TEX, B_BRUSHCHANGE, "Fixed", xco+5,yco,butw,19, &brush->flag, 0, 0, 0, 0, "Keep texture origin in fixed position");*/ + uiBlockSetCol(block, TH_AUTO); } } -#endif } static void editing_panel_mesh_texface(void) diff --git a/source/blender/src/buttons_shading.c b/source/blender/src/buttons_shading.c index 00c96389ac5..8f2d14a3d78 100644 --- a/source/blender/src/buttons_shading.c +++ b/source/blender/src/buttons_shading.c @@ -1251,7 +1251,7 @@ static void texture_panel_texture(MTex *mtex, Material *ma, World *wrld, Lamp *l std_libbuttons(block, 10, 180, 0, NULL, B_LTEXBROWSE, ID_TE, 0, id, idfrom, &(G.buts->texnr), B_TEXALONE, B_TEXLOCAL, B_TEXDELETE, B_AUTOTEXNAME, B_KEEPDATA); } else if(br) { - std_libbuttons(block, 10, 180, 0, NULL, B_BTEXBROWSE, ID_TE, 0, id, idfrom, &(G.buts->texnr), B_TEXALONE, B_TEXLOCAL, B_TEXDELETE, B_AUTOTEXNAME, B_KEEPDATA); + std_libbuttons(block, 10, 180, 0, NULL, B_BTEXBROWSE, ID_TE, 0, id, idfrom, &(G.buts->menunr), B_TEXALONE, B_TEXLOCAL, B_TEXDELETE, B_AUTOTEXNAME, B_KEEPDATA); } else if(node) { diff --git a/source/blender/src/drawimage.c b/source/blender/src/drawimage.c index 0d1aba9438a..ce6a4e63fcd 100644 --- a/source/blender/src/drawimage.c +++ b/source/blender/src/drawimage.c @@ -1097,6 +1097,7 @@ static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PROPERTIES uiBlockSetCol(block, TH_BUT_SETTING2); id= (brush->mtex[0])? (ID*)brush->mtex[0]->tex: NULL; xco= std_libbuttons(block, 0, yco, 0, NULL, B_SIMABTEXBROWSE, ID_TE, 0, id, NULL, &(G.sima->menunr), 0, 0, B_SIMABTEXDELETE, 0, 0); + /*uiDefButBitS(block, TOG|BIT, BRUSH_FIXED_TEX, B_SIMABRUSHCHANGE, "Fixed", xco+5,yco,butw,19, &brush->flag, 0, 0, 0, 0, "Keep texture origin in fixed position");*/ uiBlockSetCol(block, TH_AUTO); } } diff --git a/source/blender/src/drawmesh.c b/source/blender/src/drawmesh.c index 2ff671d33fa..d3556f82243 100644 --- a/source/blender/src/drawmesh.c +++ b/source/blender/src/drawmesh.c @@ -430,6 +430,34 @@ int set_tpage(TFace *tface) return 1; } +void update_realtime_image(Image *ima, int x, int y, int w, int h) +{ + if (ima->repbind || fDoMipMap || !ima->bindcode || !ima->ibuf || + (!is_pow2(ima->ibuf->x) || !is_pow2(ima->ibuf->y)) || + (w == 0) || (h == 0)) { + /* these special cases require full reload still */ + free_realtime_image(ima); + } + else { + int row_length = glaGetOneInteger(GL_UNPACK_ROW_LENGTH); + int skip_pixels = glaGetOneInteger(GL_UNPACK_SKIP_PIXELS); + int skip_rows = glaGetOneInteger(GL_UNPACK_SKIP_ROWS); + + glBindTexture(GL_TEXTURE_2D, ima->bindcode); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, ima->ibuf->x); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); + glPixelStorei(GL_UNPACK_SKIP_ROWS, y); + + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, + GL_UNSIGNED_BYTE, ima->ibuf->rect); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels); + glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows); + } +} + void free_realtime_image(Image *ima) { if(ima->bindcode) { diff --git a/source/blender/src/drawview.c b/source/blender/src/drawview.c index b81631601d0..751d7f6e791 100644 --- a/source/blender/src/drawview.c +++ b/source/blender/src/drawview.c @@ -53,6 +53,7 @@ #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -2253,7 +2254,15 @@ static void view3d_panel_object(short cntrl) // VIEW3D_HANDLER_OBJECT else if(G.f & (G_VERTEXPAINT|G_TEXTUREPAINT)) { extern VPaint Gvp; /* from vpaint */ static float hsv[3], old[3]; // used as temp mem for picker - uiBlockPickerButtons(block, &Gvp.r, hsv, old, hexcol, 'f', REDRAWBUTSEDIT); /* 'f' is for floating panel */ + float *rgb= NULL; + ToolSettings *settings= G.scene->toolsettings; + + if(G.f & G_VERTEXPAINT) rgb= &Gvp.r; + else if(settings->imapaint.brush) rgb= settings->imapaint.brush->rgb; + + if (rgb) + /* 'f' is for floating panel */ + uiBlockPickerButtons(block, rgb, hsv, old, hexcol, 'f', REDRAWBUTSEDIT); } else { BoundBox *bb = NULL; diff --git a/source/blender/src/editface.c b/source/blender/src/editface.c index b97b89cbe5a..6a671a0da93 100644 --- a/source/blender/src/editface.c +++ b/source/blender/src/editface.c @@ -1575,7 +1575,7 @@ void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *uv) if (nvert == 4) { texpaint_barycentric_2d(v1, v2, v4, p, w); - + if(w[0] < 0.0f) { /* if w[0] is negative, co is on the other side of the v1-v3 edge, so we interpolate using the other triangle */ diff --git a/source/blender/src/header_view3d.c b/source/blender/src/header_view3d.c index a0aa801b55a..9a3b41870ba 100644 --- a/source/blender/src/header_view3d.c +++ b/source/blender/src/header_view3d.c @@ -86,6 +86,7 @@ #include "BDR_editface.h" #include "BDR_editmball.h" #include "BDR_editobject.h" +#include "BDR_imagepaint.h" #include "BDR_vpaint.h" #include "BIF_editlattice.h" @@ -3761,6 +3762,12 @@ static uiBlock *view3d_vpaintmenu(void *arg_unused) /* texture paint menu (placeholder, no items yet??) */ static void do_view3d_tpaintmenu(void *arg, int event) { + switch(event) { + case 0: /* undo image painting */ + imagepaint_undo(); + break; + } + allqueue(REDRAWVIEW3D, 0); } @@ -3772,6 +3779,7 @@ static uiBlock *view3d_tpaintmenu(void *arg_unused) block= uiNewBlock(&curarea->uiblocks, "view3d_paintmenu", UI_EMBOSSP, UI_HELV, curarea->headwin); uiBlockSetButmFunc(block, do_view3d_tpaintmenu, NULL); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Undo Texture Painting|U", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 0, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); if(curarea->headertype==HEADERTOP) { diff --git a/source/blender/src/imagepaint.c b/source/blender/src/imagepaint.c index f80d5a6723d..1e372c3ec28 100644 --- a/source/blender/src/imagepaint.c +++ b/source/blender/src/imagepaint.c @@ -85,7 +85,7 @@ #include "blendef.h" #include "mydevice.h" -/* ImagePaint Utilities */ +/* Defines and Structs */ #define IMAPAINT_FLOAT_TO_CHAR(f) ((char)(f*255)) #define IMAPAINT_CHAR_TO_FLOAT(c) (c/255.0f) @@ -96,30 +96,191 @@ f[1]=IMAPAINT_CHAR_TO_FLOAT(c[1]); f[2]=IMAPAINT_CHAR_TO_FLOAT(c[2]); } #define IMAPAINT_FLOAT_RGB_COPY(a, b) VECCOPY(a, b) -static void imapaint_blend_line(ImBuf *ibuf, ImBuf *ibufb, float *start, float *end) +#define IMAPAINT_TILE_BITS 6 +#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) +#define IMAPAINT_TILE_NUMBER(size) (((size)+IMAPAINT_TILE_SIZE-1) >> IMAPAINT_TILE_BITS) + +typedef struct ImagePaintState { + Brush *brush; + short tool; + Image *image; + ImBuf *canvas; + ImBuf *clonecanvas; + short clonefreefloat; + char *warnpackedfile; + + /* texture paint only */ + Object *ob; + Mesh *me; + TFace *tface; + float uv[2]; +} ImagePaintState; + +typedef struct ImagePaintUndo { + Image *image; + ImBuf *tilebuf; + void **tiles; + int xtiles, ytiles; +} ImagePaintUndo; + +typedef struct ImagePaintPartialRedraw { + int x1, y1, x2, y2; + int enabled; +} ImagePaintPartialRedraw; + +static ImagePaintUndo imapaintundo = {NULL, NULL, NULL, 0, 0}; +static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; + +static void init_imagapaint_undo(Image *ima) +{ + int xt, yt; + + imapaintundo.image = ima; + imapaintundo.xtiles = xt = IMAPAINT_TILE_NUMBER(ima->ibuf->x); + imapaintundo.ytiles = yt = IMAPAINT_TILE_NUMBER(ima->ibuf->y); + imapaintundo.tiles = MEM_callocN(sizeof(void*)*xt*yt, "ImagePaintUndoTiles"); + imapaintundo.tilebuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, + ima->ibuf->depth, (ima->ibuf->rect_float)? IB_rectfloat: IB_rect, 0); +} + +static void imapaint_copy_tile(Image *ima, int tile, int x, int y, int swapundo) +{ + IMB_rectcpy(imapaintundo.tilebuf, ima->ibuf, 0, 0, x*IMAPAINT_TILE_SIZE, + y*IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + + if (imapaintundo.tilebuf->rect_float) + SWAP(void*, imapaintundo.tilebuf->rect_float, imapaintundo.tiles[tile]) + else + SWAP(void*, imapaintundo.tilebuf->rect, imapaintundo.tiles[tile]) + + if (swapundo) + IMB_rectcpy(ima->ibuf, imapaintundo.tilebuf, x*IMAPAINT_TILE_SIZE, + y*IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); +} + +static void imapaint_clear_partial_redraw() +{ + memset(&imapaintpartial, 0, sizeof(imapaintpartial)); +} + +static void imapaint_dirty_region(Image *ima, int x, int y, int w, int h) +{ + int srcx= 0, srcy= 0, origx, tile, allocsize; + + IMB_rectclip(ima->ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + + if (w == 0 || h == 0) + return; + + if (!imapaintpartial.enabled) { + imapaintpartial.x1 = x; + imapaintpartial.y1 = y; + imapaintpartial.x2 = x+w; + imapaintpartial.y2 = y+h; + imapaintpartial.enabled = 1; + } + else { + imapaintpartial.x1 = MIN2(imapaintpartial.x1, x); + imapaintpartial.y1 = MIN2(imapaintpartial.y1, y); + imapaintpartial.x2 = MAX2(imapaintpartial.x2, x+w); + imapaintpartial.y2 = MAX2(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++) { + if (ima != imapaintundo.image) { + free_imagepaint(); + init_imagapaint_undo(ima); + } + + tile = y*imapaintundo.xtiles + x; + if (!imapaintundo.tiles[tile]) { + allocsize= (ima->ibuf->rect_float)? sizeof(float): sizeof(char); + imapaintundo.tiles[tile]= MEM_mapallocN(allocsize*4* + IMAPAINT_TILE_SIZE*IMAPAINT_TILE_SIZE, "ImagePaintUndoTile"); + imapaint_copy_tile(ima, tile, x, y, 0); + } + } + } + + ima->ibuf->userflags |= IB_BITMAPDIRTY; +} + +static void imapaint_image_update(Image *image, short texpaint) +{ + if(image->ibuf->rect_float) + imb_freerectImBuf(image->ibuf); /* force recreate of char rect */ + + /* todo: should set_tpage create ->rect? */ + if(texpaint || G.sima->lock) { + int w = imapaintpartial.x2 - imapaintpartial.x1; + int h = imapaintpartial.y2 - imapaintpartial.y1; + update_realtime_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); + } +} + +static void imapaint_redraw(int final, int texpaint, Image *image) +{ + if(final) { + if(texpaint) + allqueue(REDRAWIMAGE, 0); + else if(!G.sima->lock) { + if(image) + free_realtime_image(image); /* force OpenGL reload */ + allqueue(REDRAWVIEW3D, 0); + } + allqueue(REDRAWHEADERS, 0); + } + else if(!texpaint && G.sima->lock) + force_draw_plus(SPACE_VIEW3D, 0); + else + force_draw(0); +} + +void imagepaint_undo() { - float numsteps, t, pos[2]; - int step, d[2], ipos[2]; + int x, y, tile; + Image *ima= imapaintundo.image; - d[0] = (int)(end[0] - start[0]); - d[1] = (int)(end[1] - start[1]); - numsteps = sqrt(d[0]*d[0] + d[1]*d[1])/(ibufb->x/4.0f); + if (!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float)) + return; - if(numsteps < 1.0) - numsteps = 1.0f; + for (tile = 0, y = 0; y < imapaintundo.ytiles; y++) + for (x = 0; x < imapaintundo.xtiles; x++, tile++) + if (imapaintundo.tiles[tile]) + imapaint_copy_tile(ima, tile, x, y, 1); - for (step=0; step < numsteps; step++) { - t = (step+1)/numsteps; - pos[0] = start[0] + d[0]*t; - pos[1] = start[1] + d[1]*t; + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); - ipos[0]= (int)(pos[0] - ibufb->x/2); - ipos[1]= (int)(pos[1] - ibufb->y/2); - IMB_rectblend(ibuf, ibufb, ipos[0], ipos[1], 0, 0, - ibufb->x, ibufb->y, IMB_BLEND_MIX); + free_realtime_image(ima); /* force OpenGL reload */ +} + +void free_imagepaint() +{ + /* todo: does this need to be in the same places as editmode_undo_clear, + vertex paint isn't? */ + int i, size = imapaintundo.xtiles*imapaintundo.ytiles; + + if (imapaintundo.tiles) { + for (i = 0; i < size; i++) + if (imapaintundo.tiles[i]) + MEM_freeN(imapaintundo.tiles[i]); + MEM_freeN(imapaintundo.tiles); } + if (imapaintundo.tilebuf) + IMB_freeImBuf(imapaintundo.tilebuf); + + memset(&imapaintundo, 0, sizeof(imapaintundo)); } +/* Image Paint Operations */ + static void imapaint_ibuf_get_set_rgb(ImBuf *ibuf, int x, int y, short torus, short set, float *rgb) { if (torus) { @@ -160,8 +321,6 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo return 1; } -/* ImagePaint Tools */ - static void imapaint_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, short torus) { int x, y, count, xi, yi, xo, yo; @@ -236,15 +395,6 @@ static ImBuf *imapaint_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) return clonebuf; } -/* ImagePaint state and operations */ - -typedef struct ImagePaintState { - Brush *brush; - short tool; - ImBuf *canvas; - ImBuf *clonecanvas; -} ImagePaintState; - static void imapaint_convert_brushco(ImBuf *ibufb, float *pos, int *ipos) { ipos[0]= (int)(pos[0] - ibufb->x/2); @@ -253,43 +403,45 @@ static void imapaint_convert_brushco(ImBuf *ibufb, float *pos, int *ipos) static int imapaint_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *pos) { - ImagePaintState s= *((ImagePaintState*)state); + ImagePaintState *s= ((ImagePaintState*)state); ImBuf *clonebuf= NULL; - short torus= s.brush->flag & BRUSH_TORUS; - short blend= s.brush->blend; - float *offset= s.brush->clone.offset; + short torus= s->brush->flag & BRUSH_TORUS; + short blend= s->brush->blend; + float *offset= s->brush->clone.offset; float liftpos[2]; int bpos[2], blastpos[2], bliftpos[2]; - if ((s.tool == PAINT_TOOL_SMEAR) && (lastpos[0]==pos[0]) && (lastpos[1]==pos[1])) - return 0; - imapaint_convert_brushco(ibufb, pos, bpos); /* lift from canvas */ - if(s.tool == PAINT_TOOL_SOFTEN) { - imapaint_lift_soften(s.canvas, ibufb, bpos, torus); + if(s->tool == PAINT_TOOL_SOFTEN) { + imapaint_lift_soften(s->canvas, ibufb, bpos, torus); } - else if(s.tool == PAINT_TOOL_SMEAR) { + else if(s->tool == PAINT_TOOL_SMEAR) { + if (lastpos[0]==pos[0] && lastpos[1]==pos[1]) + return 0; + imapaint_convert_brushco(ibufb, lastpos, blastpos); - imapaint_lift_smear(s.canvas, ibufb, blastpos); + imapaint_lift_smear(s->canvas, ibufb, blastpos); } - else if(s.tool == PAINT_TOOL_CLONE && s.clonecanvas) { - liftpos[0]= pos[0] - offset[0]*s.canvas->x; - liftpos[1]= pos[1] - offset[1]*s.canvas->y; + else if(s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { + liftpos[0]= pos[0] - offset[0]*s->canvas->x; + liftpos[1]= pos[1] - offset[1]*s->canvas->y; imapaint_convert_brushco(ibufb, liftpos, bliftpos); - clonebuf= imapaint_lift_clone(s.clonecanvas, ibufb, bliftpos); + clonebuf= imapaint_lift_clone(s->clonecanvas, ibufb, bliftpos); } + imapaint_dirty_region(s->image, bpos[0], bpos[1], ibufb->x, ibufb->y); + /* blend into canvas */ if(torus) - IMB_rectblend_torus(s.canvas, (clonebuf)? clonebuf: ibufb, + IMB_rectblend_torus(s->canvas, (clonebuf)? clonebuf: ibufb, bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend); else - IMB_rectblend(s.canvas, (clonebuf)? clonebuf: ibufb, + IMB_rectblend(s->canvas, (clonebuf)? clonebuf: ibufb, bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend); - + if(clonebuf) IMB_freeImBuf(clonebuf); return 1; @@ -302,299 +454,230 @@ static void imapaint_compute_uvco(short *mval, float *uv) areamouseco_to_ipoco(G.v2d, mval, &uv[0], &uv[1]); } -static void imapaint_compute_imageco(ImBuf *ibuf, short *mval, float *mousepos) -{ - areamouseco_to_ipoco(G.v2d, mval, &mousepos[0], &mousepos[1]); - mousepos[0] *= ibuf->x; - mousepos[1] *= ibuf->y; -} +/* 3D TexturePaint */ -void imapaint_redraw_tool(void) -{ - if(G.scene->toolsettings->imapaint.flag & IMAGEPAINT_DRAW_TOOL_DRAWING) - force_draw(0); -} +int facesel_face_pick(Mesh *me, short *mval, unsigned int *index, short rect); +void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *mousepos); -static void imapaint_redraw(int final, int painted) +static int texpaint_break_stroke(float *prevuv, float *fwuv, float *bkuv, float *uv) { - if(!final && !painted) { - imapaint_redraw_tool(); - return; - } + float d1[2], d2[2]; + float mismatch = Vec2Lenf(fwuv, uv); + float len1 = Vec2Lenf(prevuv, fwuv); + float len2 = Vec2Lenf(bkuv, uv); - if(final || painted) { - if (final || G.sima->lock) { - /* Make OpenGL aware of a changed texture */ - free_realtime_image(G.sima->image); - force_draw_plus(SPACE_VIEW3D,0); - } - else - force_draw(0); - } + Vec2Subf(d1, fwuv, prevuv); + Vec2Subf(d2, uv, bkuv); - if(final) - allqueue(REDRAWHEADERS, 0); + return ((Inp2f(d1, d2) < 0.0f) || (mismatch > MAX2(len1, len2)*2)); } -static int imapaint_canvas_init(Brush *brush, short tool, ImBuf **canvas, ImBuf **clonecanvas, short *freefloat) -{ - Image *ima= G.sima->image; +/* ImagePaint Common */ - /* verify that we can paint and create canvas */ - if(!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float)) +static int imapaint_canvas_set(ImagePaintState *s, Image *ima) +{ + /* verify that we can paint and set canvas */ + if(ima->packedfile) { + s->warnpackedfile = ima->id.name + 2; + return 0; + } + else if(!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float)) return 0; else if(ima->packedfile) return 0; - *canvas= ima->ibuf; + s->image= ima; + s->canvas= ima->ibuf; - /* create clone canvas */ - if(clonecanvas && (tool == PAINT_TOOL_CLONE)) { - ima= brush->clone.image; + /* set clone canvas */ + if(s->tool == PAINT_TOOL_CLONE) { + ima= s->brush->clone.image; if(!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float)) return 0; - *clonecanvas= ima->ibuf; + s->clonecanvas= ima->ibuf; - if((*canvas)->rect_float && !(*clonecanvas)->rect_float) { + if(s->canvas->rect_float && !s->clonecanvas->rect_float) { /* temporarily add float rect for cloning */ - *freefloat= 1; - IMB_float_from_rect(*clonecanvas); + IMB_float_from_rect(s->clonecanvas); + s->clonefreefloat= 1; } - else if(!(*canvas)->rect_float && !(*clonecanvas)->rect) { - *freefloat= 0; - IMB_rect_from_float(*clonecanvas); + else if(!s->canvas->rect_float && !s->clonecanvas->rect) + IMB_rect_from_float(s->clonecanvas); + } + + return 1; +} + +static void imapaint_canvas_free(ImagePaintState *s) +{ + if (s->clonefreefloat) + imb_freerectfloatImBuf(s->clonecanvas); +} + +static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *image, short texpaint, float *uv, double time, int update) +{ + float pos[2]; + + pos[0] = uv[0]*image->ibuf->x; + pos[1] = uv[1]*image->ibuf->y; + + brush_painter_require_imbuf(painter, ((image->ibuf->rect_float)? 1: 0), 0, 0); + + if (brush_painter_paint(painter, imapaint_paint_op, pos, time, s)) { + if (update) + imapaint_image_update(image, texpaint); + return 1; + } + else return 0; +} + +static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpaint, short *prevmval, short *mval, double time) +{ + TFace *newtface = NULL; + Image *newimage = NULL; + float fwuv[2], bkuv[2], newuv[2]; + unsigned int face_index; + int breakstroke = 0, redraw = 0; + + if (texpaint) { + + /* pick face and image */ + if (facesel_face_pick(s->me, mval, &face_index, 0)) { + newtface = s->me->tface + face_index; + newimage = (Image*)newtface->tpage; + texpaint_pick_uv(s->ob, s->me, newtface, mval, newuv); } else - *freefloat= 0; + newuv[0] = newuv[1] = 0.0f; + + /* see if stroke is broken, and if so finish painting in old position */ + if (s->image) { + if (newimage == s->image) { + texpaint_pick_uv(s->ob, s->me, s->tface, mval, fwuv); + texpaint_pick_uv(s->ob, s->me, newtface, prevmval, bkuv); + breakstroke= texpaint_break_stroke(s->uv, fwuv, bkuv, newuv); + } + else + breakstroke= 1; + } + + if (breakstroke) { + texpaint_pick_uv(s->ob, s->me, s->tface, mval, fwuv); + redraw |= imapaint_do_paint(s, painter, s->image, texpaint, fwuv, time, 1); + imapaint_clear_partial_redraw(); + brush_painter_break_stroke(painter); + } + + /* set new canvas */ + if (newimage && (newimage != s->image)) + if (!imapaint_canvas_set(s, newimage)) + newimage = NULL; + + /* paint in new image */ + if (newimage) { + if (breakstroke) + redraw|= imapaint_do_paint(s, painter, newimage, texpaint, bkuv, time, 0); + redraw|= imapaint_do_paint(s, painter, newimage, texpaint, newuv, time, 1); + } + + /* update state */ + s->image = newimage; + s->tface = newtface; + s->uv[0] = newuv[0]; + s->uv[1] = newuv[1]; + } + else { + imapaint_compute_uvco(mval, newuv); + redraw |= imapaint_do_paint(s, painter, s->image, texpaint, newuv, time, 1); } - else if(clonecanvas) - *clonecanvas= NULL; - return 1; + if (redraw) { + imapaint_redraw(0, texpaint, NULL); + imapaint_clear_partial_redraw(); + } } -void imagepaint_paint(short mousebutton) +void imagepaint_paint(short mousebutton, short texpaint) { ImagePaintState s; BrushPainter *painter; ToolSettings *settings= G.scene->toolsettings; - short prevmval[2], mval[2], freefloat=0; - float mousepos[2]; - double mousetime; + short prevmval[2], mval[2]; + double time; /* initialize state */ + memset(&s, 0, sizeof(s)); s.brush= settings->imapaint.brush; s.tool= settings->imapaint.tool; + if(texpaint && (s.tool == PAINT_TOOL_CLONE)) + s.tool = PAINT_TOOL_DRAW; - if(!s.brush) return; - if(!imapaint_canvas_init(s.brush, s.tool, &s.canvas, &s.clonecanvas, &freefloat)) { - if(G.sima->image && G.sima->image->packedfile) - error("Painting in packed images not supported"); + if(!s.brush) return; + + if(texpaint) { + s.ob = OBACT; + if (!s.ob || !(s.ob->lay & G.vd->lay)) return; + s.me = get_mesh(s.ob); + if (!s.me) return; + + persp(PERSP_VIEW); + } + else { + s.image = G.sima->image; + + if(!imapaint_canvas_set(&s, G.sima->image)) { + if(s.warnpackedfile) + error("Painting in packed images not supported"); + return; + } } settings->imapaint.flag |= IMAGEPAINT_DRAWING; + free_imagepaint(); /* create painter and paint once */ painter= brush_painter_new(s.brush); - brush_painter_require_imbuf(painter, ((s.canvas->rect_float)? 1: 0), 0, 0); getmouseco_areawin(mval); - mousetime= PIL_check_seconds_timer(); + time= PIL_check_seconds_timer(); prevmval[0]= mval[0]; prevmval[1]= mval[1]; - imapaint_compute_imageco(s.canvas, mval, mousepos); - if(brush_painter_paint(painter, imapaint_paint_op, mousepos, mousetime, &s)) { - if (s.canvas->rect_float) - imb_freerectImBuf(s.canvas); /* force recreate */ - imapaint_redraw(0, 1); - } + imapaint_do(&s, painter, texpaint, prevmval, mval, time); /* paint loop */ while(get_mbut() & mousebutton) { getmouseco_areawin(mval); - mousetime= PIL_check_seconds_timer(); + time= PIL_check_seconds_timer(); if((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { + imapaint_do(&s, painter, texpaint, prevmval, mval, time); prevmval[0]= mval[0]; prevmval[1]= mval[1]; - imapaint_compute_imageco(s.canvas, mval, mousepos); } - else if (!(s.brush->flag & BRUSH_AIRBRUSH)) - continue; - - if(brush_painter_paint(painter, imapaint_paint_op, mousepos, mousetime, &s)) { - if (s.canvas->rect_float) - imb_freerectImBuf(s.canvas); /* force recreate */ - imapaint_redraw(0, 1); - } - - /* todo: check if we can wait here to not take up all cpu usage? */ + else if (s.brush->flag & BRUSH_AIRBRUSH) + imapaint_do(&s, painter, texpaint, prevmval, mval, time); + else + BIF_wait_for_statechange(); } /* clean up */ settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - s.canvas->userflags |= IB_BITMAPDIRTY; - - if (freefloat) imb_freerectfloatImBuf(s.clonecanvas); - + imapaint_canvas_free(&s); brush_painter_free(painter); - imapaint_redraw(1, 0); -} - -/* 3D TexturePaint */ + imapaint_redraw(1, texpaint, s.image); -/* these will be moved */ -int facesel_face_pick(Mesh *me, short *mval, unsigned int *index, short rect); -void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *mousepos); - -static void texpaint_compute_imageco(ImBuf *ibuf, Object *ob, Mesh *mesh, TFace *tf, short *xy, float *imageco) -{ - texpaint_pick_uv(ob, mesh, tf, xy, imageco); - imageco[0] *= ibuf->x; - imageco[1] *= ibuf->y; -} + if (texpaint) { + if (s.warnpackedfile) + error("Painting in packed images is not supported: %s", s.warnpackedfile); -void texturepaint_paint(short mousebutton) -{ - Object *ob; - Mesh *me; - TFace *face, *face_old = 0; - short xy[2], xy_old[2]; - //int a, index; - Image *img=NULL, *img_old = NULL; - ImBuf *brush, *canvas = 0; - unsigned int face_index; - char *warn_packed_file = 0; - float uv[2], uv_old[2]; - extern VPaint Gvp; - Brush tmpbrush; - - ob = OBACT; - if (!ob || !(ob->lay & G.vd->lay)) return; - me = get_mesh(ob); - if (!me) return; - - /* create a fake Brush for now - will be replaced soon */ - memset(&tmpbrush, 0, sizeof(Brush)); - tmpbrush.size= Gvp.size; - tmpbrush.alpha= Gvp.a; - tmpbrush.innerradius= 0.5f; - IMAPAINT_FLOAT_RGB_COPY(tmpbrush.rgb, &Gvp.r); - brush = brush_imbuf_new(&tmpbrush, 0, 0, tmpbrush.size); - - persp(PERSP_VIEW); - - getmouseco_areawin(xy_old); - while (get_mbut() & mousebutton) { - getmouseco_areawin(xy); - /* Check if cursor has moved */ - if ((xy[0] != xy_old[0]) || (xy[1] != xy_old[1])) { - - /* Get face to draw on */ - if (!facesel_face_pick(me, xy, &face_index, 0)) face = NULL; - else face = (((TFace*)me->tface)+face_index); - - /* Check if this is another face. */ - if (face != face_old) { - /* The active face changed, check the texture */ - if (face) { - img = face->tpage; - canvas = (img)? img->ibuf: NULL; - } - else { - img = 0; - } - - if (img != img_old) { - /* Faces have different textures. Finish drawing in the old face. */ - if (face_old && canvas) { - texpaint_compute_imageco(canvas, ob, me, face_old, xy, uv); - imapaint_blend_line(canvas, brush, uv_old, uv); - img_old->ibuf->userflags |= IB_BITMAPDIRTY; - canvas = 0; - } - - /* Create new canvas and start drawing in the new face. */ - if (img) { - if (canvas && img->packedfile == 0) { - /* MAART: skipx is not set most of the times. Make a guess. */ - if (canvas) { - texpaint_compute_imageco(canvas, ob, me, face, xy_old, uv_old); - texpaint_compute_imageco(canvas, ob, me, face, xy, uv); - imapaint_blend_line(canvas, brush, uv_old, uv); - canvas->userflags |= IB_BITMAPDIRTY; - } - } - else { - if (img->packedfile) { - warn_packed_file = img->id.name + 2; - img = 0; - } - } - } - } - else { - /* Face changed and faces have the same texture. */ - if (canvas) { - /* Finish drawing in the old face. */ - if (face_old) { - texpaint_compute_imageco(canvas, ob, me, face_old, xy, uv); - imapaint_blend_line(canvas, brush, uv_old, uv); - img_old->ibuf->userflags |= IB_BITMAPDIRTY; - } - - /* Start drawing in the new face. */ - if (face) { - texpaint_compute_imageco(canvas, ob, me, face, xy_old, uv_old); - texpaint_compute_imageco(canvas, ob, me, face, xy, uv); - imapaint_blend_line(canvas, brush, uv_old, uv); - canvas->userflags |= IB_BITMAPDIRTY; - } - } - } - } - else { - /* Same face, continue drawing */ - if (face && canvas) { - /* Get the new (u,v) coordinates */ - texpaint_compute_imageco(canvas, ob, me, face, xy, uv); - imapaint_blend_line(canvas, brush, uv_old, uv); - canvas->userflags |= IB_BITMAPDIRTY; - } - } - - if (face && img) { - /* Make OpenGL aware of a change in the texture */ - free_realtime_image(img); - /* Redraw the view */ - scrarea_do_windraw(curarea); - screen_swapbuffers(); - } - - xy_old[0] = xy[0]; - xy_old[1] = xy[1]; - uv_old[0] = uv[0]; - uv_old[1] = uv[1]; - face_old = face; - img_old = img; - } + persp(PERSP_WIN); } - IMB_freeImBuf(brush); - - if (warn_packed_file) - error("Painting in packed images is not supported: %s", warn_packed_file); - - persp(PERSP_WIN); - - BIF_undo_push("UV face draw"); - allqueue(REDRAWVIEW3D, 0); - allqueue(REDRAWIMAGE, 0); - allqueue(REDRAWHEADERS, 0); + /* todo: BIF_undo_push("Image paint"); */ } void imagepaint_pick(short mousebutton) diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 6f05605cb30..87390954932 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -769,8 +769,10 @@ void BIF_undo(void) wpaint_undo(); else if(G.f & G_VERTEXPAINT) vpaint_undo(); - else if(G.f & G_TEXTUREPAINT); /* no texture paint undo yet */ - else if(curarea->spacetype==SPACE_IMAGE && (G.sima->flag & SI_DRAWTOOL)); + else if(G.f & G_TEXTUREPAINT) + imagepaint_undo(); + else if(curarea->spacetype==SPACE_IMAGE && (G.sima->flag & SI_DRAWTOOL)) + imagepaint_undo(); else { /* now also in faceselect mode */ if(U.uiflag & USER_GLOBALUNDO) { @@ -792,6 +794,10 @@ void BIF_redo(void) wpaint_undo(); else if(G.f & G_VERTEXPAINT) vpaint_undo(); + else if(G.f & G_TEXTUREPAINT) + imagepaint_undo(); + else if(curarea->spacetype==SPACE_IMAGE && (G.sima->flag & SI_DRAWTOOL)) + imagepaint_undo(); else { /* includes faceselect now */ if(U.uiflag & USER_GLOBALUNDO) { @@ -1011,7 +1017,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) vertex_paint(); } else if (G.f & G_TEXTUREPAINT) { - texturepaint_paint(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE); + imagepaint_paint(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE, 1); } break; case MIDDLEMOUSE: @@ -1847,6 +1853,8 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) wpaint_undo(); else if(G.f & G_VERTEXPAINT) vpaint_undo(); + else if(G.f & G_TEXTUREPAINT) + imagepaint_undo(); else if (G.f & G_FACESELECT) uv_autocalc_tface(); else { @@ -4075,7 +4083,7 @@ static void winqreadimagespace(ScrArea *sa, void *spacedata, BWinEvent *evt) scrarea_queue_winredraw(sa); break; case LEFTMOUSE: - imagepaint_paint(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE); + imagepaint_paint(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE, 0); break; case RIGHTMOUSE: imagepaint_pick(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE); diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index 10dd2a29024..0478968302c 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -126,6 +126,7 @@ #include "BDR_drawobject.h" #include "BDR_editobject.h" #include "BDR_editcurve.h" +#include "BDR_imagepaint.h" #include "BDR_vpaint.h" #include "BPY_extern.h" @@ -875,6 +876,7 @@ void exit_usiblender(void) free_matcopybuf(); free_ipocopybuf(); free_vertexpaint(); + free_imagepaint(); /* editnurb can remain to exist outside editmode */ freeNurblist(&editNurb); |