Texturepaint now supports all the imagepaint brush settings, with the

exception of the clone tool.

One level undo for image- and texturepaint, only storing those tiles
that changed.

Test to improve texturepaint performance using glTexSubImage2D, only
enabled with 2^n sized textures and mipmapping off. Painting a 2048x2048
texture is then pretty smooth here, as long as the geometry is not too
complex.
This commit is contained in:
Brecht Van Lommel 2006-08-27 13:29:00 +00:00
parent 84205fe0e0
commit b39f4b788d
17 changed files with 808 additions and 407 deletions

@ -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

@ -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;
brush_sample(brush, xy, dist, dstf, dstf+3, texonly);
if (texfall == 0) {
dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]);
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]);
xy[0] = x + xoff;
xy[1] = y + yoff;
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]);
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;
if ((brush->size != painter->cache.lastsize)
|| (brush->alpha != painter->cache.lastalpha)
|| (brush->innerradius != painter->cache.lastinnerradius)) {
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];
if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf);
ibuf = painter->cache.ibuf;
texibuf = painter->cache.texibuf;
maskibuf = painter->cache.maskibuf;
painter->cache.ibuf= brush_imbuf_new(brush,
painter->cache.flt, painter->cache.texonly,
painter->cache.size? painter->cache.size: brush->size);
dotexold = (oldtexibuf != NULL);
painter->cache.lastsize= brush->size;
painter->cache.lastalpha= brush->alpha;
painter->cache.lastinnerradius= brush->innerradius;
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;
brush_sample_tex(brush, xy, tf);
}
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]);
}
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;
}
}
}
}
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;
}

@ -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 */

@ -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);

@ -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 */

@ -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

@ -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

@ -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)

@ -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) {

@ -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);
}
}

@ -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) {

@ -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;

@ -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 */

@ -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) {

@ -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)
{
float numsteps, t, pos[2];
int step, d[2], ipos[2];
int xt, yt;
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);
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);
}
if(numsteps < 1.0)
numsteps = 1.0f;
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);
for (step=0; step < numsteps; step++) {
t = (step+1)/numsteps;
pos[0] = start[0] + d[0]*t;
pos[1] = start[1] + d[1]*t;
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);
}
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);
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()
{
int x, y, tile;
Image *ima= imapaintundo.image;
if (!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float))
return;
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);
allqueue(REDRAWIMAGE, 0);
allqueue(REDRAWVIEW3D, 0);
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)
/* 3D TexturePaint */
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 int texpaint_break_stroke(float *prevuv, float *fwuv, float *bkuv, float *uv)
{
areamouseco_to_ipoco(G.v2d, mval, &mousepos[0], &mousepos[1]);
mousepos[0] *= ibuf->x;
mousepos[1] *= ibuf->y;
float d1[2], d2[2];
float mismatch = Vec2Lenf(fwuv, uv);
float len1 = Vec2Lenf(prevuv, fwuv);
float len2 = Vec2Lenf(bkuv, uv);
Vec2Subf(d1, fwuv, prevuv);
Vec2Subf(d2, uv, bkuv);
return ((Inp2f(d1, d2) < 0.0f) || (mismatch > MAX2(len1, len2)*2));
}
void imapaint_redraw_tool(void)
{
if(G.scene->toolsettings->imapaint.flag & IMAGEPAINT_DRAW_TOOL_DRAWING)
force_draw(0);
}
/* ImagePaint Common */
static void imapaint_redraw(int final, int painted)
static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
{
if(!final && !painted) {
imapaint_redraw_tool();
return;
/* verify that we can paint and set canvas */
if(ima->packedfile) {
s->warnpackedfile = ima->id.name + 2;
return 0;
}
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);
}
if(final)
allqueue(REDRAWHEADERS, 0);
}
static int imapaint_canvas_init(Brush *brush, short tool, ImBuf **canvas, ImBuf **clonecanvas, short *freefloat)
{
Image *ima= G.sima->image;
/* verify that we can paint and create canvas */
if(!ima || !ima->ibuf || !(ima->ibuf->rect || ima->ibuf->rect_float))
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
*freefloat= 0;
else if(!s->canvas->rect_float && !s->clonecanvas->rect)
IMB_rect_from_float(s->clonecanvas);
}
else if(clonecanvas)
*clonecanvas= NULL;
return 1;
}
void imagepaint_paint(short mousebutton)
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
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);
}
if (redraw) {
imapaint_redraw(0, texpaint, NULL);
imapaint_clear_partial_redraw();
}
}
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);
}
imapaint_redraw(1, texpaint, s.image);
/* 3D TexturePaint */
if (texpaint) {
if (s.warnpackedfile)
error("Painting in packed images is not supported: %s", s.warnpackedfile);
/* 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;
}
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)

@ -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);

@ -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);