/* Brush Sampling */ void brush_sample_tex(Brush *brush, float *xy, float *rgba, const int thread) { MTex *mtex= &brush->mtex; if (mtex && mtex->tex) { float co[3], tin, tr, tg, tb, ta; int hasrgb; const int radius= brush_size(brush); co[0]= xy[0]/radius; co[1]= xy[1]/radius; co[2]= 0.0f; hasrgb= externtex(mtex, co, &tin, &tr, &tg, &tb, &ta, thread); 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; } } else if (rgba) rgba[0]= rgba[1]= rgba[2]= rgba[3]= 1.0f; }
void brush_jitter_pos(Brush *brush, float pos[2], float jitterpos[2]) { int use_jitter= brush->jitter != 0; /* jitter-ed brush gives weird and unpredictable result for this kinds of stroke, so manyally disable jitter usage (sergey) */ use_jitter&= (brush->flag & (BRUSH_RESTORE_MESH|BRUSH_ANCHORED)) == 0; if(use_jitter){ float rand_pos[2]; const int radius= brush_size(brush); const int diameter= 2*radius; // find random position within a circle of diameter 1 do { rand_pos[0] = BLI_frand()-0.5f; rand_pos[1] = BLI_frand()-0.5f; } while (len_v2(rand_pos) > 0.5f); jitterpos[0] = pos[0] + 2*rand_pos[0]*diameter*brush->jitter; jitterpos[1] = pos[1] + 2*rand_pos[1]*diameter*brush->jitter; } else { copy_v2_v2(jitterpos, pos); } }
static void draw_image_view_tool(Scene *scene) { ToolSettings *settings= scene->toolsettings; Brush *brush= settings->imapaint.brush; int mval[2]; float radius; int draw= 0; if(brush) { if(settings->imapaint.flag & IMAGEPAINT_DRAWING) { if(settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL_DRAWING) draw= 1; } else if(settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL) draw= 1; if(draw) { getmouseco_areawin(mval); radius= brush_size(brush)*G.sima->zoom; fdrawXORcirc(mval[0], mval[1], radius); if (brush->innerradius != 1.0) { radius *= brush->innerradius; fdrawXORcirc(mval[0], mval[1], radius); } } } }
static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) { static GLuint overlay_texture = 0; static int init = 0; static int tex_changed_timestamp = -1; static int curve_changed_timestamp = -1; static Snapshot snap; static int old_size = -1; GLubyte* buffer = NULL; int size; int j; int refresh; #ifndef _OPENMP (void)sd; /* quied unused warning */ #endif if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; refresh = !overlay_texture || (br->mtex.tex && (!br->mtex.tex->preview || br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || !br->curve || br->curve->changed_timestamp != curve_changed_timestamp || !same_snap(&snap, br, vc); if (refresh) { if (br->mtex.tex && br->mtex.tex->preview) tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0]; if (br->curve) curve_changed_timestamp = br->curve->changed_timestamp; make_snap(&snap, br, vc); if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { int s = brush_size(br); int r = 1; for (s >>= 1; s > 0; s >>= 1) r++; size = (1<<r); if (size < 256) size = 256; if (size < old_size) size = old_size; } else
static 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; const int diameter= 2*brush_size(brush); imbflag= (cache->flt)? IB_rectfloat: IB_rect; if (!cache->ibuf) cache->ibuf= IMB_allocImBuf(diameter, diameter, 32, imbflag); ibuf= cache->ibuf; oldtexibuf= cache->texibuf; cache->texibuf= IMB_allocImBuf(diameter, diameter, 32, imbflag); 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); }
BrushPainter *brush_painter_new(Brush *brush) { BrushPainter *painter= MEM_callocN(sizeof(BrushPainter), "BrushPainter"); painter->brush= brush; painter->firsttouch= 1; painter->cache.lastsize= -1; /* force ibuf create in refresh */ painter->startsize = brush_size(brush); painter->startalpha = brush_alpha(brush); painter->startjitter = brush->jitter; painter->startspacing = brush->spacing; return painter; }
static void brush_painter_refresh_cache(BrushPainter *painter, float *pos, int use_color_correction) { Brush *brush= painter->brush; BrushPainterCache *cache= &painter->cache; MTex *mtex= &brush->mtex; int size; short flt; const int diameter= 2*brush_size(brush); const float alpha= brush_alpha(brush); if (diameter != cache->lastsize || alpha != cache->lastalpha || brush->jitter != cache->lastjitter) { 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: diameter; if (brush->flag & BRUSH_FIXED_TEX) { brush_imbuf_new(brush, flt, 3, size, &cache->maskibuf, use_color_correction); brush_painter_fixed_tex_partial_update(painter, pos); } else brush_imbuf_new(brush, flt, 2, size, &cache->ibuf, use_color_correction); cache->lastsize= diameter; cache->lastalpha= alpha; cache->lastjitter= brush->jitter; } else if ((brush->flag & BRUSH_FIXED_TEX) && mtex && mtex->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); } }
static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) { MTex* mtex = &brush->mtex; return (mtex->tex && mtex->ofs[0] == snap->ofs[0] && mtex->ofs[1] == snap->ofs[1] && mtex->ofs[2] == snap->ofs[2] && mtex->size[0] == snap->size[0] && mtex->size[1] == snap->size[1] && mtex->size[2] == snap->size[2] && mtex->rot == snap->rot) && ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && brush_size(brush) <= snap->brush_size) || (brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample mtex->brush_map_mode == snap->brush_map_mode && vc->ar->winx == snap->winx && vc->ar->winy == snap->winy; }
static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) { if (brush->mtex.tex) { snap->brush_map_mode = brush->mtex.brush_map_mode; copy_v3_v3(snap->ofs, brush->mtex.ofs); copy_v3_v3(snap->size, brush->mtex.size); snap->rot = brush->mtex.rot; } else { snap->brush_map_mode = -1; snap->ofs[0]= snap->ofs[1]= snap->ofs[2]= -1; snap->size[0]= snap->size[1]= snap->size[2]= -1; snap->rot = -1; } snap->brush_size = brush_size(brush); snap->winx = vc->ar->winx; snap->winy = vc->ar->winy; }
static int brush_scale_size_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Paint *paint= paint_get_active(scene); struct Brush *brush= paint_brush(paint); // Object *ob= CTX_data_active_object(C); float scalar= RNA_float_get(op->ptr, "scalar"); if (brush) { // pixel radius { const int old_size= brush_size(scene, brush); int size= (int)(scalar*old_size); if (old_size == size) { if (scalar > 1) { size++; } else if (scalar < 1) { size--; } } CLAMP(size, 1, 2000); // XXX magic number brush_set_size(scene, brush, size); } // unprojected radius { float unprojected_radius= scalar*brush_unprojected_radius(scene, brush); if (unprojected_radius < 0.001f) // XXX magic number unprojected_radius= 0.001f; brush_set_unprojected_radius(scene, brush, unprojected_radius); } } return OPERATOR_FINISHED; }
int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user, int use_color_correction) { Brush *brush= painter->brush; int totpaintops= 0; if (pressure == 0.0f) { if(painter->lastpressure) // XXX - hack, operator misses pressure= painter->lastpressure; else pressure = 1.0f; /* zero pressure == not using tablet */ } if (painter->firsttouch) { /* paint exactly once on first touch */ painter->startpaintpos[0]= pos[0]; painter->startpaintpos[1]= pos[1]; brush_apply_pressure(painter, brush, pressure); if (painter->cache.enabled) brush_painter_refresh_cache(painter, pos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, pos); painter->lasttime= time; painter->firsttouch= 0; painter->lastpaintpos[0]= pos[0]; painter->lastpaintpos[1]= pos[1]; } #if 0 else if (painter->brush->flag & BRUSH_AIRBRUSH) { float spacing, step, paintpos[2], dmousepos[2], len; double starttime, curtime= time; /* compute brush spacing adapted to brush size */ spacing= brush->rate; //radius*brush->spacing*0.01f; /* setup starting time, direction vector and accumulated time */ starttime= painter->accumtime; sub_v2_v2v2(dmousepos, pos, painter->lastmousepos); len= normalize_v2(dmousepos); painter->accumtime += curtime - painter->lasttime; /* do paint op over unpainted time distance */ while (painter->accumtime >= spacing) { step= (spacing - starttime)*len; 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); painter->lastpaintpos[0]= paintpos[0]; painter->lastpaintpos[1]= paintpos[1]; painter->accumtime -= spacing; starttime -= spacing; } painter->lasttime= curtime; } #endif else { float startdistance, spacing, step, paintpos[2], dmousepos[2], finalpos[2]; float t, len, press; const int radius= brush_size(brush); /* compute brush spacing adapted to brush radius, spacing may depend on pressure, so update it */ brush_apply_pressure(painter, brush, painter->lastpressure); spacing= MAX2(1.0f, radius)*brush->spacing*0.01f; /* setup starting distance, direction vector and accumulated distance */ startdistance= painter->accumdistance; sub_v2_v2v2(dmousepos, pos, painter->lastmousepos); len= normalize_v2(dmousepos); painter->accumdistance += len; if (brush->flag & BRUSH_SPACE) { /* do paint op over unpainted distance */ while ((len > 0.0f) && (painter->accumdistance >= spacing)) { step= spacing - startdistance; paintpos[0]= painter->lastmousepos[0] + dmousepos[0]*step; paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step; t = step/len; press= (1.0f-t)*painter->lastpressure + t*pressure; brush_apply_pressure(painter, brush, press); spacing= MAX2(1.0f, radius)*brush->spacing*0.01f; brush_jitter_pos(brush, paintpos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, finalpos); painter->lastpaintpos[0]= paintpos[0]; painter->lastpaintpos[1]= paintpos[1]; painter->accumdistance -= spacing; startdistance -= spacing; } } else { brush_jitter_pos(brush, pos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, pos, finalpos); painter->lastpaintpos[0]= pos[0]; painter->lastpaintpos[1]= pos[1]; painter->accumdistance= 0; } /* do airbrush paint ops, based on the number of paint ops left over from regular painting. this is a temporary solution until we have accurate time stamps for mouse move events */ if (brush->flag & BRUSH_AIRBRUSH) { double curtime= time; double painttime= brush->rate*totpaintops; painter->accumtime += curtime - painter->lasttime; if (painter->accumtime <= painttime) painter->accumtime= 0.0; else painter->accumtime -= painttime; while (painter->accumtime >= (double)brush->rate) { brush_apply_pressure(painter, brush, pressure); brush_jitter_pos(brush, pos, finalpos); if (painter->cache.enabled) brush_painter_refresh_cache(painter, finalpos, use_color_correction); totpaintops += func(user, painter->cache.ibuf, painter->lastmousepos, finalpos); painter->accumtime -= (double)brush->rate; } painter->lasttime= curtime; } } painter->lastmousepos[0]= pos[0]; painter->lastmousepos[1]= pos[1]; painter->lastpressure= pressure; brush_set_alpha(brush, painter->startalpha); brush_set_size(brush, painter->startsize); brush->jitter = painter->startjitter; brush->spacing = painter->startspacing; return totpaintops; }
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; const int radius= brush_size(brush); xoff = -radius + 0.5f; yoff = -radius + 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); /* not sure if it's actually needed or it's a mistake in coords/sizes calculation in brush_painter_fixed_tex_partial_update(), but without this limitation memory gets corrupted at fast strokes with quite big spacing (sergey) */ w = MIN2(w, ibuf->x); h = MIN2(h, ibuf->y); if (painter->cache.flt) { for (; y < h; y++) { bf = ibuf->rect_float + (y*ibuf->x + origx)*4; tf = texibuf->rect_float + (y*texibuf->x + origx)*4; mf = maskibuf->rect_float + (y*maskibuf->x + origx)*4; if (dotexold) otf = oldtexibuf->rect_float + ((y - origy + yt)*oldtexibuf->x + xt)*4; for (x=origx; x < w; x++, bf+=4, mf+=4, tf+=4) { if (dotexold) { copy_v3_v3(tf, otf); tf[3] = otf[3]; otf += 4; } else { xy[0] = x + xoff; xy[1] = y + yoff; brush_sample_tex(brush, xy, tf, 0); } 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, 0); 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_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction) { ImBuf *ibuf; float xy[2], dist, rgba[4], *dstf; int x, y, rowbytes, xoff, yoff, imbflag; const int radius= brush_size(brush); char *dst, crgb[3]; const float alpha= brush_alpha(brush); float brush_rgb[3]; imbflag= (flt)? IB_rectfloat: IB_rect; xoff = -bufsize/2.0f + 0.5f; yoff = -bufsize/2.0f + 0.5f; rowbytes= bufsize*4; if (*outbuf) ibuf= *outbuf; else ibuf= IMB_allocImBuf(bufsize, bufsize, 32, imbflag); if (flt) { copy_v3_v3(brush_rgb, brush->rgb); if(use_color_correction){ srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb); } for (y=0; y < ibuf->y; y++) { dstf = ibuf->rect_float + y*rowbytes; for (x=0; x < ibuf->x; x++, dstf+=4) { xy[0] = x + xoff; xy[1] = y + yoff; if (texfall == 0) { dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); copy_v3_v3(dstf, brush_rgb); dstf[3]= alpha*brush_curve_strength_clamp(brush, dist, radius); } else if (texfall == 1) { brush_sample_tex(brush, xy, dstf, 0); } else { dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); brush_sample_tex(brush, xy, rgba, 0); mul_v3_v3v3(dstf, rgba, brush_rgb); dstf[3] = rgba[3]*alpha*brush_curve_strength_clamp(brush, dist, radius); } } } } else { 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 + 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(alpha*brush_curve_strength(brush, dist, radius)); } else if (texfall == 1) { brush_sample_tex(brush, xy, rgba, 0); dst[0]= FTOCHAR(rgba[0]); dst[1]= FTOCHAR(rgba[1]); dst[2]= FTOCHAR(rgba[2]); dst[3]= FTOCHAR(rgba[3]); } else if (texfall == 2) { dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); brush_sample_tex(brush, xy, rgba, 0); 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]*alpha*brush_curve_strength_clamp(brush, dist, radius)); } else { dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]); brush_sample_tex(brush, xy, rgba, 0); dst[0]= crgb[0]; dst[1]= crgb[1]; dst[2]= crgb[2]; dst[3] = FTOCHAR(rgba[3]*alpha*brush_curve_strength_clamp(brush, dist, radius)); } } } } *outbuf= ibuf; }