Paint: 2D paint brush stroke antialiasing

This commit enables antialiasing in 2D painting.
It also includes some fixes related to line drawing in the stroke spacing code.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D5833
This commit is contained in:
Pablo Dobarro 2019-10-07 19:34:15 +02:00
parent dccdc5df10
commit ce22efb425
3 changed files with 58 additions and 19 deletions

View File

@ -540,7 +540,7 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
RNA_float_get_array(itemptr, "mouse", mouse);
pressure = RNA_float_get(itemptr, "pressure");
eraser = RNA_boolean_get(itemptr, "pen_flip");
size = max_ff(1.0f, RNA_float_get(itemptr, "size"));
size = RNA_float_get(itemptr, "size");
/* stroking with fill tool only acts on stroke end */
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {

View File

@ -374,25 +374,65 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
/* create a mask with the falloff strength */
static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter,
int diameter,
float radius)
float radius,
const float pos[2])
{
Brush *brush = painter->brush;
int xoff = -radius;
int yoff = -radius;
int offset = (int)floorf(diameter / 2.0f);
unsigned short *mask, *m;
int x, y;
mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask");
m = mask;
for (y = 0; y < diameter; y++) {
for (x = 0; x < diameter; x++, m++) {
float xy[2] = {x + xoff, y + yoff};
float len = len_v2(xy);
int aa_samples = 1.0f / (radius * 0.20f);
aa_samples = clamp_i(aa_samples, 3, 16);
*m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius));
/* Temporal until we have the brush properties */
const float hardness = 1.0f;
const float rotation = 0.0f;
float aa_offset = 1.0f / (2.0f * (float)aa_samples);
float aa_step = 1.0f / (float)aa_samples;
float bpos[2];
bpos[0] = pos[0] - floorf(pos[0]) + offset + aa_offset;
bpos[1] = pos[1] - floorf(pos[1]) + offset + aa_offset;
const float co = cosf(DEG2RADF(rotation));
const float si = sinf(DEG2RADF(rotation));
float norm_factor = 65535.0f / (float)(aa_samples * aa_samples);
for (int y = 0; y < diameter; y++) {
for (int x = 0; x < diameter; x++, m++) {
float total_samples = 0;
for (int i = 0; i < aa_samples; i++) {
for (int j = 0; j < aa_samples; j++) {
float pixel_xy[2] = {x + (aa_step * i), y + (aa_step * j)};
float xy_rot[2];
sub_v2_v2(pixel_xy, bpos);
xy_rot[0] = co * pixel_xy[0] - si * pixel_xy[1];
xy_rot[1] = si * pixel_xy[0] + co * pixel_xy[1];
float len = len_v2(xy_rot);
float p = len / radius;
if (hardness < 1.0f) {
p = (p - hardness) / (1 - hardness);
p = 1.0f - p;
CLAMP(p, 0, 1);
}
else {
p = 1.0;
}
float hardness_factor = 3.0f * p * p - 2.0f * p * p * p;
float curve = BKE_brush_curve_strength_clamped(brush, len, radius);
total_samples += curve * hardness_factor;
}
}
*m = (unsigned short)(total_samples * norm_factor);
}
}
@ -721,7 +761,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
Brush *brush = painter->brush;
BrushPainterCache *cache = &painter->cache;
const int diameter = 2 * size;
/* Adding 4 pixels of padding for brush antialiasing */
const int diameter = MAX2(1, size * 2) + 4;
bool do_random = false;
bool do_partial_update = false;
@ -802,15 +843,13 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
/* curve mask can only change if the size changes */
if (diameter != cache->lastdiameter) {
if (cache->curve_mask) {
MEM_freeN(cache->curve_mask);
cache->curve_mask = NULL;
}
cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size);
if (cache->curve_mask) {
MEM_freeN(cache->curve_mask);
cache->curve_mask = NULL;
}
cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos);
/* detect if we need to recreate image brush buffer */
if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) ||
do_random || update_color) {

View File

@ -669,7 +669,7 @@ static float paint_space_stroke_spacing(bContext *C,
return max_ff(0.001f, size_clamp * spacing / 50.f);
}
else {
return max_ff(1.0, size_clamp * spacing / 50.0f);
return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
}
}