Painting: migrated curve mask generation to CPP.

Curve mask generation is done during 2d texture painting. There are some
performance issues in this part of the code. Before addressing those we
move the code to CPP.
This commit is contained in:
Jeroen Bakker 2021-11-22 10:44:49 +01:00
parent 29f6ec56e6
commit 9bbb5f5a6a
4 changed files with 195 additions and 99 deletions

View File

@ -44,6 +44,7 @@ set(SRC
paint_hide.c
paint_image.c
paint_image_2d.c
paint_image_2d_curve_mask.cc
paint_image_proj.c
paint_mask.c
paint_ops.c

View File

@ -78,12 +78,13 @@ typedef struct BrushPainterCache {
ImBuf *ibuf;
ImBuf *texibuf;
ushort *curve_mask;
ushort *tex_mask;
ushort *tex_mask_old;
uint tex_mask_old_w;
uint tex_mask_old_h;
CurveMaskCache curve_mask_cache;
int image_size[2];
} BrushPainterCache;
@ -169,9 +170,6 @@ static void brush_painter_2d_require_imbuf(
if (cache->ibuf) {
IMB_freeImBuf(cache->ibuf);
}
if (cache->curve_mask) {
MEM_freeN(cache->curve_mask);
}
if (cache->tex_mask) {
MEM_freeN(cache->tex_mask);
}
@ -179,7 +177,6 @@ static void brush_painter_2d_require_imbuf(
MEM_freeN(cache->tex_mask_old);
}
cache->ibuf = NULL;
cache->curve_mask = NULL;
cache->tex_mask = NULL;
cache->lastdiameter = -1; /* force ibuf create in refresh */
cache->invert = invert;
@ -200,9 +197,7 @@ static void brush_painter_cache_2d_free(BrushPainterCache *cache)
if (cache->texibuf) {
IMB_freeImBuf(cache->texibuf);
}
if (cache->curve_mask) {
MEM_freeN(cache->curve_mask);
}
paint_curve_mask_cache_free_data(&cache->curve_mask_cache);
if (cache->tex_mask) {
MEM_freeN(cache->tex_mask);
}
@ -380,92 +375,6 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter,
cache->tex_mask_old_h = diameter;
}
/* create a mask with the falloff strength */
static ushort *brush_painter_curve_mask_init(
ushort *mask, BrushPainter *painter, int diameter, float radius, const float pos[2])
{
BLI_assert_msg(MEM_allocN_len(mask) == diameter * diameter * sizeof(ushort),
"Allocated size of mask doesn't match.");
Brush *brush = painter->brush;
int offset = (int)floorf(diameter / 2.0f);
ushort *m;
m = mask;
int aa_samples = 1.0f / (radius * 0.20f);
if (brush->sampling_flag & BRUSH_PAINT_ANTIALIASING) {
aa_samples = clamp_i(aa_samples, 3, 16);
}
else {
aa_samples = 1;
}
/* 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.0f - hardness);
p = 1.0f - p;
CLAMP(p, 0.0f, 1.0f);
}
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 = (ushort)(total_samples * norm_factor);
}
}
return mask;
}
static void brush_painter_curve_mask_refresh(
BrushPainter *painter, ImagePaintTile *tile, int diameter, float radius, const float pos[2])
{
BrushPainterCache *cache = &tile->cache;
if (diameter != cache->lastdiameter) {
if (cache->curve_mask != NULL) {
MEM_freeN(cache->curve_mask);
}
cache->curve_mask = MEM_mallocN(sizeof(ushort) * diameter * diameter, "brush_painter_mask");
}
brush_painter_curve_mask_init(cache->curve_mask, painter, diameter, radius, pos);
}
/* create imbuf with brush color */
static ImBuf *brush_painter_imbuf_new(
BrushPainter *painter, ImagePaintTile *tile, const int size, float pressure, float distance)
@ -872,7 +781,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
/* Re-initialize the curve mask. Mask is always recreated due to the change of position. */
brush_painter_curve_mask_refresh(painter, tile, diameter, size, pos);
paint_curve_mask_cache_update(&cache->curve_mask_cache, brush, diameter, size, pos);
/* detect if we need to recreate image brush buffer */
if (diameter != cache->lastdiameter || (tex_rotation != cache->last_tex_rotation) || do_random ||
@ -1336,7 +1245,7 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
&tmpbuf,
frombuf,
mask,
tile->cache.curve_mask,
tile->cache.curve_mask_cache.curve_mask,
tile->cache.tex_mask,
mask_max,
region->destx,
@ -1485,7 +1394,7 @@ static int paint_2d_op(void *state,
canvas,
frombuf,
NULL,
tile->cache.curve_mask,
tile->cache.curve_mask_cache.curve_mask,
tile->cache.tex_mask,
mask_max,
region[a].destx,

View File

@ -0,0 +1,149 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup ed
*/
#include "BLI_math.h"
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
#include "BKE_brush.h"
#include "paint_intern.h"
namespace blender::ed::sculpt_paint {
/* create a mask with the falloff strength */
static void update_curve_mask(CurveMaskCache *curve_mask_cache,
const Brush *brush,
const int diameter,
const float radius,
const float cursor_position[2])
{
BLI_assert(curve_mask_cache->curve_mask != nullptr);
int offset = (int)floorf(diameter / 2.0f);
unsigned short *m = curve_mask_cache->curve_mask;
int aa_samples = 1.0f / (radius * 0.20f);
if (brush->sampling_flag & BRUSH_PAINT_ANTIALIASING) {
aa_samples = clamp_i(aa_samples, 3, 16);
}
else {
aa_samples = 1;
}
/* 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] = cursor_position[0] - floorf(cursor_position[0]) + offset - aa_offset;
bpos[1] = cursor_position[1] - floorf(cursor_position[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.0f - hardness);
p = 1.0f - p;
CLAMP(p, 0.0f, 1.0f);
}
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);
}
}
}
static size_t diameter_to_curve_mask_size(const int diameter)
{
return diameter * diameter * sizeof(ushort);
}
static bool is_curve_mask_size_valid(const CurveMaskCache *curve_mask_cache, const int diameter)
{
return curve_mask_cache->curve_mask_size == diameter_to_curve_mask_size(diameter);
}
static void curve_mask_free(CurveMaskCache *curve_mask_cache)
{
curve_mask_cache->curve_mask_size = 0;
MEM_SAFE_FREE(curve_mask_cache->curve_mask);
}
static void curve_mask_allocate(CurveMaskCache *curve_mask_cache, const int diameter)
{
const size_t curve_mask_size = diameter_to_curve_mask_size(diameter);
curve_mask_cache->curve_mask = static_cast<unsigned short *>(
MEM_mallocN(curve_mask_size, __func__));
curve_mask_cache->curve_mask_size = curve_mask_size;
}
} // namespace blender::ed::sculpt_paint
using namespace blender::ed::sculpt_paint;
void paint_curve_mask_cache_free_data(CurveMaskCache *curve_mask_cache)
{
curve_mask_free(curve_mask_cache);
}
void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache,
const Brush *brush,
const int diameter,
const float radius,
const float cursor_position[2])
{
if (!is_curve_mask_size_valid(curve_mask_cache, diameter)) {
curve_mask_free(curve_mask_cache);
curve_mask_allocate(curve_mask_cache, diameter);
}
update_curve_mask(curve_mask_cache, brush, diameter, radius, cursor_position);
}

View File

@ -23,6 +23,13 @@
#pragma once
#include "BKE_paint.h"
#include "DNA_scene_types.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ARegion;
struct Brush;
struct ColorManagedDisplay;
@ -43,8 +50,6 @@ struct wmEvent;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
enum ePaintMode;
enum ePaintSymmetryFlags;
typedef struct CoNo {
float co[3];
@ -245,6 +250,33 @@ void PAINT_OT_add_texture_paint_slot(struct wmOperatorType *ot);
void PAINT_OT_image_paint(struct wmOperatorType *ot);
void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot);
/* paint_image_2d_curve_mask.cc */
/**
* \brief Caching structure for curve mask.
*
* When 2d painting images the curve mask is used as an input.
*/
typedef struct CurveMaskCache {
/**
* \brief Size in bytes of the curve_mask field.
*
* Used to determine if the curve_mask needs to be re-allocated.
*/
size_t curve_mask_size;
/**
* \brief Curve mask that can be passed as curve_mask parameter when.
*/
ushort *curve_mask;
} CurveMaskCache;
void paint_curve_mask_cache_free_data(CurveMaskCache *curve_mask_cache);
void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache,
const struct Brush *brush,
const int diameter,
const float radius,
const float cursor_position[2]);
/* sculpt_uv.c */
void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot);
@ -366,3 +398,8 @@ void paint_delete_blur_kernel(BlurKernel *);
/* paint curve defines */
#define PAINT_CURVE_NUM_SEGMENTS 40
#ifdef __cplusplus
}
#endif