void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], float jitterpos[2]) { float rand_pos[2]; float spread; int diameter; do { rand_pos[0] = BLI_rng_get_float(brush_rng) - 0.5f; rand_pos[1] = BLI_rng_get_float(brush_rng) - 0.5f; } while (len_squared_v2(rand_pos) > SQUARE(0.5f)); if (brush->flag & BRUSH_ABSOLUTE_JITTER) { diameter = 2 * brush->jitter_absolute; spread = 1.0; } else { diameter = 2 * BKE_brush_size_get(scene, brush); spread = brush->jitter; } /* find random position within a circle of diameter 1 */ jitterpos[0] = pos[0] + 2 * rand_pos[0] * diameter * spread; jitterpos[1] = pos[1] + 2 * rand_pos[1] * diameter * spread; }
void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], float jitterpos[2]) { int use_jitter = (brush->flag & BRUSH_ABSOLUTE_JITTER) ? (brush->jitter_absolute != 0) : (brush->jitter != 0); /* jitter-ed brush gives weird and unpredictable result for this * kinds of stroke, so manually disable jitter usage (sergey) */ use_jitter &= (brush->flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0; if (use_jitter) { float rand_pos[2]; float spread; int diameter; do { rand_pos[0] = BLI_rng_get_float(brush_rng) - 0.5f; rand_pos[1] = BLI_rng_get_float(brush_rng) - 0.5f; } while (len_v2(rand_pos) > 0.5f); if (brush->flag & BRUSH_ABSOLUTE_JITTER) { diameter = 2 * brush->jitter_absolute; spread = 1.0; } else { diameter = 2 * BKE_brush_size_get(scene, brush); spread = brush->jitter; } /* find random position within a circle of diameter 1 */ jitterpos[0] = pos[0] + 2 * rand_pos[0] * diameter * spread; jitterpos[1] = pos[1] + 2 * rand_pos[1] * diameter * spread; } else { copy_v2_v2(jitterpos, pos); } }
float BKE_brush_sample_masktex(const Scene *scene, Brush *br, const float point[2], const int thread, struct ImagePool *pool) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; MTex *mtex = &br->mask_mtex; float rgba[4], intensity; if (!mtex->tex) { return 1.0f; } if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) { float rotation = -mtex->rot; float point_2d[2] = {point[0], point[1]}; float x, y; float co[3]; x = point_2d[0] - br->mask_stencil_pos[0]; y = point_2d[1] - br->mask_stencil_pos[1]; if (rotation > 0.001f || rotation < -0.001f) { const float angle = atan2f(y, x) + rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); y = flen * sinf(angle); } if (fabsf(x) > br->mask_stencil_dimension[0] || fabsf(y) > br->mask_stencil_dimension[1]) { zero_v4(rgba); return 0.0f; } x /= (br->mask_stencil_dimension[0]); y /= (br->mask_stencil_dimension[1]); co[0] = x; co[1] = y; co[2] = 0.0f; externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); } else { float rotation = -mtex->rot; float point_2d[2] = {point[0], point[1]}; float x = 0.0f, y = 0.0f; /* Quite warnings */ float invradius = 1.0f; /* Quite warnings */ float co[3]; if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { /* keep coordinates relative to mouse */ rotation += ups->brush_rotation_sec; x = point_2d[0] - ups->mask_tex_mouse[0]; y = point_2d[1] - ups->mask_tex_mouse[1]; /* use pressure adjusted size for fixed mode */ invradius = 1.0f / ups->pixel_radius; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ invradius = 1.0f / BKE_brush_size_get(scene, br); x = point_2d[0]; y = point_2d[1]; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) { rotation += ups->brush_rotation_sec; /* these contain a random coordinate */ x = point_2d[0] - ups->mask_tex_mouse[0]; y = point_2d[1] - ups->mask_tex_mouse[1]; invradius = 1.0f / ups->pixel_radius; } x *= invradius; y *= invradius; /* it is probably worth optimizing for those cases where * the texture is not rotated by skipping the calls to * atan2, sqrtf, sin, and cos. */ if (rotation > 0.001f || rotation < -0.001f) { const float angle = atan2f(y, x) + rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); y = flen * sinf(angle); } co[0] = x; co[1] = y; co[2] = 0.0f; externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); } CLAMP(intensity, 0.0f, 1.0f); switch (br->mask_pressure) { case BRUSH_MASK_PRESSURE_CUTOFF: intensity = ((1.0f - intensity) < ups->size_pressure_value) ? 1.0f : 0.0f; break; case BRUSH_MASK_PRESSURE_RAMP: intensity = ups->size_pressure_value + intensity * (1.0f - ups->size_pressure_value); break; default: break; } return intensity; }
/* Generic texture sampler for 3D painting systems. point has to be either in * region space mouse coordinates, or 3d world coordinates for 3D mapping. * * rgba outputs straight alpha. */ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br, const float point[3], float rgba[4], const int thread, struct ImagePool *pool) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; MTex *mtex = &br->mtex; float intensity = 1.0; bool hasrgb = false; if (!mtex->tex) { intensity = 1; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { /* Get strength by feeding the vertex * location directly into a texture */ hasrgb = externtex(mtex, point, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); } else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) { float rotation = -mtex->rot; float point_2d[2] = {point[0], point[1]}; float x, y; float co[3]; x = point_2d[0] - br->stencil_pos[0]; y = point_2d[1] - br->stencil_pos[1]; if (rotation > 0.001f || rotation < -0.001f) { const float angle = atan2f(y, x) + rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); y = flen * sinf(angle); } if (fabsf(x) > br->stencil_dimension[0] || fabsf(y) > br->stencil_dimension[1]) { zero_v4(rgba); return 0.0f; } x /= (br->stencil_dimension[0]); y /= (br->stencil_dimension[1]); co[0] = x; co[1] = y; co[2] = 0.0f; hasrgb = externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); } else { float rotation = -mtex->rot; float point_2d[2] = {point[0], point[1]}; float x = 0.0f, y = 0.0f; /* Quite warnings */ float invradius = 1.0f; /* Quite warnings */ float co[3]; if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { /* keep coordinates relative to mouse */ rotation += ups->brush_rotation; x = point_2d[0] - ups->tex_mouse[0]; y = point_2d[1] - ups->tex_mouse[1]; /* use pressure adjusted size for fixed mode */ invradius = 1.0f / ups->pixel_radius; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ invradius = 1.0f / BKE_brush_size_get(scene, br); x = point_2d[0]; y = point_2d[1]; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) { rotation += ups->brush_rotation; /* these contain a random coordinate */ x = point_2d[0] - ups->tex_mouse[0]; y = point_2d[1] - ups->tex_mouse[1]; invradius = 1.0f / ups->pixel_radius; } x *= invradius; y *= invradius; /* it is probably worth optimizing for those cases where * the texture is not rotated by skipping the calls to * atan2, sqrtf, sin, and cos. */ if (rotation > 0.001f || rotation < -0.001f) { const float angle = atan2f(y, x) + rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); y = flen * sinf(angle); } co[0] = x; co[1] = y; co[2] = 0.0f; hasrgb = externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); } intensity += br->texture_sample_bias; if (!hasrgb) { rgba[0] = intensity; rgba[1] = intensity; rgba[2] = intensity; rgba[3] = 1.0f; } /* For consistency, sampling always returns color in linear space */ else if (ups->do_linear_conversion) { IMB_colormanagement_colorspace_to_scene_linear_v3(rgba, ups->colorspace); } return intensity; }
static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2]) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; const int diameter = 2 * BKE_brush_size_get(scene, brush); const int size = (cache->size) ? cache->size : diameter; const float alpha = BKE_brush_alpha_get(scene, brush); const bool use_masking = painter->cache.use_masking; bool do_random = false; bool do_partial_update = false; bool do_view = false; float tex_rotation = -brush->mtex.rot; float mask_rotation = -brush->mask_mtex.rot; /* determine how can update based on textures used */ if (painter->cache.is_texbrush) { if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { do_view = true; tex_rotation += ups->brush_rotation; } else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) do_random = true; else do_partial_update = true; brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } if (painter->cache.is_maskbrush) { if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { do_view = true; mask_rotation += ups->brush_rotation; } else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) do_random = true; else do_partial_update = true; brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); } if (do_view || do_random) do_partial_update = false; painter->pool = BKE_image_pool_new(); /* detect if we need to recreate image brush buffer */ if (diameter != cache->lastsize || alpha != cache->lastalpha || brush->jitter != cache->lastjitter || tex_rotation != cache->last_tex_rotation || mask_rotation != cache->last_mask_rotation || do_random) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); cache->ibuf = NULL; } if (cache->mask) { MEM_freeN(cache->mask); cache->mask = NULL; } if (do_partial_update) { /* do partial update of texture + recreate mask */ cache->mask = brush_painter_mask_new(painter, size); brush_painter_imbuf_partial_update(painter, pos); } else { /* create brush and mask from scratch */ if (use_masking) cache->mask = brush_painter_mask_new(painter, size); cache->ibuf = brush_painter_imbuf_new(painter, size); } cache->lastsize = diameter; cache->lastalpha = alpha; cache->lastjitter = brush->jitter; cache->last_tex_rotation = tex_rotation; cache->last_mask_rotation = mask_rotation; } else if (do_partial_update) { /* do only partial update of texture */ int dx = (int)painter->lastpaintpos[0] - (int)pos[0]; int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; if ((dx != 0) || (dy != 0)) { brush_painter_imbuf_partial_update(painter, pos); } } BKE_image_pool_free(painter->pool); painter->pool = NULL; }
/* create imbuf with brush color */ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) { Scene *scene = painter->scene; Brush *brush = painter->brush; rctf tex_mapping = painter->tex_mapping; rctf mask_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; bool use_masking = painter->cache.use_masking; bool use_color_correction = painter->cache.use_color_correction; bool use_float = painter->cache.use_float; bool is_texbrush = painter->cache.is_texbrush; bool is_maskbrush = painter->cache.is_maskbrush; float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush); int radius = BKE_brush_size_get(scene, brush); int xoff = -size * 0.5f + 0.5f; int yoff = -size * 0.5f + 0.5f; int x, y, thread = 0; float brush_rgb[3]; /* allocate image buffer */ ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { copy_v3_v3(brush_rgb, brush->rgb); if (use_color_correction) srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb); } else { brush_rgb[0] = 1.0f; brush_rgb[1] = 1.0f; brush_rgb[2] = 1.0f; } /* fill image buffer */ for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { /* sample texture and multiply with brush color */ float texco[3], rgba[4]; if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { linearrgb_to_srgb_v3_v3(rgba, rgba); } mul_v3_v3(rgba, brush_rgb); } else { copy_v3_v3(rgba, brush_rgb); rgba[3] = 1.0f; } if (is_maskbrush) { brush_imbuf_tex_co(&mask_mapping, x, y, texco); rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); } /* when not using masking, multiply in falloff and strength */ if (!use_masking) { float xy[2] = {x + xoff, y + yoff}; float len = len_v2(xy); rgba[3] *= alpha * BKE_brush_curve_strength_clamp(brush, len, radius); } if (use_float) { /* write to float pixel */ float *dstf = ibuf->rect_float + (y * size + x) * 4; mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ dstf[3] = rgba[3]; } else { /* write to byte pixel */ unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; rgb_float_to_uchar(dst, rgba); dst[3] = FTOCHAR(rgba[3]); } } } return ibuf; }
int BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, const float pos[2], double time, float pressure, void *user, int use_color_correction) { Scene *scene = painter->scene; 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_pressure_apply(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 = BKE_brush_size_get(scene, brush); /* compute brush spacing adapted to brush radius, spacing may depend * on pressure, so update it */ brush_pressure_apply(painter, brush, painter->lastpressure); spacing = max_ff(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_pressure_apply(painter, brush, press); spacing = max_ff(1.0f, radius) * brush->spacing * 0.01f; BKE_brush_jitter_pos(scene, 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 { BKE_brush_jitter_pos(scene, 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_pressure_apply(painter, brush, pressure); BKE_brush_jitter_pos(scene, 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; BKE_brush_alpha_set(scene, brush, painter->startalpha); BKE_brush_size_set(scene, 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, const float pos[2]) { Scene *scene = painter->scene; Brush *brush = painter->brush; ImBuf *ibuf, *maskibuf, *texibuf; float *bf, *mf, *tf, *otf = NULL, xoff, yoff, xy[2], rgba[4]; unsigned char *b, *m, *t, *ot = NULL; int dotexold, origx = x, origy = y; const int radius = BKE_brush_size_get(painter->scene, 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 = min_ii(w, ibuf->x); h = min_ii(h, ibuf->y); if (painter->cache.flt) { for (; y < h; y++) { bf = ibuf->rect_float + (y * ibuf->x + origx) * 4; tf = texibuf->rect_float + (y * texibuf->x + origx) * 4; mf = maskibuf->rect_float + (y * maskibuf->x + origx) * 4; if (dotexold) otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + xt) * 4; for (x = origx; x < w; x++, bf += 4, mf += 4, tf += 4) { if (dotexold) { copy_v3_v3(tf, otf); tf[3] = otf[3]; otf += 4; } else { xy[0] = x + xoff; xy[1] = y + yoff; BKE_brush_sample_tex(scene, 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 = (unsigned char *)ibuf->rect + (y * ibuf->x + origx) * 4; t = (unsigned char *)texibuf->rect + (y * texibuf->x + origx) * 4; m = (unsigned char *)maskibuf->rect + (y * maskibuf->x + origx) * 4; if (dotexold) ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + xt) * 4; for (x = origx; x < w; x++, b += 4, m += 4, t += 4) { if (dotexold) { t[0] = ot[0]; t[1] = ot[1]; t[2] = ot[2]; t[3] = ot[3]; ot += 4; } else { xy[0] = x + xoff; xy[1] = y + yoff; BKE_brush_sample_tex(scene, brush, xy, rgba, 0); rgba_float_to_uchar(t, rgba); } b[0] = t[0] * m[0] / 255; b[1] = t[1] * m[1] / 255; b[2] = t[2] * m[2] / 255; b[3] = t[3] * m[3] / 255; } } } }
/* TODO, use define for 'texfall' arg */ void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction) { ImBuf *ibuf; float xy[2], rgba[4], *dstf; int x, y, rowbytes, xoff, yoff, imbflag; const int radius = BKE_brush_size_get(scene, brush); unsigned char *dst, crgb[3]; const float alpha = BKE_brush_alpha_get(scene, 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) { copy_v3_v3(dstf, brush_rgb); dstf[3] = alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); } else if (texfall == 1) { BKE_brush_sample_tex(scene, brush, xy, dstf, 0); } else { BKE_brush_sample_tex(scene, brush, xy, rgba, 0); mul_v3_v3v3(dstf, rgba, brush_rgb); dstf[3] = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); } } } } else { float alpha_f; /* final float alpha to convert to char */ rgb_float_to_uchar(crgb, brush->rgb); for (y = 0; y < ibuf->y; y++) { dst = (unsigned char *)ibuf->rect + y * rowbytes; for (x = 0; x < ibuf->x; x++, dst += 4) { xy[0] = x + xoff; xy[1] = y + yoff; if (texfall == 0) { alpha_f = alpha * BKE_brush_curve_strength(brush, len_v2(xy), radius); dst[0] = crgb[0]; dst[1] = crgb[1]; dst[2] = crgb[2]; dst[3] = FTOCHAR(alpha_f); } else if (texfall == 1) { BKE_brush_sample_tex(scene, brush, xy, rgba, 0); rgba_float_to_uchar(dst, rgba); } else if (texfall == 2) { BKE_brush_sample_tex(scene, brush, xy, rgba, 0); mul_v3_v3(rgba, brush->rgb); alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); rgb_float_to_uchar(dst, rgba); dst[3] = FTOCHAR(alpha_f); } else { BKE_brush_sample_tex(scene, brush, xy, rgba, 0); alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); dst[0] = crgb[0]; dst[1] = crgb[1]; dst[2] = crgb[2]; dst[3] = FTOCHAR(alpha_f); } } } } *outbuf = ibuf; }