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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>2006-08-27 17:29:00 +0400
committerBrecht Van Lommel <brechtvanlommel@pandora.be>2006-08-27 17:29:00 +0400
commitb39f4b788dc9c5ccc9430b02852cbc1cbe56eca1 (patch)
tree7a1c91c3d4235db02b231ab5f504b31c2c0e4f15 /source/blender/blenkernel
parent84205fe0e0bfc524b8fd9ba09aedbf98b0b9457b (diff)
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.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_brush.h13
-rw-r--r--source/blender/blenkernel/intern/brush.c391
2 files changed, 319 insertions, 85 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;
}