diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
4 files changed, 195 insertions, 99 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 3b668a1bd4c..b826ff8701d 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -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 diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 318180e5eb4..7191431cf4c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -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, diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc new file mode 100644 index 00000000000..639dfcdae72 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc @@ -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); +} + diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 7341d984c91..13ae2b34860 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -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 + |