Compare commits
167 Commits
temp-multi
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
24d1c352e8 | |||
b3639670ee | |||
be1891e895 | |||
cbe4036406 | |||
eb0c50ac78 | |||
6e51ef9531 | |||
![]() |
ebdae75736 | ||
7d17f2addf | |||
4c6d207343 | |||
42f89b9212 | |||
153b45037f | |||
daa7c59e38 | |||
344aca3b1b | |||
064167fce7 | |||
a95e56b741 | |||
8f4730e66f | |||
![]() |
21d4a888b8 | ||
![]() |
cd118c5581 | ||
27f138f4c8 | |||
2f0e350ffd | |||
b4b3f518aa | |||
5aa3167e48 | |||
8165333de9 | |||
9564b6cf23 | |||
0682af0d63 | |||
62f2204d65 | |||
aa067bef5e | |||
0de3d4e8c7 | |||
b477333473 | |||
a1e91fbef3 | |||
ea3ee04fa8 | |||
4838436387 | |||
62d485e470 | |||
cd6eb65482 | |||
d337c20cf9 | |||
a715aec6a3 | |||
eba32b5f4a | |||
1d859c9183 | |||
a3d02965da | |||
45e58a7e92 | |||
c920b83b84 | |||
34e0d8079f | |||
a376073f1e | |||
29ac510198 | |||
d7cf8babf2 | |||
3becd4cb61 | |||
52bb0d42b3 | |||
acae8f430d | |||
bbb692ffb5 | |||
df6a819982 | |||
48f2643556 | |||
d4b441851f | |||
e0406d029d | |||
c5c82f801a | |||
78c0e7c015 | |||
fd0ac1aec5 | |||
0827cce9de | |||
2bba77045f | |||
00ecf29ec4 | |||
172e713cc6 | |||
5c32227025 | |||
fa9d1cb2a9 | |||
577a12f840 | |||
8dad29d0ea | |||
4eda4fd49a | |||
1db53a8923 | |||
20aba8a4c0 | |||
1d53bf0412 | |||
1177debb01 | |||
047daf5442 | |||
f2112199a4 | |||
36de290807 | |||
c09a535333 | |||
4e78bb3f0c | |||
49f33e9820 | |||
6f525e0d98 | |||
963448a2af | |||
9506ef320e | |||
1700fde0dc | |||
d5a83a5a32 | |||
a7dc3d1e90 | |||
6fd836f53b | |||
40455c21fa | |||
2d0442e943 | |||
d55046b167 | |||
4233128fa5 | |||
798496233a | |||
991f6b15f3 | |||
aef45a4ef2 | |||
504e3c563f | |||
8645ea16de | |||
5e292d923f | |||
1ab1d6665c | |||
b1c98ad8ee | |||
e9d95fddf9 | |||
782d240a1b | |||
36055ba366 | |||
236576fc91 | |||
e7d57e84bb | |||
b5573bfbf4 | |||
3b2522650b | |||
eacc1f1ff4 | |||
9562a90632 | |||
2ceeaf95a1 | |||
c8de9dc2a4 | |||
f8f2873b44 | |||
648fa6d89e | |||
d24419564e | |||
184be40aa1 | |||
6131506689 | |||
e5f43cf4b7 | |||
770d70127f | |||
801034ba6a | |||
17442bcb7b | |||
ba9561ab0d | |||
c659af0c13 | |||
dd8be48466 | |||
5914b8ed6b | |||
50da3cfbd6 | |||
131e8c0d59 | |||
aedd43b33f | |||
adb437ad3f | |||
c106ed8fcd | |||
11a6a56982 | |||
0a7ab583a5 | |||
09009ce062 | |||
dc502fad43 | |||
3abce9e633 | |||
dad5c3991c | |||
dab3a07659 | |||
53a724c804 | |||
bacde38b67 | |||
d9b128baf2 | |||
ae6c063b33 | |||
67e7aee853 | |||
302ab3ff7c | |||
4352d1beb5 | |||
32cb953b9b | |||
1d92a4d1a0 | |||
2472b0f0ef | |||
626be25646 | |||
8132383662 | |||
f95214e9ae | |||
dde42e19e3 | |||
f90a83f816 | |||
433b4ae22a | |||
23e217eafc | |||
87153cf019 | |||
08122a7794 | |||
58af171bc0 | |||
c98a535f5f | |||
8262e6a0c1 | |||
c00cc9fd60 | |||
0a31b1f044 | |||
5e3b33dfe8 | |||
f716060408 | |||
aa092f4e6f | |||
ad8dfb3823 | |||
bb0e0fde32 | |||
3472f83f00 | |||
1b2b07fb18 | |||
64a11ba6a2 | |||
26d8d2884c | |||
12014ccfbc | |||
34cebfbb78 | |||
2cc4e1572a | |||
ceb9e7d71e |
@@ -1598,8 +1598,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_PARAMETER -Wunused-parameter)
|
||||
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_ALL -Wall)
|
||||
# Designated initializer is a C++20 feature & breaks MSVC build. Dropping MSVC 2019 or
|
||||
# updating to C++20 allows removing this.
|
||||
# Using C++20 features while having C++17 as the project language isn't allowed by MSVC.
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_CXX20_DESIGNATOR -Wc++20-designator)
|
||||
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare)
|
||||
|
@@ -51,9 +51,9 @@ buildbot:
|
||||
gcc:
|
||||
version: '9.0.0'
|
||||
cuda10:
|
||||
version: '10.1.0'
|
||||
version: '10.1.243'
|
||||
cuda11:
|
||||
version: '11.4.0'
|
||||
version: '11.4.1'
|
||||
optix:
|
||||
version: '7.1.0'
|
||||
cmake:
|
||||
|
@@ -424,7 +424,7 @@ static inline void set_enum(PointerRNA &ptr, const char *name, const string &ide
|
||||
static inline string get_string(PointerRNA &ptr, const char *name)
|
||||
{
|
||||
char cstrbuf[1024];
|
||||
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf));
|
||||
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf), NULL);
|
||||
string str(cstr);
|
||||
if (cstr != cstrbuf)
|
||||
MEM_freeN(cstr);
|
||||
|
@@ -36,10 +36,10 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf),
|
||||
ccl_device float3 calculate_principled_diffuse_brdf(
|
||||
const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf)
|
||||
{
|
||||
float NdotL = max(dot(N, L), 0.0f);
|
||||
float NdotV = max(dot(N, V), 0.0f);
|
||||
float NdotL = dot(N, L);
|
||||
float NdotV = dot(N, V);
|
||||
|
||||
if (NdotL < 0 || NdotV < 0) {
|
||||
if (NdotL <= 0 || NdotV <= 0) {
|
||||
*pdf = 0.0f;
|
||||
return make_float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
@@ -997,7 +997,6 @@ class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel):
|
||||
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
||||
|
||||
col = flow.column()
|
||||
col.row().prop(font_style, "font_kerning_style", expand=True)
|
||||
col.prop(font_style, "points")
|
||||
|
||||
col = flow.column(align=True)
|
||||
|
@@ -475,24 +475,15 @@ texture_node_categories = [
|
||||
geometry_node_categories = [
|
||||
# Geometry Nodes
|
||||
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
|
||||
NodeItem("GeometryNodeAttributeRandomize"),
|
||||
NodeItem("GeometryNodeAttributeMath"),
|
||||
NodeItem("GeometryNodeAttributeClamp"),
|
||||
NodeItem("GeometryNodeAttributeCompare"),
|
||||
NodeItem("GeometryNodeAttributeConvert"),
|
||||
NodeItem("GeometryNodeAttributeCurveMap"),
|
||||
NodeItem("GeometryNodeAttributeFill"),
|
||||
NodeItem("GeometryNodeAttributeMix"),
|
||||
NodeItem("GeometryNodeAttributeProximity"),
|
||||
NodeItem("GeometryNodeAttributeColorRamp"),
|
||||
NodeItem("GeometryNodeAttributeVectorMath"),
|
||||
NodeItem("GeometryNodeAttributeVectorRotate"),
|
||||
NodeItem("GeometryNodeAttributeSampleTexture"),
|
||||
NodeItem("GeometryNodeAttributeCombineXYZ"),
|
||||
NodeItem("GeometryNodeAttributeSeparateXYZ"),
|
||||
NodeItem("GeometryNodeAttributeRemove"),
|
||||
NodeItem("GeometryNodeAttributeMapRange"),
|
||||
NodeItem("GeometryNodeAttributeTransfer"),
|
||||
NodeItem("GeometryNodeAttributeExtract"),
|
||||
NodeItem("GeometryNodeAttributeFreeze"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeMixRGB"),
|
||||
@@ -514,6 +505,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeCurveSplineType"),
|
||||
NodeItem("GeometryNodeCurveSetHandles"),
|
||||
NodeItem("GeometryNodeCurveSelectHandles"),
|
||||
NodeItem("GeometryNodeEvaluateCurve"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
|
||||
NodeItem("GeometryNodeCurvePrimitiveLine"),
|
||||
@@ -532,6 +524,8 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeJoinGeometry"),
|
||||
NodeItem("GeometryNodeSeparateComponents"),
|
||||
NodeItem("GeometryNodeRaycast"),
|
||||
NodeItem("GeometryNodeAttributeProximity"),
|
||||
NodeItem("GeometryNodeSetPosition"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_INPUT", "Input", items=[
|
||||
NodeItem("GeometryNodeObjectInfo"),
|
||||
@@ -542,6 +536,11 @@ geometry_node_categories = [
|
||||
NodeItem("FunctionNodeInputVector"),
|
||||
NodeItem("GeometryNodeInputMaterial"),
|
||||
NodeItem("GeometryNodeIsViewport"),
|
||||
NodeItem("GeometryNodeAttribute"),
|
||||
NodeItem("GeometryNodeIndex"),
|
||||
NodeItem("GeometryNodeNormal"),
|
||||
NodeItem("GeometryNodePosition"),
|
||||
NodeItem("GeometryNodeCurveParameter"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
|
||||
NodeItem("GeometryNodeMaterialAssign"),
|
||||
@@ -554,6 +553,9 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeEdgeSplit"),
|
||||
NodeItem("GeometryNodeSubdivisionSurface"),
|
||||
NodeItem("GeometryNodeMeshSubdivide"),
|
||||
NodeItem("GeometryNodeExtrude"),
|
||||
NodeItem("GeometryNodeExtrudeAndMove"),
|
||||
NodeItem("GeometryNodeSampleMeshSurface"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
|
||||
NodeItem("GeometryNodeMeshCircle"),
|
||||
@@ -565,7 +567,6 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeMeshLine"),
|
||||
NodeItem("GeometryNodeMeshUVSphere"),
|
||||
]),
|
||||
|
||||
GeometryNodeCategory("GEO_POINT", "Point", items=[
|
||||
NodeItem("GeometryNodePointDistribute"),
|
||||
NodeItem("GeometryNodePointInstance"),
|
||||
@@ -573,7 +574,6 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodePointScale"),
|
||||
NodeItem("GeometryNodePointTranslate"),
|
||||
NodeItem("GeometryNodeRotatePoints"),
|
||||
NodeItem("GeometryNodeAlignRotationToVector"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
@@ -583,6 +583,7 @@ geometry_node_categories = [
|
||||
NodeItem("FunctionNodeFloatCompare"),
|
||||
NodeItem("FunctionNodeFloatToInt"),
|
||||
NodeItem("GeometryNodeSwitch"),
|
||||
NodeItem("ShaderNodeTexNoise"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
|
||||
NodeItem("ShaderNodeVectorCurve"),
|
||||
@@ -590,6 +591,7 @@ geometry_node_categories = [
|
||||
NodeItem("ShaderNodeCombineXYZ"),
|
||||
NodeItem("ShaderNodeVectorMath"),
|
||||
NodeItem("ShaderNodeVectorRotate"),
|
||||
NodeItem("FunctionNodeAlignRotationToVector"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
|
||||
NodeItem("GeometryNodeViewer"),
|
||||
|
@@ -98,13 +98,13 @@ void BLF_batch_draw_flush(void);
|
||||
void BLF_batch_draw_end(void);
|
||||
|
||||
/* Draw the string using the current font. */
|
||||
void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
|
||||
ATTR_NONNULL(2);
|
||||
void BLF_draw(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
|
||||
void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
|
||||
void BLF_draw_ascii_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
|
||||
ATTR_NONNULL(2);
|
||||
void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
|
||||
int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) ATTR_NONNULL(2);
|
||||
void BLF_draw_ascii(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
|
||||
int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth) ATTR_NONNULL(2);
|
||||
|
||||
typedef bool (*BLF_GlyphBoundsFn)(const char *str,
|
||||
const size_t str_step_ofs,
|
||||
@@ -116,43 +116,45 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str,
|
||||
|
||||
void BLF_boundbox_foreach_glyph_ex(int fontid,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
BLF_GlyphBoundsFn user_fn,
|
||||
void *user_data,
|
||||
struct ResultBLF *r_info) ATTR_NONNULL(2);
|
||||
void BLF_boundbox_foreach_glyph(int fontid,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
BLF_GlyphBoundsFn user_fn,
|
||||
void *user_data) ATTR_NONNULL(2);
|
||||
|
||||
/* Get the string byte offset that fits within a given width */
|
||||
size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
|
||||
ATTR_NONNULL(2);
|
||||
size_t BLF_width_to_strlen(
|
||||
int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
|
||||
/* Same as BLF_width_to_strlen but search from the string end */
|
||||
size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
|
||||
ATTR_NONNULL(2);
|
||||
size_t BLF_width_to_rstrlen(
|
||||
int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
|
||||
|
||||
/* This function return the bounding box of the string
|
||||
* and are not multiplied by the aspect.
|
||||
*/
|
||||
void BLF_boundbox_ex(int fontid,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct rctf *box,
|
||||
struct ResultBLF *r_info) ATTR_NONNULL(2);
|
||||
void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box) ATTR_NONNULL();
|
||||
void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL();
|
||||
|
||||
/* The next both function return the width and height
|
||||
* of the string, using the current font and both value
|
||||
* are multiplied by the aspect of the font.
|
||||
*/
|
||||
float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float BLF_width_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
|
||||
float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
float BLF_height_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
|
||||
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
|
||||
float BLF_height(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
|
||||
/* Return dimensions of the font without any sample text. */
|
||||
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT;
|
||||
@@ -163,8 +165,8 @@ float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
|
||||
/* The following function return the width and height of the string, but
|
||||
* just in one call, so avoid extra freetype2 stuff.
|
||||
*/
|
||||
void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
|
||||
ATTR_NONNULL();
|
||||
void BLF_width_and_height(
|
||||
int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL();
|
||||
|
||||
/* For fixed width fonts only, returns the width of a
|
||||
* character.
|
||||
@@ -221,9 +223,9 @@ void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2);
|
||||
/* Draw the string into the buffer, this function draw in both buffer,
|
||||
* float and unsigned char _BUT_ it's not necessary set both buffer, NULL is valid here.
|
||||
*/
|
||||
void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw_buffer_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
|
||||
ATTR_NONNULL(2);
|
||||
void BLF_draw_buffer(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
|
||||
void BLF_draw_buffer(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
|
||||
|
||||
/* Add a path to the font dir paths. */
|
||||
void BLF_dir_add(const char *path) ATTR_NONNULL();
|
||||
@@ -254,8 +256,9 @@ void BLF_default_dpi(int dpi);
|
||||
void BLF_default_set(int fontid);
|
||||
int BLF_default(void); /* get default font ID so we can pass it to other functions */
|
||||
/* Draw the string using the default font, size and dpi. */
|
||||
void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
|
||||
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
|
||||
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL();
|
||||
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t str_len)
|
||||
ATTR_NONNULL();
|
||||
/* Set size and DPI, and return default font ID. */
|
||||
int BLF_set_default(void);
|
||||
|
||||
|
@@ -521,7 +521,7 @@ static void blf_draw_gl__end(FontBLF *font)
|
||||
}
|
||||
}
|
||||
|
||||
void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
@@ -530,27 +530,27 @@ void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_in
|
||||
if (font) {
|
||||
blf_draw_gl__start(font);
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_draw__wrap(font, str, len, r_info);
|
||||
blf_font_draw__wrap(font, str, str_len, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_draw(font, str, len, r_info);
|
||||
blf_font_draw(font, str, str_len, r_info);
|
||||
}
|
||||
blf_draw_gl__end(font);
|
||||
}
|
||||
}
|
||||
void BLF_draw(int fontid, const char *str, size_t len)
|
||||
void BLF_draw(int fontid, const char *str, const size_t str_len)
|
||||
{
|
||||
if (len == 0 || str[0] == '\0') {
|
||||
if (str_len == 0 || str[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid bgl usage to corrupt BLF drawing. */
|
||||
GPU_bgl_end();
|
||||
|
||||
BLF_draw_ex(fontid, str, len, NULL);
|
||||
BLF_draw_ex(fontid, str, str_len, NULL);
|
||||
}
|
||||
|
||||
void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw_ascii_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
@@ -560,27 +560,27 @@ void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF
|
||||
blf_draw_gl__start(font);
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
/* Use non-ASCII draw function for word-wrap. */
|
||||
blf_font_draw__wrap(font, str, len, r_info);
|
||||
blf_font_draw__wrap(font, str, str_len, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_draw_ascii(font, str, len, r_info);
|
||||
blf_font_draw_ascii(font, str, str_len, r_info);
|
||||
}
|
||||
blf_draw_gl__end(font);
|
||||
}
|
||||
}
|
||||
|
||||
void BLF_draw_ascii(int fontid, const char *str, size_t len)
|
||||
void BLF_draw_ascii(int fontid, const char *str, const size_t str_len)
|
||||
{
|
||||
if (len == 0 || str[0] == '\0') {
|
||||
if (str_len == 0 || str[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
BLF_draw_ascii_ex(fontid, str, len, NULL);
|
||||
BLF_draw_ascii_ex(fontid, str, str_len, NULL);
|
||||
}
|
||||
|
||||
int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
|
||||
int BLF_draw_mono(int fontid, const char *str, const size_t str_len, int cwidth)
|
||||
{
|
||||
if (len == 0 || str[0] == '\0') {
|
||||
if (str_len == 0 || str[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -589,7 +589,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
|
||||
|
||||
if (font) {
|
||||
blf_draw_gl__start(font);
|
||||
columns = blf_font_draw_mono(font, str, len, cwidth);
|
||||
columns = blf_font_draw_mono(font, str, str_len, cwidth);
|
||||
blf_draw_gl__end(font);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
|
||||
*/
|
||||
void BLF_boundbox_foreach_glyph_ex(int fontid,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
BLF_GlyphBoundsFn user_fn,
|
||||
void *user_data,
|
||||
struct ResultBLF *r_info)
|
||||
@@ -621,25 +621,26 @@ void BLF_boundbox_foreach_glyph_ex(int fontid,
|
||||
BLI_assert(0);
|
||||
}
|
||||
else {
|
||||
blf_font_boundbox_foreach_glyph(font, str, len, user_fn, user_data, r_info);
|
||||
blf_font_boundbox_foreach_glyph(font, str, str_len, user_fn, user_data, r_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLF_boundbox_foreach_glyph(
|
||||
int fontid, const char *str, size_t len, BLF_GlyphBoundsFn user_fn, void *user_data)
|
||||
int fontid, const char *str, const size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data)
|
||||
{
|
||||
BLF_boundbox_foreach_glyph_ex(fontid, str, len, user_fn, user_data, NULL);
|
||||
BLF_boundbox_foreach_glyph_ex(fontid, str, str_len, user_fn, user_data, NULL);
|
||||
}
|
||||
|
||||
size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
|
||||
size_t BLF_width_to_strlen(
|
||||
int fontid, const char *str, const size_t str_len, float width, float *r_width)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font) {
|
||||
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
|
||||
size_t ret;
|
||||
ret = blf_font_width_to_strlen(font, str, len, width / xa, r_width);
|
||||
ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width);
|
||||
if (r_width) {
|
||||
*r_width *= xa;
|
||||
}
|
||||
@@ -652,14 +653,15 @@ size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width,
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
|
||||
size_t BLF_width_to_rstrlen(
|
||||
int fontid, const char *str, const size_t str_len, float width, float *r_width)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font) {
|
||||
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
|
||||
size_t ret;
|
||||
ret = blf_font_width_to_rstrlen(font, str, len, width / xa, r_width);
|
||||
ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width);
|
||||
if (r_width) {
|
||||
*r_width *= xa;
|
||||
}
|
||||
@@ -673,7 +675,7 @@ size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width
|
||||
}
|
||||
|
||||
void BLF_boundbox_ex(
|
||||
int fontid, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
|
||||
int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
@@ -681,47 +683,48 @@ void BLF_boundbox_ex(
|
||||
|
||||
if (font) {
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_boundbox__wrap(font, str, len, r_box, r_info);
|
||||
blf_font_boundbox__wrap(font, str, str_len, r_box, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_boundbox(font, str, len, r_box, r_info);
|
||||
blf_font_boundbox(font, str, str_len, r_box, r_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLF_boundbox(int fontid, const char *str, size_t len, rctf *r_box)
|
||||
void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box)
|
||||
{
|
||||
BLF_boundbox_ex(fontid, str, len, r_box, NULL);
|
||||
BLF_boundbox_ex(fontid, str, str_len, r_box, NULL);
|
||||
}
|
||||
|
||||
void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
|
||||
void BLF_width_and_height(
|
||||
int fontid, const char *str, const size_t str_len, float *r_width, float *r_height)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font) {
|
||||
blf_font_width_and_height(font, str, len, r_width, r_height, NULL);
|
||||
blf_font_width_and_height(font, str, str_len, r_width, r_height, NULL);
|
||||
}
|
||||
else {
|
||||
*r_width = *r_height = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float BLF_width_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
BLF_RESULT_CHECK_INIT(r_info);
|
||||
|
||||
if (font) {
|
||||
return blf_font_width(font, str, len, r_info);
|
||||
return blf_font_width(font, str, str_len, r_info);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float BLF_width(int fontid, const char *str, size_t len)
|
||||
float BLF_width(int fontid, const char *str, const size_t str_len)
|
||||
{
|
||||
return BLF_width_ex(fontid, str, len, NULL);
|
||||
return BLF_width_ex(fontid, str, str_len, NULL);
|
||||
}
|
||||
|
||||
float BLF_fixed_width(int fontid)
|
||||
@@ -735,22 +738,22 @@ float BLF_fixed_width(int fontid)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float BLF_height_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
BLF_RESULT_CHECK_INIT(r_info);
|
||||
|
||||
if (font) {
|
||||
return blf_font_height(font, str, len, r_info);
|
||||
return blf_font_height(font, str, str_len, r_info);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float BLF_height(int fontid, const char *str, size_t len)
|
||||
float BLF_height(int fontid, const char *str, const size_t str_len)
|
||||
{
|
||||
return BLF_height_ex(fontid, str, len, NULL);
|
||||
return BLF_height_ex(fontid, str, str_len, NULL);
|
||||
}
|
||||
|
||||
int BLF_height_max(int fontid)
|
||||
@@ -894,24 +897,27 @@ void blf_draw_buffer__end(void)
|
||||
{
|
||||
}
|
||||
|
||||
void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void BLF_draw_buffer_ex(int fontid,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font && (font->buf_info.fbuf || font->buf_info.cbuf)) {
|
||||
blf_draw_buffer__start(font);
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_draw_buffer__wrap(font, str, len, r_info);
|
||||
blf_font_draw_buffer__wrap(font, str, str_len, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_draw_buffer(font, str, len, r_info);
|
||||
blf_font_draw_buffer(font, str, str_len, r_info);
|
||||
}
|
||||
blf_draw_buffer__end();
|
||||
}
|
||||
}
|
||||
void BLF_draw_buffer(int fontid, const char *str, size_t len)
|
||||
void BLF_draw_buffer(int fontid, const char *str, const size_t str_len)
|
||||
{
|
||||
BLF_draw_buffer_ex(fontid, str, len, NULL);
|
||||
BLF_draw_buffer_ex(fontid, str, str_len, NULL);
|
||||
}
|
||||
|
||||
char *BLF_display_name_from_file(const char *filename)
|
||||
|
@@ -68,23 +68,23 @@ int BLF_set_default(void)
|
||||
return global_font_default;
|
||||
}
|
||||
|
||||
void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
|
||||
void BLF_draw_default(float x, float y, float z, const char *str, const size_t str_len)
|
||||
{
|
||||
ASSERT_DEFAULT_SET;
|
||||
|
||||
const uiStyle *style = UI_style_get();
|
||||
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
|
||||
BLF_position(global_font_default, x, y, z);
|
||||
BLF_draw(global_font_default, str, len);
|
||||
BLF_draw(global_font_default, str, str_len);
|
||||
}
|
||||
|
||||
/* same as above but call 'BLF_draw_ascii' */
|
||||
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len)
|
||||
void BLF_draw_default_ascii(float x, float y, float z, const char *str, const size_t str_len)
|
||||
{
|
||||
ASSERT_DEFAULT_SET;
|
||||
|
||||
const uiStyle *style = UI_style_get();
|
||||
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
|
||||
BLF_position(global_font_default, x, y, z);
|
||||
BLF_draw_ascii(global_font_default, str, len); /* XXX, use real length */
|
||||
BLF_draw_ascii(global_font_default, str, str_len); /* XXX, use real length */
|
||||
}
|
||||
|
@@ -365,7 +365,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
|
||||
static void blf_font_draw_ex(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info,
|
||||
int pen_y)
|
||||
{
|
||||
@@ -374,14 +374,14 @@ static void blf_font_draw_ex(FontBLF *font,
|
||||
int pen_x = 0;
|
||||
size_t i = 0;
|
||||
|
||||
if (len == 0) {
|
||||
if (str_len == 0) {
|
||||
/* early output, don't do any IMM OpenGL. */
|
||||
return;
|
||||
}
|
||||
|
||||
blf_batch_draw_begin(font);
|
||||
|
||||
while ((i < len) && str[i]) {
|
||||
while ((i < str_len) && str[i]) {
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
if (UNLIKELY(c == BLI_UTF8_ERR)) {
|
||||
@@ -407,16 +407,16 @@ static void blf_font_draw_ex(FontBLF *font,
|
||||
r_info->width = pen_x;
|
||||
}
|
||||
}
|
||||
void blf_font_draw(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info)
|
||||
{
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
blf_font_draw_ex(font, gc, str, len, r_info, 0);
|
||||
blf_font_draw_ex(font, gc, str, str_len, r_info, 0);
|
||||
blf_glyph_cache_release(font);
|
||||
}
|
||||
|
||||
/* faster version of blf_font_draw, ascii only for view dimensions */
|
||||
static void blf_font_draw_ascii_ex(
|
||||
FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y)
|
||||
FontBLF *font, const char *str, size_t str_len, struct ResultBLF *r_info, int pen_y)
|
||||
{
|
||||
unsigned int c, c_prev = BLI_UTF8_ERR;
|
||||
GlyphBLF *g, *g_prev = NULL;
|
||||
@@ -426,7 +426,7 @@ static void blf_font_draw_ascii_ex(
|
||||
|
||||
blf_batch_draw_begin(font);
|
||||
|
||||
while ((c = *(str++)) && len--) {
|
||||
while ((c = *(str++)) && str_len--) {
|
||||
BLI_assert(c < GLYPH_ASCII_TABLE_SIZE);
|
||||
g = gc->glyph_ascii_table[c];
|
||||
if (UNLIKELY(g == NULL)) {
|
||||
@@ -456,13 +456,16 @@ static void blf_font_draw_ascii_ex(
|
||||
blf_glyph_cache_release(font);
|
||||
}
|
||||
|
||||
void blf_font_draw_ascii(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void blf_font_draw_ascii(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
blf_font_draw_ascii_ex(font, str, len, r_info, 0);
|
||||
blf_font_draw_ascii_ex(font, str, str_len, r_info, 0);
|
||||
}
|
||||
|
||||
/* use fixed column width, but an utf8 character may occupy multiple columns */
|
||||
int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
|
||||
int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth)
|
||||
{
|
||||
unsigned int c;
|
||||
GlyphBLF *g;
|
||||
@@ -474,7 +477,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
|
||||
|
||||
blf_batch_draw_begin(font);
|
||||
|
||||
while ((i < len) && str[i]) {
|
||||
while ((i < str_len) && str[i]) {
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
if (UNLIKELY(c == BLI_UTF8_ERR)) {
|
||||
@@ -512,7 +515,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
|
||||
static void blf_font_draw_buffer_ex(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info,
|
||||
int pen_y)
|
||||
{
|
||||
@@ -531,7 +534,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
|
||||
|
||||
/* another buffer specific call for color conversion */
|
||||
|
||||
while ((i < len) && str[i]) {
|
||||
while ((i < str_len) && str[i]) {
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
if (UNLIKELY(c == BLI_UTF8_ERR)) {
|
||||
@@ -646,10 +649,13 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
|
||||
}
|
||||
}
|
||||
|
||||
void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void blf_font_draw_buffer(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
blf_font_draw_buffer_ex(font, gc, str, len, r_info, 0);
|
||||
blf_font_draw_buffer_ex(font, gc, str, str_len, r_info, 0);
|
||||
blf_glyph_cache_release(font);
|
||||
}
|
||||
|
||||
@@ -685,7 +691,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
|
||||
}
|
||||
|
||||
size_t blf_font_width_to_strlen(
|
||||
FontBLF *font, const char *str, size_t len, float width, float *r_width)
|
||||
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
|
||||
{
|
||||
unsigned int c, c_prev = BLI_UTF8_ERR;
|
||||
GlyphBLF *g, *g_prev;
|
||||
@@ -695,7 +701,7 @@ size_t blf_font_width_to_strlen(
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
const int width_i = (int)width;
|
||||
|
||||
for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i];
|
||||
for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i];
|
||||
i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) {
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
@@ -713,7 +719,7 @@ size_t blf_font_width_to_strlen(
|
||||
}
|
||||
|
||||
size_t blf_font_width_to_rstrlen(
|
||||
FontBLF *font, const char *str, size_t len, float width, float *r_width)
|
||||
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
|
||||
{
|
||||
unsigned int c, c_prev = BLI_UTF8_ERR;
|
||||
GlyphBLF *g, *g_prev;
|
||||
@@ -724,7 +730,7 @@ size_t blf_font_width_to_rstrlen(
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
const int width_i = (int)width;
|
||||
|
||||
i = BLI_strnlen(str, len);
|
||||
i = BLI_strnlen(str, str_len);
|
||||
s = BLI_str_find_prev_char_utf8(str, &str[i]);
|
||||
i = (size_t)((s != NULL) ? s - str : 0);
|
||||
s_prev = BLI_str_find_prev_char_utf8(str, s);
|
||||
@@ -765,7 +771,7 @@ size_t blf_font_width_to_rstrlen(
|
||||
static void blf_font_boundbox_ex(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
rctf *box,
|
||||
struct ResultBLF *r_info,
|
||||
int pen_y)
|
||||
@@ -781,7 +787,7 @@ static void blf_font_boundbox_ex(FontBLF *font,
|
||||
box->ymin = 32000.0f;
|
||||
box->ymax = -32000.0f;
|
||||
|
||||
while ((i < len) && str[i]) {
|
||||
while ((i < str_len) && str[i]) {
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
if (UNLIKELY(c == BLI_UTF8_ERR)) {
|
||||
@@ -829,16 +835,16 @@ static void blf_font_boundbox_ex(FontBLF *font,
|
||||
}
|
||||
}
|
||||
void blf_font_boundbox(
|
||||
FontBLF *font, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
|
||||
FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
|
||||
{
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
blf_font_boundbox_ex(font, gc, str, len, r_box, r_info, 0);
|
||||
blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0);
|
||||
blf_glyph_cache_release(font);
|
||||
}
|
||||
|
||||
void blf_font_width_and_height(FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
float *r_width,
|
||||
float *r_height,
|
||||
struct ResultBLF *r_info)
|
||||
@@ -856,16 +862,19 @@ void blf_font_width_and_height(FontBLF *font,
|
||||
}
|
||||
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_boundbox__wrap(font, str, len, &box, r_info);
|
||||
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_boundbox(font, str, len, &box, r_info);
|
||||
blf_font_boundbox(font, str, str_len, &box, r_info);
|
||||
}
|
||||
*r_width = (BLI_rctf_size_x(&box) * xa);
|
||||
*r_height = (BLI_rctf_size_y(&box) * ya);
|
||||
}
|
||||
|
||||
float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float blf_font_width(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
float xa;
|
||||
rctf box;
|
||||
@@ -878,15 +887,18 @@ float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBL
|
||||
}
|
||||
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_boundbox__wrap(font, str, len, &box, r_info);
|
||||
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_boundbox(font, str, len, &box, r_info);
|
||||
blf_font_boundbox(font, str, str_len, &box, r_info);
|
||||
}
|
||||
return BLI_rctf_size_x(&box) * xa;
|
||||
}
|
||||
|
||||
float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
float blf_font_height(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
float ya;
|
||||
rctf box;
|
||||
@@ -899,10 +911,10 @@ float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultB
|
||||
}
|
||||
|
||||
if (font->flags & BLF_WORD_WRAP) {
|
||||
blf_font_boundbox__wrap(font, str, len, &box, r_info);
|
||||
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
|
||||
}
|
||||
else {
|
||||
blf_font_boundbox(font, str, len, &box, r_info);
|
||||
blf_font_boundbox(font, str, str_len, &box, r_info);
|
||||
}
|
||||
return BLI_rctf_size_y(&box) * ya;
|
||||
}
|
||||
@@ -930,7 +942,7 @@ float blf_font_fixed_width(FontBLF *font)
|
||||
static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
BLF_GlyphBoundsFn user_fn,
|
||||
void *user_data,
|
||||
struct ResultBLF *r_info,
|
||||
@@ -942,12 +954,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
|
||||
size_t i = 0, i_curr;
|
||||
rcti gbox;
|
||||
|
||||
if (len == 0) {
|
||||
if (str_len == 0) {
|
||||
/* early output. */
|
||||
return;
|
||||
}
|
||||
|
||||
while ((i < len) && str[i]) {
|
||||
while ((i < str_len) && str[i]) {
|
||||
i_curr = i;
|
||||
g = blf_utf8_next_fast(font, gc, str, &i, &c);
|
||||
|
||||
@@ -981,13 +993,13 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
|
||||
}
|
||||
void blf_font_boundbox_foreach_glyph(FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
BLF_GlyphBoundsFn user_fn,
|
||||
void *user_data,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
|
||||
blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0);
|
||||
blf_font_boundbox_foreach_glyph_ex(font, gc, str, str_len, user_fn, user_data, r_info, 0);
|
||||
blf_glyph_cache_release(font);
|
||||
}
|
||||
|
||||
@@ -1008,12 +1020,12 @@ void blf_font_boundbox_foreach_glyph(FontBLF *font,
|
||||
*/
|
||||
static void blf_font_wrap_apply(FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info,
|
||||
void (*callback)(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
int pen_y,
|
||||
void *userdata),
|
||||
void *userdata)
|
||||
@@ -1032,8 +1044,8 @@ static void blf_font_wrap_apply(FontBLF *font,
|
||||
size_t start, last[2];
|
||||
} wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}};
|
||||
|
||||
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str);
|
||||
while ((i < len) && str[i]) {
|
||||
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str);
|
||||
while ((i < str_len) && str[i]) {
|
||||
|
||||
/* wrap vars */
|
||||
size_t i_curr = i;
|
||||
@@ -1061,7 +1073,7 @@ static void blf_font_wrap_apply(FontBLF *font,
|
||||
if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) {
|
||||
do_draw = true;
|
||||
}
|
||||
else if (UNLIKELY(((i < len) && str[i]) == 0)) {
|
||||
else if (UNLIKELY(((i < str_len) && str[i]) == 0)) {
|
||||
/* need check here for trailing newline, else we draw it */
|
||||
wrap.last[0] = i + ((g->c != '\n') ? 1 : 0);
|
||||
wrap.last[1] = i;
|
||||
@@ -1112,54 +1124,61 @@ static void blf_font_wrap_apply(FontBLF *font,
|
||||
static void blf_font_draw__wrap_cb(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
int pen_y,
|
||||
void *UNUSED(userdata))
|
||||
{
|
||||
blf_font_draw_ex(font, gc, str, len, NULL, pen_y);
|
||||
blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y);
|
||||
}
|
||||
void blf_font_draw__wrap(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
|
||||
void blf_font_draw__wrap(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
blf_font_wrap_apply(font, str, len, r_info, blf_font_draw__wrap_cb, NULL);
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw__wrap_cb, NULL);
|
||||
}
|
||||
|
||||
/* blf_font_boundbox__wrap */
|
||||
static void blf_font_boundbox_wrap_cb(
|
||||
FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t len, int pen_y, void *userdata)
|
||||
static void blf_font_boundbox_wrap_cb(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
const size_t str_len,
|
||||
int pen_y,
|
||||
void *userdata)
|
||||
{
|
||||
rctf *box = userdata;
|
||||
rctf box_single;
|
||||
|
||||
blf_font_boundbox_ex(font, gc, str, len, &box_single, NULL, pen_y);
|
||||
blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y);
|
||||
BLI_rctf_union(box, &box_single);
|
||||
}
|
||||
void blf_font_boundbox__wrap(
|
||||
FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info)
|
||||
FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info)
|
||||
{
|
||||
box->xmin = 32000.0f;
|
||||
box->xmax = -32000.0f;
|
||||
box->ymin = 32000.0f;
|
||||
box->ymax = -32000.0f;
|
||||
|
||||
blf_font_wrap_apply(font, str, len, r_info, blf_font_boundbox_wrap_cb, box);
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box);
|
||||
}
|
||||
|
||||
/* blf_font_draw_buffer__wrap */
|
||||
static void blf_font_draw_buffer__wrap_cb(FontBLF *font,
|
||||
GlyphCacheBLF *gc,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
int pen_y,
|
||||
void *UNUSED(userdata))
|
||||
{
|
||||
blf_font_draw_buffer_ex(font, gc, str, len, NULL, pen_y);
|
||||
blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y);
|
||||
}
|
||||
void blf_font_draw_buffer__wrap(FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
const size_t str_len,
|
||||
struct ResultBLF *r_info)
|
||||
{
|
||||
blf_font_wrap_apply(font, str, len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
|
||||
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1170,14 +1189,14 @@ void blf_font_draw_buffer__wrap(FontBLF *font,
|
||||
|
||||
int blf_font_count_missing_chars(FontBLF *font,
|
||||
const char *str,
|
||||
const size_t len,
|
||||
const size_t str_len,
|
||||
int *r_tot_chars)
|
||||
{
|
||||
int missing = 0;
|
||||
size_t i = 0;
|
||||
|
||||
*r_tot_chars = 0;
|
||||
while (i < len) {
|
||||
while (i < str_len) {
|
||||
unsigned int c;
|
||||
|
||||
if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {
|
||||
|
@@ -53,46 +53,55 @@ struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem
|
||||
void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
|
||||
|
||||
void blf_font_size(struct FontBLF *font, unsigned int size, unsigned int dpi);
|
||||
void blf_font_draw(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
|
||||
void blf_font_draw(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
void blf_font_draw__wrap(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
void blf_font_draw_ascii(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t len, int cwidth);
|
||||
int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t str_len, int cwidth);
|
||||
void blf_font_draw_buffer(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
void blf_font_draw_buffer__wrap(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
size_t blf_font_width_to_strlen(
|
||||
struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
|
||||
struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
|
||||
size_t blf_font_width_to_rstrlen(
|
||||
struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
|
||||
struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
|
||||
void blf_font_boundbox(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct rctf *r_box,
|
||||
struct ResultBLF *r_info);
|
||||
void blf_font_boundbox__wrap(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
struct rctf *r_box,
|
||||
struct ResultBLF *r_info);
|
||||
void blf_font_width_and_height(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
float *r_width,
|
||||
float *r_height,
|
||||
struct ResultBLF *r_info);
|
||||
float blf_font_width(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
|
||||
float blf_font_height(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
|
||||
float blf_font_width(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
float blf_font_height(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t str_len,
|
||||
struct ResultBLF *r_info);
|
||||
float blf_font_fixed_width(struct FontBLF *font);
|
||||
int blf_font_height_max(struct FontBLF *font);
|
||||
int blf_font_width_max(struct FontBLF *font);
|
||||
@@ -103,7 +112,7 @@ char *blf_display_name(struct FontBLF *font);
|
||||
|
||||
void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
|
||||
const char *str,
|
||||
size_t len,
|
||||
size_t str_len,
|
||||
bool (*user_fn)(const char *str,
|
||||
const size_t str_step_ofs,
|
||||
const struct rcti *glyph_step_bounds,
|
||||
@@ -116,7 +125,7 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
|
||||
|
||||
int blf_font_count_missing_chars(struct FontBLF *font,
|
||||
const char *str,
|
||||
const size_t len,
|
||||
const size_t str_len,
|
||||
int *r_tot_chars);
|
||||
|
||||
void blf_font_free(struct FontBLF *font);
|
||||
|
@@ -37,10 +37,12 @@
|
||||
struct AttributeMetaData {
|
||||
AttributeDomain domain;
|
||||
CustomDataType data_type;
|
||||
const AnonymousCustomDataLayerID *anonymous_layer_id = nullptr;
|
||||
|
||||
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
|
||||
{
|
||||
return (a.domain == b.domain) && (a.data_type == b.data_type);
|
||||
return (a.domain == b.domain) && (a.data_type == b.data_type) &&
|
||||
(a.anonymous_layer_id == b.anonymous_layer_id);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -354,6 +356,12 @@ class CustomDataAttributes {
|
||||
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
|
||||
bool remove(const blender::StringRef name);
|
||||
|
||||
bool create_anonymous(const AnonymousCustomDataLayerID &id, const CustomDataType data_type);
|
||||
std::optional<blender::fn::GSpan> get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id) const;
|
||||
std::optional<blender::fn::GMutableSpan> get_anonymous_for_write(
|
||||
const AnonymousCustomDataLayerID &id);
|
||||
|
||||
bool foreach_attribute(const AttributeForeachCallback callback,
|
||||
const AttributeDomain domain) const;
|
||||
};
|
||||
|
@@ -482,6 +482,14 @@ void CustomData_external_reload(struct CustomData *data,
|
||||
CustomDataMask mask,
|
||||
int totelem);
|
||||
|
||||
/* Anonymous layers. */
|
||||
struct AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name);
|
||||
void CustomData_anonymous_id_strong_decrement(const struct AnonymousCustomDataLayerID *layer_id);
|
||||
void CustomData_anonymous_id_strong_increment(const struct AnonymousCustomDataLayerID *layer_id);
|
||||
void CustomData_anonymous_id_weak_decrement(const struct AnonymousCustomDataLayerID *layer_id);
|
||||
void CustomData_anonymous_id_weak_increment(const struct AnonymousCustomDataLayerID *layer_id);
|
||||
bool CustomData_layer_is_unused_anonymous(const struct CustomDataLayer *layer);
|
||||
|
||||
/* Mesh-to-mesh transfer data. */
|
||||
|
||||
struct CustomDataTransferLayerMap;
|
||||
|
553
source/blender/blenkernel/BKE_field.hh
Normal file
553
source/blender/blenkernel/BKE_field.hh
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_optional_ptr.hh"
|
||||
#include "BLI_user_counter.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "FN_cpp_type.hh"
|
||||
#include "FN_cpp_type_make.hh"
|
||||
#include "FN_multi_function.hh"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
using fn::CPPType;
|
||||
using fn::GMutableSpan;
|
||||
using fn::GVArray;
|
||||
using fn::GVArrayPtr;
|
||||
using fn::MultiFunction;
|
||||
|
||||
class FieldInputKey {
|
||||
public:
|
||||
virtual ~FieldInputKey() = default;
|
||||
virtual uint64_t hash() const = 0;
|
||||
virtual const CPPType &type() const = 0;
|
||||
|
||||
friend bool operator==(const FieldInputKey &a, const FieldInputKey &b)
|
||||
{
|
||||
return a.is_same_as(b);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool is_same_as(const FieldInputKey &other) const
|
||||
{
|
||||
UNUSED_VARS(other);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class FieldInputValue {
|
||||
public:
|
||||
virtual ~FieldInputValue() = default;
|
||||
};
|
||||
|
||||
class IndexFieldInputKey : public FieldInputKey {
|
||||
public:
|
||||
uint64_t hash() const override
|
||||
{
|
||||
/* Arbitrary number. */
|
||||
return 78582029;
|
||||
}
|
||||
|
||||
const CPPType &type() const override
|
||||
{
|
||||
return CPPType::get<int>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_same_as(const FieldInputKey &other) const override
|
||||
{
|
||||
return dynamic_cast<const IndexFieldInputKey *>(&other) != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class CurveParameterFieldInputKey : public FieldInputKey {
|
||||
public:
|
||||
uint64_t hash() const override
|
||||
{
|
||||
/* Arbitrary number. */
|
||||
return 928347504059;
|
||||
}
|
||||
|
||||
const CPPType &type() const override
|
||||
{
|
||||
return CPPType::get<float>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_same_as(const FieldInputKey &other) const override
|
||||
{
|
||||
return dynamic_cast<const CurveParameterFieldInputKey *>(&other) != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class AnonymousAttributeFieldInputKey : public FieldInputKey {
|
||||
private:
|
||||
AnonymousCustomDataLayerID *layer_id_;
|
||||
const CPPType &type_;
|
||||
|
||||
public:
|
||||
AnonymousAttributeFieldInputKey(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
|
||||
: layer_id_(&layer_id), type_(type)
|
||||
{
|
||||
CustomData_anonymous_id_strong_increment(layer_id_);
|
||||
}
|
||||
|
||||
~AnonymousAttributeFieldInputKey()
|
||||
{
|
||||
CustomData_anonymous_id_strong_decrement(layer_id_);
|
||||
}
|
||||
|
||||
const CPPType &type() const override
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
uint64_t hash() const override
|
||||
{
|
||||
return get_default_hash(layer_id_);
|
||||
}
|
||||
|
||||
const AnonymousCustomDataLayerID &layer_id() const
|
||||
{
|
||||
return *layer_id_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_same_as(const FieldInputKey &other) const override
|
||||
{
|
||||
if (const AnonymousAttributeFieldInputKey *other_typed =
|
||||
dynamic_cast<const AnonymousAttributeFieldInputKey *>(&other)) {
|
||||
return layer_id_ == other_typed->layer_id_ && type_ == other_typed->type_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class PersistentAttributeFieldInputKey : public FieldInputKey {
|
||||
private:
|
||||
std::string name_;
|
||||
const CPPType *type_;
|
||||
|
||||
public:
|
||||
PersistentAttributeFieldInputKey(std::string name, const CPPType &type)
|
||||
: name_(std::move(name)), type_(&type)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t hash() const override
|
||||
{
|
||||
return get_default_hash_2(name_, type_);
|
||||
}
|
||||
|
||||
const CPPType &type() const override
|
||||
{
|
||||
return *type_;
|
||||
}
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_same_as(const FieldInputKey &other) const override
|
||||
{
|
||||
if (const PersistentAttributeFieldInputKey *other_typed =
|
||||
dynamic_cast<const PersistentAttributeFieldInputKey *>(&other)) {
|
||||
return other_typed->type_ == type_ && other_typed->name_ == name_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class GVArrayFieldInputValue : public FieldInputValue {
|
||||
private:
|
||||
optional_ptr<GVArray> varray_;
|
||||
|
||||
public:
|
||||
GVArrayFieldInputValue(optional_ptr<GVArray> varray) : varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
|
||||
const GVArray &varray() const
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
};
|
||||
|
||||
class FieldInputs {
|
||||
private:
|
||||
using InputMap = Map<std::reference_wrapper<const FieldInputKey>, const FieldInputValue *>;
|
||||
InputMap inputs_;
|
||||
|
||||
friend class Field;
|
||||
|
||||
public:
|
||||
InputMap::KeyIterator begin() const
|
||||
{
|
||||
return inputs_.keys().begin();
|
||||
}
|
||||
|
||||
InputMap::KeyIterator end() const
|
||||
{
|
||||
return inputs_.keys().end();
|
||||
}
|
||||
|
||||
int tot_inputs() const
|
||||
{
|
||||
return inputs_.size();
|
||||
}
|
||||
|
||||
void set_input(const FieldInputKey &key, const FieldInputValue &value)
|
||||
{
|
||||
*inputs_.lookup_ptr(key) = &value;
|
||||
}
|
||||
|
||||
const FieldInputValue *get(const FieldInputKey &key) const
|
||||
{
|
||||
return inputs_.lookup_default(key, nullptr);
|
||||
}
|
||||
|
||||
template<typename ValueT> const ValueT *get(const FieldInputKey &key) const
|
||||
{
|
||||
return dynamic_cast<const ValueT *>(this->get(key));
|
||||
}
|
||||
};
|
||||
|
||||
class FieldOutput {
|
||||
private:
|
||||
optional_ptr<const GVArray> varray_;
|
||||
|
||||
public:
|
||||
FieldOutput(optional_ptr<const GVArray> varray) : varray_(std::move(varray))
|
||||
{
|
||||
}
|
||||
|
||||
const GVArray &varray_ref() const
|
||||
{
|
||||
return *varray_;
|
||||
}
|
||||
};
|
||||
|
||||
class Field {
|
||||
private:
|
||||
mutable std::atomic<int> users_ = 1;
|
||||
|
||||
public:
|
||||
virtual ~Field() = default;
|
||||
|
||||
FieldInputs prepare_inputs() const
|
||||
{
|
||||
FieldInputs inputs;
|
||||
this->foreach_input_key([&](const FieldInputKey &key) { inputs.inputs_.add(key, nullptr); });
|
||||
return inputs;
|
||||
}
|
||||
|
||||
virtual void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const
|
||||
{
|
||||
UNUSED_VARS(callback);
|
||||
}
|
||||
|
||||
virtual const CPPType &output_type() const = 0;
|
||||
|
||||
virtual FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const = 0;
|
||||
|
||||
void user_add() const
|
||||
{
|
||||
users_.fetch_add(1);
|
||||
}
|
||||
|
||||
void user_remove() const
|
||||
{
|
||||
const int new_users = users_.fetch_sub(1) - 1;
|
||||
if (new_users == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using FieldPtr = UserCounter<Field>;
|
||||
|
||||
template<typename T> class ConstantField : public Field {
|
||||
private:
|
||||
T value_;
|
||||
|
||||
public:
|
||||
ConstantField(T value) : value_(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
const CPPType &output_type() const override
|
||||
{
|
||||
return CPPType::get<T>();
|
||||
}
|
||||
|
||||
FieldOutput evaluate(IndexMask mask, const FieldInputs &UNUSED(inputs)) const
|
||||
{
|
||||
return optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValue>(
|
||||
CPPType::get<T>(), mask.min_array_size(), &value_)};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename KeyT> class GVArrayInputField : public Field {
|
||||
private:
|
||||
KeyT key_;
|
||||
|
||||
public:
|
||||
template<typename... Args> GVArrayInputField(Args &&...args) : key_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
|
||||
{
|
||||
callback(key_);
|
||||
}
|
||||
|
||||
const CPPType &output_type() const override
|
||||
{
|
||||
return key_.type();
|
||||
}
|
||||
|
||||
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const override
|
||||
{
|
||||
const GVArrayFieldInputValue *input = inputs.get<GVArrayFieldInputValue>(key_);
|
||||
if (input == nullptr) {
|
||||
return FieldOutput{
|
||||
optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValueRef>(
|
||||
key_.type(), mask.min_array_size(), key_.type().default_value())}};
|
||||
}
|
||||
return FieldOutput{optional_ptr<const GVArray>{input->varray()}};
|
||||
}
|
||||
};
|
||||
|
||||
class MultiFunctionField : public Field {
|
||||
private:
|
||||
Vector<FieldPtr> input_fields_;
|
||||
optional_ptr<const MultiFunction> fn_;
|
||||
const int output_param_index_;
|
||||
|
||||
public:
|
||||
MultiFunctionField(Vector<FieldPtr> input_fields,
|
||||
optional_ptr<const MultiFunction> fn,
|
||||
const int output_param_index)
|
||||
: input_fields_(std::move(input_fields)),
|
||||
fn_(std::move(fn)),
|
||||
output_param_index_(output_param_index)
|
||||
{
|
||||
}
|
||||
|
||||
const CPPType &output_type() const override
|
||||
{
|
||||
return fn_->param_type(output_param_index_).data_type().single_type();
|
||||
}
|
||||
|
||||
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
|
||||
{
|
||||
for (const FieldPtr &field : input_fields_) {
|
||||
field->foreach_input_key(callback);
|
||||
}
|
||||
}
|
||||
|
||||
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const final
|
||||
{
|
||||
fn::MFParamsBuilder params{*fn_, mask.min_array_size()};
|
||||
fn::MFContextBuilder context;
|
||||
|
||||
ResourceScope &scope = params.resource_scope();
|
||||
|
||||
Vector<GMutableSpan> outputs;
|
||||
int output_span_index = -1;
|
||||
|
||||
int input_index = 0;
|
||||
for (const int param_index : fn_->param_indices()) {
|
||||
fn::MFParamType param_type = fn_->param_type(param_index);
|
||||
switch (param_type.category()) {
|
||||
case fn::MFParamType::SingleInput: {
|
||||
const Field &field = *input_fields_[input_index];
|
||||
FieldOutput &output = scope.add_value(field.evaluate(mask, inputs), __func__);
|
||||
params.add_readonly_single_input(output.varray_ref());
|
||||
input_index++;
|
||||
break;
|
||||
}
|
||||
case fn::MFParamType::SingleOutput: {
|
||||
const CPPType &type = param_type.data_type().single_type();
|
||||
void *buffer = MEM_mallocN_aligned(
|
||||
mask.min_array_size() * type.size(), type.alignment(), __func__);
|
||||
GMutableSpan span{type, buffer, mask.min_array_size()};
|
||||
outputs.append(span);
|
||||
params.add_uninitialized_single_output(span);
|
||||
if (param_index == output_param_index_) {
|
||||
output_span_index = outputs.size() - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fn::MFParamType::SingleMutable:
|
||||
case fn::MFParamType::VectorInput:
|
||||
case fn::MFParamType::VectorMutable:
|
||||
case fn::MFParamType::VectorOutput:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn_->call(mask, params, context);
|
||||
|
||||
GMutableSpan output_span = outputs[output_span_index];
|
||||
outputs.remove(output_span_index);
|
||||
|
||||
for (GMutableSpan span : outputs) {
|
||||
span.type().destruct_indices(span.data(), mask);
|
||||
MEM_freeN(span.data());
|
||||
}
|
||||
|
||||
std::unique_ptr<GVArray> out_array = std::make_unique<fn::GVArray_For_OwnedGSpan>(output_span,
|
||||
mask);
|
||||
return FieldOutput{optional_ptr<const GVArray>{std::move(out_array)}};
|
||||
}
|
||||
};
|
||||
|
||||
class PersistentAttributeField : public GVArrayInputField<PersistentAttributeFieldInputKey> {
|
||||
public:
|
||||
PersistentAttributeField(std::string name, const CPPType &type)
|
||||
: GVArrayInputField<PersistentAttributeFieldInputKey>(std::move(name), type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AnonymousAttributeField : public GVArrayInputField<AnonymousAttributeFieldInputKey> {
|
||||
public:
|
||||
AnonymousAttributeField(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
|
||||
: GVArrayInputField<AnonymousAttributeFieldInputKey>(layer_id, type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class IndexField : public GVArrayInputField<IndexFieldInputKey> {
|
||||
};
|
||||
class CurveParameterField : public GVArrayInputField<CurveParameterFieldInputKey> {
|
||||
};
|
||||
|
||||
class FieldRefBase {
|
||||
protected:
|
||||
FieldPtr field_;
|
||||
|
||||
public:
|
||||
const FieldPtr &field() const
|
||||
{
|
||||
return field_;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class FieldRef : public FieldRefBase {
|
||||
|
||||
public:
|
||||
FieldRef()
|
||||
{
|
||||
field_ = new ConstantField<T>(T());
|
||||
}
|
||||
|
||||
FieldRef(FieldPtr field)
|
||||
{
|
||||
field_ = std::move(field);
|
||||
}
|
||||
|
||||
const Field *operator->() const
|
||||
{
|
||||
return &*field_;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(&*field_);
|
||||
}
|
||||
|
||||
friend bool operator==(const FieldRef &a, const FieldRef &b)
|
||||
{
|
||||
return &*a.field_ == &*b.field_;
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const FieldRef &a)
|
||||
{
|
||||
stream << &*a.field_;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct FieldRefCPPTypeParam {
|
||||
};
|
||||
|
||||
class FieldRefCPPType : public CPPType {
|
||||
private:
|
||||
const CPPType &field_type_;
|
||||
FieldPtr (*get_field_)(const void *field_ref);
|
||||
void (*construct_)(void *dst, FieldPtr field);
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
FieldRefCPPType(FieldRefCPPTypeParam<FieldRef<T>> /* unused */, StringRef debug_name)
|
||||
: CPPType(fn::CPPTypeParam<FieldRef<T>, CPPTypeFlags::BasicType>(), debug_name),
|
||||
field_type_(CPPType::get<T>())
|
||||
{
|
||||
get_field_ = [](const void *field_ref) {
|
||||
return ((const blender::bke::FieldRef<T> *)field_ref)->field();
|
||||
};
|
||||
construct_ = [](void *dst, blender::bke::FieldPtr field) {
|
||||
new (dst) blender::bke::FieldRef<T>(std::move(field));
|
||||
};
|
||||
}
|
||||
|
||||
const CPPType &field_type() const
|
||||
{
|
||||
return field_type_;
|
||||
};
|
||||
|
||||
FieldPtr get_field(const void *field_ref) const
|
||||
{
|
||||
return get_field_(field_ref);
|
||||
}
|
||||
|
||||
void construct(void *dst, FieldPtr field) const
|
||||
{
|
||||
construct_(dst, std::move(field));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
#define MAKE_FIELD_REF_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \
|
||||
template<> \
|
||||
const blender::fn::CPPType & \
|
||||
blender::fn::CPPType::get_impl<blender::bke::FieldRef<FIELD_TYPE>>() \
|
||||
{ \
|
||||
static blender::bke::FieldRefCPPType cpp_type{ \
|
||||
blender::bke::FieldRefCPPTypeParam<blender::bke::FieldRef<FIELD_TYPE>>(), \
|
||||
STRINGIFY(DEBUG_NAME)}; \
|
||||
return cpp_type; \
|
||||
}
|
@@ -128,6 +128,28 @@ class GeometryComponent {
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
bool attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
blender::bke::ReadAttributeLookup attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &layer_id) const;
|
||||
|
||||
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const;
|
||||
|
||||
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const;
|
||||
|
||||
blender::bke::WriteAttributeLookup attribute_try_get_anonymous_for_write(
|
||||
const AnonymousCustomDataLayerID &layer_id);
|
||||
|
||||
/* Try to create the builtin attribute with the given name. No data type or domain has to be
|
||||
* provided, because those are fixed for builtin attributes. */
|
||||
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
|
||||
@@ -139,8 +161,8 @@ class GeometryComponent {
|
||||
virtual bool is_empty() const;
|
||||
|
||||
/* Get a virtual array to read the data of an attribute on the given domain and data type.
|
||||
* Returns null when the attribute does not exist or cannot be converted to the requested domain
|
||||
* and data type. */
|
||||
* Returns null when the attribute does not exist or cannot be converted to the requested
|
||||
* domain and data type. */
|
||||
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
@@ -181,14 +203,14 @@ class GeometryComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
|
||||
* used convince features. The returned output attribute might be empty if requested attribute
|
||||
* cannot exist on the geometry.
|
||||
* Returns an "output attribute", which is essentially a mutable virtual array with some
|
||||
* commonly used convince features. The returned output attribute might be empty if requested
|
||||
* attribute cannot exist on the geometry.
|
||||
*
|
||||
* The included convenience features are:
|
||||
* - Implicit type conversion when writing to builtin attributes.
|
||||
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
|
||||
* is created that will overwrite the existing attribute in the end.
|
||||
* - If the attribute name exists already, but has a different type/domain, a temporary
|
||||
* attribute is created that will overwrite the existing attribute in the end.
|
||||
*/
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output(
|
||||
const blender::StringRef attribute_name,
|
||||
@@ -197,8 +219,8 @@ class GeometryComponent {
|
||||
const void *default_value = nullptr);
|
||||
|
||||
/* Same as attribute_try_get_for_output, but should be used when the original values in the
|
||||
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
|
||||
* from this attribute, no default value is necessary. */
|
||||
* attributes are not read, i.e. the attribute is used only for output. Since values are not
|
||||
* read from this attribute, no default value is necessary. */
|
||||
blender::bke::OutputAttribute attribute_try_get_for_output_only(
|
||||
const blender::StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
@@ -224,6 +246,35 @@ class GeometryComponent {
|
||||
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
|
||||
}
|
||||
|
||||
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value = nullptr);
|
||||
|
||||
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output_only(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type);
|
||||
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output(
|
||||
const AnonymousCustomDataLayerID &id, const AttributeDomain domain, const T default_value)
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_anonymous_for_output(id, domain, data_type, &default_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output_only(
|
||||
const AnonymousCustomDataLayerID &id, const AttributeDomain domain)
|
||||
{
|
||||
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
|
||||
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
|
||||
return this->attribute_try_get_anonymous_for_output_only(id, domain, data_type);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
|
||||
};
|
||||
@@ -232,12 +283,12 @@ template<typename T>
|
||||
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
|
||||
|
||||
/**
|
||||
* A geometry set contains zero or more geometry components. There is at most one component of each
|
||||
* type. Individual components might be shared between multiple geometries. Shared components are
|
||||
* copied automatically when write access is requested.
|
||||
* A geometry set contains zero or more geometry components. There is at most one component of
|
||||
* each type. Individual components might be shared between multiple geometries. Shared
|
||||
* components are copied automatically when write access is requested.
|
||||
*
|
||||
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
|
||||
* geometry components.
|
||||
* Copying a geometry set is a relatively cheap operation, because it does not copy the
|
||||
* referenced geometry components.
|
||||
*/
|
||||
struct GeometrySet {
|
||||
private:
|
||||
@@ -345,6 +396,10 @@ class MeshComponent : public GeometryComponent {
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const final;
|
||||
|
||||
blender::VArrayPtr<bool> adapt_selection(blender::VArrayPtr<bool> selection,
|
||||
AttributeDomain from_domain,
|
||||
AttributeDomain to_domain) const;
|
||||
|
||||
bool is_empty() const final;
|
||||
|
||||
bool owns_direct_data() const override;
|
||||
@@ -441,8 +496,8 @@ class InstanceReference {
|
||||
enum class Type {
|
||||
/**
|
||||
* An empty instance. This allows an `InstanceReference` to be default constructed without
|
||||
* being in an invalid state. There might also be other use cases that we haven't explored much
|
||||
* yet (such as changing the instance later on, and "disabling" some instances).
|
||||
* being in an invalid state. There might also be other use cases that we haven't explored
|
||||
* much yet (such as changing the instance later on, and "disabling" some instances).
|
||||
*/
|
||||
None,
|
||||
Object,
|
||||
@@ -513,8 +568,8 @@ class InstancesComponent : public GeometryComponent {
|
||||
blender::Vector<int> instance_ids_;
|
||||
|
||||
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
|
||||
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
|
||||
* unique. Code using these ids should not crash when they are not unique but can generally
|
||||
* all. They are *almost* unique, because under certain very unlikely circumstances, they are
|
||||
* not unique. Code using these ids should not crash when they are not unique but can generally
|
||||
* expect them to be unique. */
|
||||
mutable std::mutex almost_unique_ids_mutex_;
|
||||
mutable blender::Array<int> almost_unique_ids_;
|
||||
|
@@ -346,7 +346,7 @@ typedef struct bNodeType {
|
||||
#define NODE_CLASS_OP_FILTER 5
|
||||
#define NODE_CLASS_GROUP 6
|
||||
// #define NODE_CLASS_FILE 7
|
||||
#define NODE_CLASS_CONVERTOR 8
|
||||
#define NODE_CLASS_CONVERTER 8
|
||||
#define NODE_CLASS_MATTE 9
|
||||
#define NODE_CLASS_DISTORT 10
|
||||
// #define NODE_CLASS_OP_DYNAMIC 11 /* deprecated */
|
||||
@@ -1415,7 +1415,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
|
||||
#define GEO_NODE_POINT_ROTATE 1016
|
||||
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
|
||||
#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
|
||||
// #define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
|
||||
#define GEO_NODE_POINT_TRANSLATE 1019
|
||||
#define GEO_NODE_POINT_SCALE 1020
|
||||
#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021
|
||||
@@ -1472,6 +1472,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_CURVE_SET_HANDLES 1072
|
||||
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
|
||||
#define GEO_NODE_CURVE_SELECT_HANDLES 1074
|
||||
#define GEO_NODE_ATTRIBUTE 1075
|
||||
#define GEO_NODE_INDEX 1076
|
||||
#define GEO_NODE_EXTRUDE 1077
|
||||
#define GEO_NODE_ATTRIBUTE_FREEZE 1078
|
||||
#define GEO_NODE_ATTRIBUTE_EXTRACT 1079
|
||||
#define GEO_NODE_NORMAL 1080
|
||||
#define GEO_NODE_CURVE_PARAMETER 1081
|
||||
#define GEO_NODE_EXTRUDE_AND_MOVE 1082
|
||||
#define GEO_NODE_POSITION 1083
|
||||
#define GEO_NODE_SET_POSITION 1084
|
||||
#define GEO_NODE_SAMPLE_MESH_SURFACE 1085
|
||||
#define GEO_NODE_EVALUATE_CURVE 1086
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -1485,6 +1497,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define FN_NODE_INPUT_VECTOR 1207
|
||||
#define FN_NODE_INPUT_STRING 1208
|
||||
#define FN_NODE_FLOAT_TO_INT 1209
|
||||
#define FN_NODE_ALIGN_ROTATION_TO_VECTOR 1210
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -346,27 +346,33 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
|
||||
if (layer.name != attribute_name) {
|
||||
continue;
|
||||
}
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return this->layer_to_read_attribute<float>(layer, domain_size);
|
||||
case CD_PROP_FLOAT2:
|
||||
return this->layer_to_read_attribute<float2>(layer, domain_size);
|
||||
case CD_PROP_FLOAT3:
|
||||
return this->layer_to_read_attribute<float3>(layer, domain_size);
|
||||
case CD_PROP_INT32:
|
||||
return this->layer_to_read_attribute<int>(layer, domain_size);
|
||||
case CD_PROP_COLOR:
|
||||
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
|
||||
case CD_PROP_BOOL:
|
||||
return this->layer_to_read_attribute<bool>(layer, domain_size);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this->layer_to_read_attribute(layer, domain_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ReadAttributeLookup CustomDataAttributeProvider::layer_to_read_attribute(
|
||||
const CustomDataLayer &layer, const int domain_size) const
|
||||
{
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return this->layer_to_read_attribute<float>(layer, domain_size);
|
||||
case CD_PROP_FLOAT2:
|
||||
return this->layer_to_read_attribute<float2>(layer, domain_size);
|
||||
case CD_PROP_FLOAT3:
|
||||
return this->layer_to_read_attribute<float3>(layer, domain_size);
|
||||
case CD_PROP_INT32:
|
||||
return this->layer_to_read_attribute<int>(layer, domain_size);
|
||||
case CD_PROP_COLOR:
|
||||
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
|
||||
case CD_PROP_BOOL:
|
||||
return this->layer_to_read_attribute<bool>(layer, domain_size);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
GeometryComponent &component, const StringRef attribute_name) const
|
||||
{
|
||||
@@ -380,27 +386,33 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
|
||||
continue;
|
||||
}
|
||||
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return this->layer_to_write_attribute<float>(layer, domain_size);
|
||||
case CD_PROP_FLOAT2:
|
||||
return this->layer_to_write_attribute<float2>(layer, domain_size);
|
||||
case CD_PROP_FLOAT3:
|
||||
return this->layer_to_write_attribute<float3>(layer, domain_size);
|
||||
case CD_PROP_INT32:
|
||||
return this->layer_to_write_attribute<int>(layer, domain_size);
|
||||
case CD_PROP_COLOR:
|
||||
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
|
||||
case CD_PROP_BOOL:
|
||||
return this->layer_to_write_attribute<bool>(layer, domain_size);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this->layer_to_write_attribute(layer, domain_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributeLookup CustomDataAttributeProvider::layer_to_write_attribute(
|
||||
CustomDataLayer &layer, const int domain_size) const
|
||||
{
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT:
|
||||
return this->layer_to_write_attribute<float>(layer, domain_size);
|
||||
case CD_PROP_FLOAT2:
|
||||
return this->layer_to_write_attribute<float2>(layer, domain_size);
|
||||
case CD_PROP_FLOAT3:
|
||||
return this->layer_to_write_attribute<float3>(layer, domain_size);
|
||||
case CD_PROP_INT32:
|
||||
return this->layer_to_write_attribute<int>(layer, domain_size);
|
||||
case CD_PROP_COLOR:
|
||||
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
|
||||
case CD_PROP_BOOL:
|
||||
return this->layer_to_write_attribute<bool>(layer, domain_size);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
|
||||
const StringRef attribute_name) const
|
||||
{
|
||||
@@ -487,6 +499,84 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string get_anonymous_attribute_name()
|
||||
{
|
||||
static std::atomic<int> index = 0;
|
||||
const int next_index = index.fetch_add(1);
|
||||
return "anonymous_attribute_" + std::to_string(next_index);
|
||||
}
|
||||
|
||||
bool CustomDataAttributeProvider::try_create_anonymous(GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &layer_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const
|
||||
{
|
||||
if (domain_ != domain) {
|
||||
return false;
|
||||
}
|
||||
if (!this->type_is_supported(data_type)) {
|
||||
return false;
|
||||
}
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.anonymous_id == &layer_id) {
|
||||
/* Don't create two layers with the same id. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
const std::string attribute_name = get_anonymous_attribute_name();
|
||||
add_named_custom_data_layer_from_attribute_init(
|
||||
attribute_name, *custom_data, data_type, domain_size, initializer);
|
||||
const int layer_index = CustomData_get_named_layer_index(
|
||||
custom_data, data_type, attribute_name.c_str());
|
||||
CustomDataLayer *layer = &custom_data->layers[layer_index];
|
||||
layer->flag |= CD_FLAG_ANONYMOUS;
|
||||
layer->anonymous_id = &layer_id;
|
||||
CustomData_anonymous_id_weak_increment(&layer_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_read(
|
||||
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
|
||||
{
|
||||
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.anonymous_id != &layer_id) {
|
||||
continue;
|
||||
}
|
||||
return this->layer_to_read_attribute(layer, domain_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
WriteAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_write(
|
||||
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
|
||||
{
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(component);
|
||||
if (custom_data == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const int domain_size = component.attribute_domain_size(domain_);
|
||||
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
|
||||
if (layer.anonymous_id != &layer_id) {
|
||||
continue;
|
||||
}
|
||||
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
|
||||
return this->layer_to_write_attribute(layer, domain_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const
|
||||
{
|
||||
@@ -497,7 +587,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
|
||||
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
|
||||
const CustomDataType data_type = (CustomDataType)layer.type;
|
||||
if (this->type_is_supported(data_type)) {
|
||||
AttributeMetaData meta_data{domain_, data_type};
|
||||
AttributeMetaData meta_data{domain_, data_type, layer.anonymous_id};
|
||||
if (!callback(layer.name, meta_data)) {
|
||||
return false;
|
||||
}
|
||||
@@ -698,6 +788,56 @@ bool CustomDataAttributes::create_by_move(const blender::StringRef name,
|
||||
return result != nullptr;
|
||||
}
|
||||
|
||||
bool CustomDataAttributes::create_anonymous(const AnonymousCustomDataLayerID &id,
|
||||
const CustomDataType data_type)
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
||||
if (layer.anonymous_id == &id) {
|
||||
/* Don't create two layers with the same id. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string name = get_anonymous_attribute_name();
|
||||
if (!this->create(name, data_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int layer_index = CustomData_get_named_layer_index(&data, data_type, name.c_str());
|
||||
CustomDataLayer &layer = data.layers[layer_index];
|
||||
layer.flag |= CD_FLAG_ANONYMOUS;
|
||||
layer.anonymous_id = &id;
|
||||
CustomData_anonymous_id_weak_increment(&id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<GSpan> CustomDataAttributes::get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id) const
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
||||
if (layer.anonymous_id == &id) {
|
||||
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
return GSpan(*cpp_type, layer.data, size_);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<GMutableSpan> CustomDataAttributes::get_anonymous_for_write(
|
||||
const AnonymousCustomDataLayerID &id)
|
||||
{
|
||||
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
|
||||
if (layer.anonymous_id == &id) {
|
||||
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
return GMutableSpan(*cpp_type, layer.data, size_);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CustomDataAttributes::remove(const blender::StringRef name)
|
||||
{
|
||||
bool result = false;
|
||||
@@ -721,7 +861,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
|
||||
const AttributeDomain domain) const
|
||||
{
|
||||
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
|
||||
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
|
||||
AttributeMetaData meta_data{domain, (CustomDataType)layer.type, layer.anonymous_id};
|
||||
if (!callback(layer.name, meta_data)) {
|
||||
return false;
|
||||
}
|
||||
@@ -735,6 +875,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
|
||||
/** \name Geometry Component
|
||||
* \{ */
|
||||
|
||||
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
|
||||
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
|
||||
{
|
||||
const blender::nodes::DataTypeConversions &conversions =
|
||||
blender::nodes::get_implicit_type_conversions();
|
||||
return conversions.try_convert(std::move(varray), to_type);
|
||||
}
|
||||
|
||||
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
|
||||
{
|
||||
return nullptr;
|
||||
@@ -875,6 +1023,102 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
if (dynamic_provider->try_create_anonymous(*this, layer_id, domain, data_type, initializer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &layer_id) const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
for (const DynamicAttributesProvider *dynamic_provider :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
ReadAttributeLookup attribute = dynamic_provider->try_get_anonymous_for_read(*this, layer_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type) const
|
||||
{
|
||||
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_anonymous_for_read(id);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
|
||||
if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
|
||||
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
|
||||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
if (varray->type() != *cpp_type) {
|
||||
varray = try_adapt_data_type(std::move(varray), *cpp_type);
|
||||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return varray;
|
||||
}
|
||||
|
||||
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value) const
|
||||
{
|
||||
blender::fn::GVArrayPtr varray = this->attribute_try_get_anonymous_for_read(
|
||||
id, domain, data_type);
|
||||
if (varray) {
|
||||
return varray;
|
||||
}
|
||||
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
|
||||
if (default_value == nullptr) {
|
||||
default_value = type->default_value();
|
||||
}
|
||||
const int domain_size = this->attribute_domain_size(domain);
|
||||
return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
|
||||
}
|
||||
|
||||
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_write(
|
||||
const AnonymousCustomDataLayerID &layer_id)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const ComponentAttributeProviders *providers = this->get_attribute_providers();
|
||||
for (const DynamicAttributesProvider *dynamic_providers :
|
||||
providers->dynamic_attribute_providers()) {
|
||||
WriteAttributeLookup attribute = dynamic_providers->try_get_anonymous_for_write(*this,
|
||||
layer_id);
|
||||
if (attribute) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
@@ -968,14 +1212,6 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
|
||||
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
|
||||
{
|
||||
const blender::nodes::DataTypeConversions &conversions =
|
||||
blender::nodes::get_implicit_type_conversions();
|
||||
return conversions.try_convert(std::move(varray), to_type);
|
||||
}
|
||||
|
||||
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
|
||||
const StringRef attribute_name,
|
||||
const AttributeDomain domain,
|
||||
@@ -1227,3 +1463,122 @@ blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_on
|
||||
{
|
||||
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
|
||||
}
|
||||
|
||||
class GVMutableAttribute_For_AnonymousOutputAttribute
|
||||
: public blender::fn::GVMutableArray_For_GMutableSpan {
|
||||
public:
|
||||
GeometryComponent *component;
|
||||
const AnonymousCustomDataLayerID &final_id;
|
||||
|
||||
GVMutableAttribute_For_AnonymousOutputAttribute(GMutableSpan data,
|
||||
GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &final_id)
|
||||
|
||||
: blender::fn::GVMutableArray_For_GMutableSpan(data),
|
||||
component(&component),
|
||||
final_id(final_id)
|
||||
{
|
||||
}
|
||||
|
||||
~GVMutableAttribute_For_AnonymousOutputAttribute() override
|
||||
{
|
||||
type_->destruct_n(data_, size_);
|
||||
MEM_freeN(data_);
|
||||
}
|
||||
};
|
||||
|
||||
static void save_output_anonymous_attribute(blender::bke::OutputAttribute &output_attribute)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::fn;
|
||||
using namespace blender::bke;
|
||||
|
||||
GVMutableAttribute_For_AnonymousOutputAttribute &varray =
|
||||
dynamic_cast<GVMutableAttribute_For_AnonymousOutputAttribute &>(output_attribute.varray());
|
||||
|
||||
GeometryComponent &component = *varray.component;
|
||||
WriteAttributeLookup write_attribute = component.attribute_try_get_anonymous_for_write(
|
||||
varray.final_id);
|
||||
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
|
||||
for (const int i : IndexRange(varray.size())) {
|
||||
varray.get(i, buffer);
|
||||
write_attribute.varray->set_by_relocate(i, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static blender::bke::OutputAttribute create_output_attribute_anonymous(
|
||||
GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const bool ignore_old_values,
|
||||
const void *default_value)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::fn;
|
||||
using namespace blender::bke;
|
||||
|
||||
const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
|
||||
const int domain_size = component.attribute_domain_size(domain);
|
||||
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_anonymous_for_write(id);
|
||||
if (!attribute) {
|
||||
if (default_value) {
|
||||
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
|
||||
component.attribute_try_create_anonymous(
|
||||
id, domain, data_type, AttributeInitVArray(&default_varray));
|
||||
}
|
||||
else {
|
||||
component.attribute_try_create_anonymous(id, domain, data_type, AttributeInitDefault());
|
||||
}
|
||||
|
||||
attribute = component.attribute_try_get_anonymous_for_write(id);
|
||||
if (!attribute) {
|
||||
/* Can't create the attribute. */
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
|
||||
/* Existing generic attribute matches exactly. */
|
||||
return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
|
||||
}
|
||||
|
||||
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
|
||||
* attribute after processing is done. */
|
||||
void *data = MEM_mallocN_aligned(
|
||||
cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
|
||||
if (ignore_old_values) {
|
||||
/* This does nothing for trivially constructible types, but is necessary for correctness. */
|
||||
cpp_type->default_construct_n(data, domain);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
/* Fill the temporary array with values from the existing attribute. */
|
||||
GVArrayPtr old_varray = component.attribute_try_get_anonymous_for_read(
|
||||
id, domain, data_type, default_value);
|
||||
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
|
||||
}
|
||||
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_AnonymousOutputAttribute>(
|
||||
GMutableSpan{*cpp_type, data, domain_size}, component, id);
|
||||
|
||||
return OutputAttribute(std::move(varray), domain, save_output_anonymous_attribute, true);
|
||||
}
|
||||
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const void *default_value)
|
||||
{
|
||||
return create_output_attribute_anonymous(*this, id, domain, data_type, false, default_value);
|
||||
}
|
||||
|
||||
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output_only(
|
||||
const AnonymousCustomDataLayerID &id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type)
|
||||
{
|
||||
return create_output_attribute_anonymous(*this, id, domain, data_type, true, nullptr);
|
||||
}
|
||||
|
@@ -130,6 +130,30 @@ class DynamicAttributesProvider {
|
||||
return false;
|
||||
};
|
||||
|
||||
/** Returns the id of the new anonymous or null if no new attribute was created. */
|
||||
virtual bool try_create_anonymous(GeometryComponent &UNUSED(component),
|
||||
const AnonymousCustomDataLayerID &UNUSED(layer_id),
|
||||
const AttributeDomain UNUSED(domain),
|
||||
const CustomDataType UNUSED(data_type),
|
||||
const AttributeInit &UNUSED(initializer)) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ReadAttributeLookup try_get_anonymous_for_read(
|
||||
const GeometryComponent &UNUSED(component),
|
||||
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual WriteAttributeLookup try_get_anonymous_for_write(
|
||||
GeometryComponent &UNUSED(component),
|
||||
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const = 0;
|
||||
virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
|
||||
@@ -167,6 +191,18 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const final;
|
||||
|
||||
bool try_create_anonymous(GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &layer_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const final;
|
||||
|
||||
ReadAttributeLookup try_get_anonymous_for_read(
|
||||
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
|
||||
|
||||
WriteAttributeLookup try_get_anonymous_for_write(
|
||||
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final;
|
||||
|
||||
@@ -176,6 +212,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
}
|
||||
|
||||
private:
|
||||
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const;
|
||||
|
||||
template<typename T>
|
||||
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
@@ -185,6 +224,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
domain_};
|
||||
}
|
||||
|
||||
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
|
||||
const int domain_size) const;
|
||||
|
||||
template<typename T>
|
||||
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
|
||||
const int domain_size) const
|
||||
|
@@ -1254,11 +1254,10 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
|
||||
bool in_cache = bvhcache_find(
|
||||
bvh_cache_p, bvh_cache_type, &tree, &lock_started, mesh_eval_mutex);
|
||||
BVHCache *bvh_cache = *bvh_cache_p;
|
||||
bvhtree_balance(tree, true);
|
||||
|
||||
if (in_cache == false) {
|
||||
tree = bvhtree_from_editmesh_looptri_create_tree(
|
||||
epsilon, tree_type, axis, em, looptri_mask, looptri_num_active);
|
||||
bvhtree_balance(tree, true);
|
||||
|
||||
/* Save on cache for later use */
|
||||
// printf("BVHTree built and saved on cache\n");
|
||||
|
@@ -339,7 +339,11 @@ void CurveEval::assert_valid_point_attributes() const
|
||||
name,
|
||||
[&](AttributeMetaData *map_data) {
|
||||
/* All unique attribute names should be added on the first spline. */
|
||||
BLI_assert(spline == splines_.first());
|
||||
/* TODO(Hans/Jacques): This check seems very bad, anonymous attributes with have
|
||||
* different names on different splines. */
|
||||
if (meta_data.anonymous_layer_id == nullptr) {
|
||||
BLI_assert(spline == splines_.first());
|
||||
}
|
||||
*map_data = meta_data;
|
||||
},
|
||||
[&](AttributeMetaData *map_data) {
|
||||
|
@@ -57,6 +57,8 @@
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
@@ -2127,6 +2129,9 @@ bool CustomData_merge(const struct CustomData *source,
|
||||
if (flag & CD_FLAG_NOCOPY) {
|
||||
continue;
|
||||
}
|
||||
if (CustomData_layer_is_unused_anonymous(layer)) {
|
||||
continue;
|
||||
}
|
||||
if (!(mask & CD_TYPE_AS_MASK(type))) {
|
||||
continue;
|
||||
}
|
||||
@@ -2164,8 +2169,13 @@ bool CustomData_merge(const struct CustomData *source,
|
||||
newlayer->active_rnd = lastrender;
|
||||
newlayer->active_clone = lastclone;
|
||||
newlayer->active_mask = lastmask;
|
||||
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
|
||||
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY | CD_FLAG_ANONYMOUS);
|
||||
changed = true;
|
||||
|
||||
if (layer->flag & CD_FLAG_ANONYMOUS) {
|
||||
CustomData_anonymous_id_weak_increment(layer->anonymous_id);
|
||||
newlayer->anonymous_id = layer->anonymous_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2206,6 +2216,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
|
||||
{
|
||||
const LayerTypeInfo *typeInfo;
|
||||
|
||||
if (layer->flag & CD_FLAG_ANONYMOUS) {
|
||||
CustomData_anonymous_id_weak_decrement(layer->anonymous_id);
|
||||
layer->anonymous_id = NULL;
|
||||
}
|
||||
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
|
||||
typeInfo = layerType_getInfo(layer->type);
|
||||
|
||||
@@ -4244,7 +4258,8 @@ void CustomData_blend_write_prepare(CustomData *data,
|
||||
|
||||
for (i = 0, j = 0; i < totlayer; i++) {
|
||||
CustomDataLayer *layer = &data->layers[i];
|
||||
if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
|
||||
/* Layers with this flag set are not written to file. */
|
||||
if (layer->flag & (CD_FLAG_NOCOPY | CD_FLAG_ANONYMOUS)) {
|
||||
data->totlayer--;
|
||||
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
|
||||
}
|
||||
@@ -5031,6 +5046,68 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
|
||||
MEM_SAFE_FREE(tmp_data_src);
|
||||
}
|
||||
|
||||
AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name)
|
||||
{
|
||||
AnonymousCustomDataLayerID *layer_id = MEM_callocN(sizeof(AnonymousCustomDataLayerID), __func__);
|
||||
layer_id->debug_name = BLI_strdup(debug_name);
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
static void CustomData_anonymous_id_free(AnonymousCustomDataLayerID *layer_id)
|
||||
{
|
||||
BLI_assert(layer_id->strong_references == 0);
|
||||
BLI_assert(layer_id->tot_references == 0);
|
||||
MEM_freeN(layer_id->debug_name);
|
||||
MEM_freeN(layer_id);
|
||||
}
|
||||
|
||||
void CustomData_anonymous_id_strong_decrement(const AnonymousCustomDataLayerID *layer_id)
|
||||
{
|
||||
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
|
||||
int strong_references = atomic_sub_and_fetch_int32(&mutable_layer_id->strong_references, 1);
|
||||
BLI_assert(strong_references >= 0);
|
||||
UNUSED_VARS_NDEBUG(strong_references);
|
||||
|
||||
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
|
||||
BLI_assert(tot_references >= 0);
|
||||
if (tot_references == 0) {
|
||||
CustomData_anonymous_id_free(mutable_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomData_anonymous_id_strong_increment(const AnonymousCustomDataLayerID *layer_id)
|
||||
{
|
||||
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
|
||||
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
|
||||
atomic_add_and_fetch_int32(&mutable_layer_id->strong_references, 1);
|
||||
}
|
||||
|
||||
void CustomData_anonymous_id_weak_decrement(const AnonymousCustomDataLayerID *layer_id)
|
||||
{
|
||||
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
|
||||
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
|
||||
BLI_assert(tot_references >= 0);
|
||||
if (tot_references == 0) {
|
||||
CustomData_anonymous_id_free(mutable_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
void CustomData_anonymous_id_weak_increment(const AnonymousCustomDataLayerID *layer_id)
|
||||
{
|
||||
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
|
||||
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
|
||||
}
|
||||
|
||||
bool CustomData_layer_is_unused_anonymous(const CustomDataLayer *layer)
|
||||
{
|
||||
if (layer->flag & CD_FLAG_ANONYMOUS) {
|
||||
if (layer->anonymous_id->strong_references == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
|
||||
{
|
||||
if (mdlist) {
|
||||
|
@@ -1094,6 +1094,165 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_create_anonymous(GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &layer_id,
|
||||
const AttributeDomain domain,
|
||||
const CustomDataType data_type,
|
||||
const AttributeInit &initializer) const
|
||||
{
|
||||
BLI_assert(this->type_is_supported(data_type));
|
||||
if (domain != ATTR_DOMAIN_POINT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableSpan<SplinePtr> splines = curve->splines();
|
||||
|
||||
/* Otherwise just create a custom data layer on each of the splines. */
|
||||
for (const int i : splines.index_range()) {
|
||||
if (!splines[i]->attributes.create_anonymous(layer_id, data_type)) {
|
||||
/* If attribute creation fails on one of the splines, we cannot leave the custom data
|
||||
* layers in the previous splines around, so delete them before returning. However,
|
||||
* this is not an expected case. */
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* With a default initializer type, we can keep the values at their initial values. */
|
||||
if (initializer.type == AttributeInit::Type::Default) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WriteAttributeLookup write_attribute = this->try_get_anonymous_for_write(component, layer_id);
|
||||
/* We just created the attribute, it should exist. */
|
||||
BLI_assert(write_attribute);
|
||||
|
||||
const int total_size = curve->control_point_offsets().last();
|
||||
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
|
||||
/* TODO: When we can call a variant of #set_all with a virtual array argument,
|
||||
* this theoretically unnecessary materialize step could be removed. */
|
||||
GVArray_GSpan source_varray_span{*source_varray};
|
||||
write_attribute.varray->set_all(source_varray_span.data());
|
||||
|
||||
if (initializer.type == AttributeInit::Type::MoveArray) {
|
||||
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
|
||||
}
|
||||
|
||||
curve->assert_valid_point_attributes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadAttributeLookup try_get_anonymous_for_read(const GeometryComponent &component,
|
||||
const AnonymousCustomDataLayerID &layer_id) const
|
||||
{
|
||||
const CurveEval *curve = get_curve_from_component_for_read(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Span<SplinePtr> splines = curve->splines();
|
||||
Vector<GSpan> spans; /* GSpan has no default constructor. */
|
||||
spans.reserve(splines.size());
|
||||
std::optional<GSpan> first_span = splines[0]->attributes.get_anonymous_for_read(layer_id);
|
||||
if (!first_span) {
|
||||
return {};
|
||||
}
|
||||
spans.append(*first_span);
|
||||
for (const int i : IndexRange(1, splines.size() - 1)) {
|
||||
std::optional<GSpan> span = splines[i]->attributes.get_anonymous_for_read(layer_id);
|
||||
if (!span) {
|
||||
/* All splines should have the same set of data layers. It would be possible to recover
|
||||
* here and return partial data instead, but that would add a lot of complexity for a
|
||||
* situation we don't even expect to encounter. */
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
if (span->type() != spans.last().type()) {
|
||||
/* Data layer types on separate splines do not match. */
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
spans.append(*span);
|
||||
}
|
||||
|
||||
/* First check for the simpler situation when we can return a simpler span virtual array. */
|
||||
if (spans.size() == 1) {
|
||||
return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
ReadAttributeLookup attribute = {};
|
||||
Array<int> offsets = curve->control_point_offsets();
|
||||
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
Array<Span<T>> data(splines.size());
|
||||
for (const int i : splines.index_range()) {
|
||||
data[i] = spans[i].typed<T>();
|
||||
BLI_assert(data[i].data() != nullptr);
|
||||
}
|
||||
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
WriteAttributeLookup try_get_anonymous_for_write(
|
||||
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
|
||||
{
|
||||
CurveEval *curve = get_curve_from_component_for_write(component);
|
||||
if (curve == nullptr || curve->splines().size() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
MutableSpan<SplinePtr> splines = curve->splines();
|
||||
Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
|
||||
spans.reserve(splines.size());
|
||||
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_anonymous_for_write(
|
||||
layer_id);
|
||||
if (!first_span) {
|
||||
return {};
|
||||
}
|
||||
spans.append(*first_span);
|
||||
for (const int i : IndexRange(1, splines.size() - 1)) {
|
||||
std::optional<GMutableSpan> span = splines[i]->attributes.get_anonymous_for_write(layer_id);
|
||||
if (!span) {
|
||||
/* All splines should have the same set of data layers. It would be possible to recover
|
||||
* here and return partial data instead, but that would add a lot of complexity for a
|
||||
* situation we don't even expect to encounter. */
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
if (span->type() != spans.last().type()) {
|
||||
/* Data layer types on separate splines do not match. */
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
spans.append(*span);
|
||||
}
|
||||
|
||||
/* First check for the simpler situation when we can return a simpler span virtual array. */
|
||||
if (spans.size() == 1) {
|
||||
return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
|
||||
}
|
||||
|
||||
WriteAttributeLookup attribute = {};
|
||||
Array<int> offsets = curve->control_point_offsets();
|
||||
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
Array<MutableSpan<T>> data(splines.size());
|
||||
for (const int i : splines.index_range()) {
|
||||
data[i] = spans[i].typed<T>();
|
||||
BLI_assert(data[i].data() != nullptr);
|
||||
}
|
||||
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
|
||||
});
|
||||
return attribute;
|
||||
}
|
||||
|
||||
bool foreach_attribute(const GeometryComponent &component,
|
||||
const AttributeForeachCallback callback) const final
|
||||
{
|
||||
|
@@ -651,6 +651,207 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace blender::bke::adapt_selection_domain {
|
||||
static VArrayPtr<bool> varray_from_array(Array<bool> array)
|
||||
{
|
||||
return std::make_unique<VArray_For_ArrayContainer<Array<bool>>>(std::move(array));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_point_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totpoly);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
bool poly_is_selected = true;
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
if (!selection->get(loop.v)) {
|
||||
poly_is_selected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_selection[poly_index] = poly_is_selected;
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_point_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totloop);
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
new_selection[loop_index] = selection->get(loop.v);
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_point_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totedge);
|
||||
for (const int edge_index : IndexRange(mesh.totedge)) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
const bool edge_is_selected = selection->get(edge.v1) && selection->get(edge.v2);
|
||||
new_selection[edge_index] = edge_is_selected;
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_edge_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totvert, false);
|
||||
for (const int edge_index : IndexRange(mesh.totedge)) {
|
||||
if (selection->get(edge_index)) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
new_selection[edge.v1] = true;
|
||||
new_selection[edge.v2] = true;
|
||||
}
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_edge_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totpoly);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
bool poly_is_selected = true;
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
if (!selection->get(loop.e)) {
|
||||
poly_is_selected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_selection[poly_index] = poly_is_selected;
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_face_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totvert, false);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
if (selection->get(poly_index)) {
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
BLI_assert(loop.v < mesh.totvert);
|
||||
new_selection[loop.v] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_face_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totedge, false);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
if (selection->get(poly_index)) {
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
new_selection[loop.e] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
static VArrayPtr<bool> adapt_selection_face_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
|
||||
{
|
||||
Array<bool> new_selection(mesh.totloop);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
const bool is_selected = selection->get(poly_index);
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
new_selection[loop_index] = is_selected;
|
||||
}
|
||||
}
|
||||
return varray_from_array(std::move(new_selection));
|
||||
}
|
||||
|
||||
} // namespace blender::bke::adapt_selection_domain
|
||||
|
||||
blender::VArrayPtr<bool> MeshComponent::adapt_selection(blender::VArrayPtr<bool> selection,
|
||||
const AttributeDomain from_domain,
|
||||
const AttributeDomain to_domain) const
|
||||
{
|
||||
using namespace blender::bke::adapt_selection_domain;
|
||||
|
||||
const int from_domain_size = this->attribute_domain_size(from_domain);
|
||||
BLI_assert(selection->size() == from_domain_size);
|
||||
|
||||
if (from_domain == to_domain) {
|
||||
return selection;
|
||||
}
|
||||
if (from_domain_size == 0) {
|
||||
return selection;
|
||||
}
|
||||
if (selection->is_single()) {
|
||||
return selection;
|
||||
}
|
||||
|
||||
switch (from_domain) {
|
||||
case ATTR_DOMAIN_CORNER: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
break;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return adapt_selection_point_to_corner(*mesh_, std::move(selection));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return adapt_selection_point_to_face(*mesh_, std::move(selection));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return adapt_selection_point_to_edge(*mesh_, std::move(selection));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_FACE: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return adapt_selection_face_to_point(*mesh_, std::move(selection));
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return adapt_selection_face_to_corner(*mesh_, std::move(selection));
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return adapt_selection_face_to_edge(*mesh_, std::move(selection));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_EDGE: {
|
||||
switch (to_domain) {
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
break;
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return adapt_selection_edge_to_point(*mesh_, std::move(selection));
|
||||
case ATTR_DOMAIN_FACE:
|
||||
return adapt_selection_edge_to_face(*mesh_, std::move(selection));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
|
||||
{
|
||||
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_field.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.h"
|
||||
|
@@ -620,7 +620,10 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
|
||||
}
|
||||
|
||||
if (new_count == 1) {
|
||||
BKE_gpencil_free_stroke_weights(gps);
|
||||
if (gps->dvert) {
|
||||
BKE_gpencil_free_stroke_weights(gps);
|
||||
MEM_freeN(gps->dvert);
|
||||
}
|
||||
MEM_freeN(gps->points);
|
||||
gps->points = nullptr;
|
||||
gps->dvert = nullptr;
|
||||
@@ -628,27 +631,24 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
|
||||
return false;
|
||||
}
|
||||
|
||||
new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
|
||||
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
|
||||
}
|
||||
new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
|
||||
memcpy(new_pt, &pt[index_from], sizeof(bGPDspoint) * new_count);
|
||||
|
||||
if (gps->dvert) {
|
||||
new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
|
||||
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
|
||||
"gp_stroke_dverts_trimmed");
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
dv = &gps->dvert[i + index_from];
|
||||
new_dv[i].flag = dv->flag;
|
||||
new_dv[i].totweight = dv->totweight;
|
||||
new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
|
||||
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
|
||||
"gp_stroke_dverts_dw_trimmed");
|
||||
for (int j = 0; j < dv->totweight; j++) {
|
||||
new_dv[i].dw[j].weight = dv->dw[j].weight;
|
||||
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
|
||||
}
|
||||
BKE_defvert_clear(dv);
|
||||
}
|
||||
BKE_gpencil_free_stroke_weights(gps);
|
||||
MEM_freeN(gps->dvert);
|
||||
gps->dvert = new_dv;
|
||||
}
|
||||
@@ -692,25 +692,21 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
|
||||
gpf, gps, gps->mat_nr, new_count, gps->thickness);
|
||||
|
||||
new_pt = new_gps->points; /* Allocated from above. */
|
||||
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
|
||||
}
|
||||
memcpy(new_pt, &pt[before_index], sizeof(bGPDspoint) * new_count);
|
||||
|
||||
if (gps->dvert) {
|
||||
new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
|
||||
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
|
||||
"gp_stroke_dverts_remaining(MDeformVert)");
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
dv = &gps->dvert[i + before_index];
|
||||
new_dv[i].flag = dv->flag;
|
||||
new_dv[i].totweight = dv->totweight;
|
||||
new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
|
||||
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
|
||||
"gp_stroke_dverts_dw_remaining(MDeformWeight)");
|
||||
for (int j = 0; j < dv->totweight; j++) {
|
||||
new_dv[i].dw[j].weight = dv->dw[j].weight;
|
||||
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
|
||||
}
|
||||
BKE_defvert_clear(dv);
|
||||
}
|
||||
new_gps->dvert = new_dv;
|
||||
}
|
||||
|
@@ -1481,7 +1481,7 @@ static bool id_name_final_build(char *name, char *base_name, size_t base_name_le
|
||||
|
||||
/* Code above may have generated invalid utf-8 string, due to raw truncation.
|
||||
* Ensure we get a valid one now. */
|
||||
base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len);
|
||||
base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len);
|
||||
|
||||
/* Also truncate orig name, and start the whole check again. */
|
||||
name[base_name_len] = '\0';
|
||||
@@ -1731,7 +1731,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo
|
||||
else {
|
||||
/* disallow non utf8 chars,
|
||||
* the interface checks for this but new ID's based on file names don't */
|
||||
BLI_utf8_invalid_strip(name, strlen(name));
|
||||
BLI_str_utf8_invalid_strip(name, strlen(name));
|
||||
}
|
||||
|
||||
ID *id_sorting_hint = NULL;
|
||||
|
@@ -5115,13 +5115,16 @@ static void registerGeometryNodes()
|
||||
{
|
||||
register_node_type_geo_group();
|
||||
|
||||
register_node_type_geo_align_rotation_to_vector();
|
||||
register_node_type_geo_extrude();
|
||||
|
||||
register_node_type_geo_attribute();
|
||||
register_node_type_geo_attribute_clamp();
|
||||
register_node_type_geo_attribute_color_ramp();
|
||||
register_node_type_geo_attribute_combine_xyz();
|
||||
register_node_type_geo_attribute_compare();
|
||||
register_node_type_geo_attribute_convert();
|
||||
register_node_type_geo_attribute_curve_map();
|
||||
register_node_type_geo_attribute_extract();
|
||||
register_node_type_geo_attribute_fill();
|
||||
register_node_type_geo_attribute_map_range();
|
||||
register_node_type_geo_attribute_math();
|
||||
@@ -5139,6 +5142,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_convex_hull();
|
||||
register_node_type_geo_curve_endpoints();
|
||||
register_node_type_geo_curve_length();
|
||||
register_node_type_geo_curve_parameter();
|
||||
register_node_type_geo_curve_primitive_bezier_segment();
|
||||
register_node_type_geo_curve_primitive_circle();
|
||||
register_node_type_geo_curve_primitive_line();
|
||||
@@ -5156,6 +5160,9 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_curve_trim();
|
||||
register_node_type_geo_delete_geometry();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_evaluate_curve();
|
||||
register_node_type_geo_extrude_and_move();
|
||||
register_node_type_geo_index();
|
||||
register_node_type_geo_input_material();
|
||||
register_node_type_geo_is_viewport();
|
||||
register_node_type_geo_join_geometry();
|
||||
@@ -5171,6 +5178,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_mesh_primitive_uv_sphere();
|
||||
register_node_type_geo_mesh_subdivide();
|
||||
register_node_type_geo_mesh_to_curve();
|
||||
register_node_type_geo_normal();
|
||||
register_node_type_geo_object_info();
|
||||
register_node_type_geo_point_distribute();
|
||||
register_node_type_geo_point_instance();
|
||||
@@ -5179,17 +5187,21 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_point_separate();
|
||||
register_node_type_geo_point_translate();
|
||||
register_node_type_geo_points_to_volume();
|
||||
register_node_type_geo_position();
|
||||
register_node_type_geo_raycast();
|
||||
register_node_type_geo_sample_mesh_surface();
|
||||
register_node_type_geo_sample_texture();
|
||||
register_node_type_geo_select_by_handle_type();
|
||||
register_node_type_geo_select_by_material();
|
||||
register_node_type_geo_separate_components();
|
||||
register_node_type_geo_set_position();
|
||||
register_node_type_geo_subdivision_surface();
|
||||
register_node_type_geo_switch();
|
||||
register_node_type_geo_transform();
|
||||
register_node_type_geo_triangulate();
|
||||
register_node_type_geo_viewer();
|
||||
register_node_type_geo_volume_to_mesh();
|
||||
register_node_type_geo_attribute_freeze();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes()
|
||||
@@ -5199,6 +5211,7 @@ static void registerFunctionNodes()
|
||||
register_node_type_fn_float_to_int();
|
||||
register_node_type_fn_input_string();
|
||||
register_node_type_fn_input_vector();
|
||||
register_node_type_fn_align_rotation_to_vector();
|
||||
register_node_type_fn_random_float();
|
||||
}
|
||||
|
||||
|
@@ -123,7 +123,7 @@ int Spline::evaluated_edges_size() const
|
||||
float Spline::length() const
|
||||
{
|
||||
Span<float> lengths = this->evaluated_lengths();
|
||||
return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
|
||||
return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
|
||||
}
|
||||
|
||||
int Spline::segments_size() const
|
||||
|
@@ -308,7 +308,7 @@ int txt_extended_ascii_as_utf8(char **str)
|
||||
int added = 0;
|
||||
|
||||
while ((*str)[i]) {
|
||||
if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
|
||||
if ((bad_char = BLI_str_utf8_invalid_byte(*str + i, length - i)) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ int txt_extended_ascii_as_utf8(char **str)
|
||||
i = 0;
|
||||
|
||||
while ((*str)[i]) {
|
||||
if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
|
||||
if ((bad_char = BLI_str_utf8_invalid_byte((*str) + i, length - i)) == -1) {
|
||||
memcpy(newstr + mi, (*str) + i, length - i + 1);
|
||||
break;
|
||||
}
|
||||
|
@@ -250,6 +250,20 @@ template<typename T> struct DefaultHash<std::unique_ptr<T>> {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct DefaultHash<std::shared_ptr<T>> {
|
||||
uint64_t operator()(const std::shared_ptr<T> &value) const
|
||||
{
|
||||
return get_default_hash(value.get());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct DefaultHash<std::reference_wrapper<T>> {
|
||||
uint64_t operator()(const std::reference_wrapper<T> &value) const
|
||||
{
|
||||
return get_default_hash(value.get());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
|
||||
uint64_t operator()(const std::pair<T1, T2> &value) const
|
||||
{
|
||||
|
@@ -196,6 +196,8 @@ MINLINE unsigned int log2_ceil_u(unsigned int x);
|
||||
MINLINE int divide_round_i(int a, int b);
|
||||
MINLINE int mod_i(int i, int n);
|
||||
|
||||
MINLINE float round_to_even(float f);
|
||||
|
||||
MINLINE signed char round_fl_to_char(float a);
|
||||
MINLINE unsigned char round_fl_to_uchar(float a);
|
||||
MINLINE short round_fl_to_short(float a);
|
||||
|
88
source/blender/blenlib/BLI_optional_ptr.hh
Normal file
88
source/blender/blenlib/BLI_optional_ptr.hh
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
template<typename T, typename OwnedTPtr = std::unique_ptr<T>> class optional_ptr {
|
||||
private:
|
||||
OwnedTPtr owned_ptr_;
|
||||
T *ptr_ = nullptr;
|
||||
|
||||
public:
|
||||
optional_ptr() = default;
|
||||
|
||||
optional_ptr(T &ptr) : ptr_(&ptr)
|
||||
{
|
||||
}
|
||||
|
||||
optional_ptr(OwnedTPtr owned_ptr) : owned_ptr_(std::move(owned_ptr)), ptr_(&*owned_ptr_)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_owned() const
|
||||
{
|
||||
return static_cast<bool>(owned_ptr_);
|
||||
}
|
||||
|
||||
OwnedTPtr extract_owned()
|
||||
{
|
||||
OwnedTPtr ptr = std::move(owned_ptr_);
|
||||
owned_ptr_ = OwnedTPtr{nullptr};
|
||||
ptr_ = nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
T *operator->()
|
||||
{
|
||||
BLI_assert(ptr_ != nullptr);
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
const T *operator->() const
|
||||
{
|
||||
BLI_assert(ptr_ != nullptr);
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
T &operator*()
|
||||
{
|
||||
BLI_assert(ptr_ != nullptr);
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
const T &operator*() const
|
||||
{
|
||||
BLI_assert(ptr_ != nullptr);
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return ptr_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
@@ -478,8 +478,8 @@ template<typename T> class MutableSpan {
|
||||
using size_type = int64_t;
|
||||
|
||||
protected:
|
||||
T *data_;
|
||||
int64_t size_;
|
||||
T *data_ = nullptr;
|
||||
int64_t size_ = 0;
|
||||
|
||||
public:
|
||||
constexpr MutableSpan() = default;
|
||||
|
@@ -31,8 +31,8 @@ char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t
|
||||
ATTR_NONNULL();
|
||||
size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy)
|
||||
ATTR_NONNULL();
|
||||
ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
|
||||
int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
|
||||
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
|
||||
int BLI_str_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
|
||||
|
||||
/* warning, can return -1 on bad chars */
|
||||
int BLI_str_utf8_size(const char *p) ATTR_NONNULL();
|
||||
|
@@ -84,12 +84,24 @@ template<typename T> class UserCounter {
|
||||
return data_;
|
||||
}
|
||||
|
||||
const T *operator->() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return data_;
|
||||
}
|
||||
|
||||
T &operator*()
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return *data_;
|
||||
}
|
||||
|
||||
const T &operator*() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return *data_;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
|
@@ -255,7 +255,7 @@ template<typename T> class VMutableArray : public VArray<T> {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
|
||||
template<typename T> using VArrayPtr = std::unique_ptr<const VArray<T>>;
|
||||
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
|
||||
|
||||
/**
|
||||
|
@@ -302,7 +302,7 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
|
||||
/* Get the name. */
|
||||
if (face->family_name) {
|
||||
BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name);
|
||||
BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name));
|
||||
BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name));
|
||||
}
|
||||
|
||||
/* Select a character map. */
|
||||
|
@@ -363,6 +363,14 @@ MINLINE signed char round_db_to_char_clamp(double a){
|
||||
#undef _round_clamp_fl_impl
|
||||
#undef _round_clamp_db_impl
|
||||
|
||||
/**
|
||||
* Round to closest even number, halfway cases are rounded away from zero.
|
||||
*/
|
||||
MINLINE float round_to_even(float f)
|
||||
{
|
||||
return roundf(f * 0.5f) * 2.0f;
|
||||
}
|
||||
|
||||
/* integer division that rounds 0.5 up, particularly useful for color blending
|
||||
* with integers, to avoid gradual darkening when rounding down */
|
||||
MINLINE int divide_round_i(int a, int b)
|
||||
|
@@ -70,7 +70,7 @@ static const size_t utf8_skip_data[256] = {
|
||||
*
|
||||
* \return the offset of the first invalid byte.
|
||||
*/
|
||||
ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length)
|
||||
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
|
||||
{
|
||||
const unsigned char *p, *perr, *pend = (const unsigned char *)str + length;
|
||||
unsigned char c;
|
||||
@@ -200,14 +200,14 @@ utf8_error:
|
||||
*
|
||||
* \return number of stripped bytes.
|
||||
*/
|
||||
int BLI_utf8_invalid_strip(char *str, size_t length)
|
||||
int BLI_str_utf8_invalid_strip(char *str, size_t length)
|
||||
{
|
||||
ptrdiff_t bad_char;
|
||||
int tot = 0;
|
||||
|
||||
BLI_assert(str[length] == '\0');
|
||||
|
||||
while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) {
|
||||
while ((bad_char = BLI_str_utf8_invalid_byte(str, length)) != -1) {
|
||||
str += bad_char;
|
||||
length -= (size_t)(bad_char + 1);
|
||||
|
||||
|
@@ -653,6 +653,18 @@ TEST(map, LookupKey)
|
||||
EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
|
||||
}
|
||||
|
||||
TEST(map, ReferenceWrapperKey)
|
||||
{
|
||||
Map<std::reference_wrapper<int>, int> map;
|
||||
int a = 2;
|
||||
int b = 5;
|
||||
map.add(a, 10);
|
||||
map.add(a, 20);
|
||||
map.add(b, 20);
|
||||
EXPECT_EQ(map.lookup(a), 10);
|
||||
EXPECT_EQ(map.lookup(b), 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
|
||||
*/
|
||||
|
@@ -266,7 +266,7 @@ static const char *utf8_invalid_tests[][3] = {
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */
|
||||
/* BLI_str_utf8_invalid_strip (and indirectly, BLI_str_utf8_invalid_byte). */
|
||||
TEST(string, Utf8InvalidBytes)
|
||||
{
|
||||
for (int i = 0; utf8_invalid_tests[i][0] != nullptr; i++) {
|
||||
@@ -277,7 +277,7 @@ TEST(string, Utf8InvalidBytes)
|
||||
char buff[80];
|
||||
memcpy(buff, tst, sizeof(buff));
|
||||
|
||||
const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1);
|
||||
const int num_errors_found = BLI_str_utf8_invalid_strip(buff, sizeof(buff) - 1);
|
||||
|
||||
printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff);
|
||||
EXPECT_EQ(num_errors_found, num_errors);
|
||||
|
@@ -1446,4 +1446,141 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to select bmesh vertex data based on an array of bool.
|
||||
*/
|
||||
void BM_select_vertices(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
int i = 0;
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
if (mask[i]) {
|
||||
BM_elem_flag_set(v, BM_ELEM_SELECT, true);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_set(v, BM_ELEM_SELECT, false);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to select bmesh edge data based on an array of bool.
|
||||
*/
|
||||
void BM_select_edges(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
int i = 0;
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (mask[i]) {
|
||||
BM_elem_flag_set(e, BM_ELEM_SELECT, true);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_set(e, BM_ELEM_SELECT, false);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to select bmesh face data based on an array of bool.
|
||||
*/
|
||||
void BM_select_faces(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_tag_vertices(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to temporary tag bmesh edge data based on an array of bool.
|
||||
*/
|
||||
void BM_tag_edges(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
|
||||
BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to temporary tag bmesh face data based on an array of bool.
|
||||
*/
|
||||
void BM_tag_faces(BMesh *bm, const bool *mask)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_get_tagged_faces(BMesh *bm, bool *selection)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
selection[i] = BM_elem_flag_test(f, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator)
|
||||
{
|
||||
BMOIter iter;
|
||||
BMFace *f;
|
||||
BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
|
||||
BMO_ITER (f, &iter, b_mesh_operator->slots_out, "faces.out", BM_FACE) {
|
||||
BM_elem_flag_enable(f, BM_ELEM_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_get_selected_faces(BMesh *bm, bool *selection)
|
||||
{
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
|
||||
selection[i] = BM_elem_flag_test(f, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_get_selected_edges(BMesh *bm, bool *selection)
|
||||
{
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
|
||||
selection[i] = BM_elem_flag_test(e, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_get_selected_vertices(BMesh *bm, bool *selection)
|
||||
{
|
||||
BMIter iter;
|
||||
BMVert *v;
|
||||
int i = 0;
|
||||
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
selection[i] = BM_elem_flag_test(v, BM_ELEM_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -134,3 +134,16 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
|
||||
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
|
||||
const float (*vert_coords)[3],
|
||||
const float mat[4][4]);
|
||||
|
||||
void BM_select_vertices(BMesh *bm, const bool *mask);
|
||||
void BM_select_edges(BMesh *bm, const bool *mask);
|
||||
void BM_select_faces(BMesh *bm, const bool *mask);
|
||||
void BM_tag_vertices(BMesh *bm, const bool *mask);
|
||||
void BM_tag_edges(BMesh *bm, const bool *mask);
|
||||
void BM_tag_faces(BMesh *bm, const bool *mask);
|
||||
|
||||
void BM_get_tagged_faces(BMesh *bm, bool *selection);
|
||||
void BM_get_selected_faces(BMesh *bm, bool *selection);
|
||||
void BM_get_selected_edges(BMesh *bm, bool *selection);
|
||||
void BM_get_selected_vertices(BMesh *bm, bool *selection);
|
||||
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator);
|
@@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = {
|
||||
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
|
||||
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
|
||||
{"depth", BMO_OP_SLOT_FLT}, /* depth */
|
||||
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
|
||||
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
|
||||
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
|
||||
{"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */
|
||||
{"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */
|
||||
{"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */
|
||||
@@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = {
|
||||
{"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */
|
||||
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
|
||||
{"depth", BMO_OP_SLOT_FLT}, /* depth */
|
||||
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
|
||||
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
|
||||
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
|
||||
{"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */
|
||||
{{'\0'}},
|
||||
},
|
||||
@@ -1940,7 +1946,6 @@ static BMOpDefine bmo_inset_region_def = {
|
||||
(BMO_OPTYPE_FLAG_NORMALS_CALC |
|
||||
BMO_OPTYPE_FLAG_SELECT_FLUSH),
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge-loop Offset.
|
||||
*
|
||||
|
@@ -471,6 +471,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
|
||||
for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e;
|
||||
e = BMO_iter_step(&siter)) {
|
||||
BMVert *f_verts[4];
|
||||
|
||||
#ifdef USE_EDGE_REGION_FLAGS
|
||||
BMEdge *f_edges[4];
|
||||
#endif
|
||||
|
@@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
|
||||
BMOIter oiter;
|
||||
MemArena *interp_arena = NULL;
|
||||
|
||||
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
|
||||
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
|
||||
const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
|
||||
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
|
||||
const float depth = BMO_slot_float_get(op->slots_in, "depth");
|
||||
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
|
||||
@@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
|
||||
if (use_interpolate) {
|
||||
interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
}
|
||||
int i = 0;
|
||||
if (use_attributes) {
|
||||
BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
|
||||
bmo_face_inset_individual(bm,
|
||||
f,
|
||||
interp_arena,
|
||||
thickness_array[i],
|
||||
depth_array[i],
|
||||
use_even_offset,
|
||||
use_relative_offset,
|
||||
use_interpolate);
|
||||
|
||||
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
|
||||
bmo_face_inset_individual(bm,
|
||||
f,
|
||||
interp_arena,
|
||||
thickness,
|
||||
depth,
|
||||
use_even_offset,
|
||||
use_relative_offset,
|
||||
use_interpolate);
|
||||
if (use_interpolate) {
|
||||
BLI_memarena_clear(interp_arena);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
|
||||
bmo_face_inset_individual(bm,
|
||||
f,
|
||||
interp_arena,
|
||||
thickness,
|
||||
depth,
|
||||
use_even_offset,
|
||||
use_relative_offset,
|
||||
use_interpolate);
|
||||
|
||||
if (use_interpolate) {
|
||||
BLI_memarena_clear(interp_arena);
|
||||
if (use_interpolate) {
|
||||
BLI_memarena_clear(interp_arena);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,12 +698,16 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
|
||||
const bool use_outset = BMO_slot_bool_get(op->slots_in, "use_outset");
|
||||
const bool use_boundary = BMO_slot_bool_get(op->slots_in, "use_boundary") &&
|
||||
(use_outset == false);
|
||||
|
||||
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
|
||||
const bool use_even_boundary = use_even_offset; /* could make own option */
|
||||
const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
|
||||
const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
|
||||
const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
|
||||
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
|
||||
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
|
||||
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
|
||||
// const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
|
||||
const float depth = BMO_slot_float_get(op->slots_in, "depth");
|
||||
#ifdef USE_LOOP_CUSTOMDATA_MERGE
|
||||
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
|
||||
@@ -1096,7 +1121,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
|
||||
}
|
||||
|
||||
/* apply the offset */
|
||||
madd_v3_v3fl(v_split->co, tvec, thickness);
|
||||
if (use_attributes) {
|
||||
madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]);
|
||||
}
|
||||
else {
|
||||
madd_v3_v3fl(v_split->co, tvec, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
/* this saves expensive/slow glue check for common cases */
|
||||
|
@@ -328,10 +328,14 @@ set(SRC
|
||||
operations/COM_FastGaussianBlurOperation.h
|
||||
operations/COM_GammaCorrectOperation.cc
|
||||
operations/COM_GammaCorrectOperation.h
|
||||
operations/COM_GaussianAlphaBlurBaseOperation.cc
|
||||
operations/COM_GaussianAlphaBlurBaseOperation.h
|
||||
operations/COM_GaussianAlphaXBlurOperation.cc
|
||||
operations/COM_GaussianAlphaXBlurOperation.h
|
||||
operations/COM_GaussianAlphaYBlurOperation.cc
|
||||
operations/COM_GaussianAlphaYBlurOperation.h
|
||||
operations/COM_GaussianBlurBaseOperation.cc
|
||||
operations/COM_GaussianBlurBaseOperation.h
|
||||
operations/COM_GaussianBokehBlurOperation.cc
|
||||
operations/COM_GaussianBokehBlurOperation.h
|
||||
operations/COM_GaussianXBlurOperation.cc
|
||||
@@ -515,6 +519,8 @@ set(SRC
|
||||
operations/COM_ScaleOperation.h
|
||||
operations/COM_ScreenLensDistortionOperation.cc
|
||||
operations/COM_ScreenLensDistortionOperation.h
|
||||
operations/COM_TransformOperation.cc
|
||||
operations/COM_TransformOperation.h
|
||||
operations/COM_TranslateOperation.cc
|
||||
operations/COM_TranslateOperation.h
|
||||
operations/COM_WrapOperation.cc
|
||||
|
@@ -33,6 +33,8 @@ enum class eExecutionModel {
|
||||
FullFrame
|
||||
};
|
||||
|
||||
enum class eDimension { X, Y };
|
||||
|
||||
/**
|
||||
* \brief possible data types for sockets
|
||||
* \ingroup Model
|
||||
@@ -119,6 +121,8 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
|
||||
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
|
||||
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
|
||||
|
||||
constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1};
|
||||
|
||||
constexpr IndexRange XRange(const rcti &area)
|
||||
{
|
||||
return IndexRange(area.xmin, area.xmax - area.xmin);
|
||||
|
@@ -239,6 +239,12 @@ class CompositorContext {
|
||||
this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices;
|
||||
}
|
||||
|
||||
/** Whether it has a view with a specific name and not the default one. */
|
||||
bool has_explicit_view() const
|
||||
{
|
||||
return m_viewName && m_viewName[0] != '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief get the active rendering view
|
||||
*/
|
||||
|
@@ -245,7 +245,9 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src,
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
|
||||
{
|
||||
copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0);
|
||||
const int elem_stride = this->get_num_channels();
|
||||
const int row_stride = elem_stride * getWidth();
|
||||
copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0);
|
||||
}
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src,
|
||||
@@ -253,10 +255,18 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
const int channel_offset,
|
||||
const int elem_size,
|
||||
const int elem_stride,
|
||||
const int row_stride,
|
||||
const int to_channel_offset)
|
||||
{
|
||||
copy_from(
|
||||
src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset);
|
||||
copy_from(src,
|
||||
area,
|
||||
channel_offset,
|
||||
elem_size,
|
||||
elem_stride,
|
||||
row_stride,
|
||||
area.xmin,
|
||||
area.ymin,
|
||||
to_channel_offset);
|
||||
}
|
||||
|
||||
void MemoryBuffer::copy_from(const uchar *src,
|
||||
@@ -264,6 +274,7 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
const int channel_offset,
|
||||
const int elem_size,
|
||||
const int elem_stride,
|
||||
const int row_stride,
|
||||
const int to_x,
|
||||
const int to_y,
|
||||
const int to_channel_offset)
|
||||
@@ -273,10 +284,9 @@ void MemoryBuffer::copy_from(const uchar *src,
|
||||
|
||||
const int width = BLI_rcti_size_x(&area);
|
||||
const int height = BLI_rcti_size_y(&area);
|
||||
const int src_row_stride = width * elem_stride;
|
||||
const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset;
|
||||
const uchar *const src_start = src + area.ymin * row_stride + channel_offset;
|
||||
for (int y = 0; y < height; y++) {
|
||||
const uchar *from_elem = src_start + y * src_row_stride;
|
||||
const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride;
|
||||
float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
|
||||
const float *row_end = to_elem + width * this->elem_stride;
|
||||
while (to_elem < row_end) {
|
||||
@@ -346,7 +356,16 @@ void MemoryBuffer::copy_from(const ImBuf *src,
|
||||
else if (src->rect) {
|
||||
const uchar *uc_buf = (uchar *)src->rect;
|
||||
const int elem_stride = src->channels;
|
||||
copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset);
|
||||
const int row_stride = elem_stride * src->x;
|
||||
copy_from(uc_buf,
|
||||
area,
|
||||
channel_offset,
|
||||
elem_size,
|
||||
elem_stride,
|
||||
row_stride,
|
||||
to_x,
|
||||
to_y,
|
||||
to_channel_offset);
|
||||
if (ensure_linear_space) {
|
||||
colorspace_to_scene_linear(this, area, src->rect_colorspace);
|
||||
}
|
||||
@@ -405,12 +424,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
|
||||
}
|
||||
}
|
||||
|
||||
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
|
||||
buffer->read_elem_checked(x, y, result);
|
||||
}
|
||||
|
||||
void MemoryBuffer::read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const
|
||||
{
|
||||
BLI_assert(this->m_datatype == DataType::Color);
|
||||
|
||||
const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
|
||||
|
||||
float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
|
||||
/* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
|
||||
* but compositor uses pixel space. For now let's just divide the values and
|
||||
* switch compositor to normalized space for EWA later.
|
||||
*/
|
||||
float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
|
||||
float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
|
||||
float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
|
||||
|
||||
BLI_ewa_filter(this->getWidth(),
|
||||
this->getHeight(),
|
||||
false,
|
||||
true,
|
||||
uv_normal,
|
||||
du_normal,
|
||||
dv_normal,
|
||||
read_ewa_elem,
|
||||
const_cast<MemoryBuffer *>(this),
|
||||
out);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
MemoryBuffer *buffer = (MemoryBuffer *)userdata;
|
||||
buffer->read(result, x, y);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
|
||||
{
|
||||
if (m_is_a_single_elem) {
|
||||
|
@@ -191,23 +191,96 @@ class MemoryBuffer {
|
||||
|
||||
void read_elem(int x, int y, float *out) const
|
||||
{
|
||||
memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
|
||||
memcpy(out, get_elem(x, y), get_elem_bytes_len());
|
||||
}
|
||||
|
||||
void read_elem_checked(int x, int y, float *out) const
|
||||
{
|
||||
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
}
|
||||
else {
|
||||
read_elem(x, y, out);
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_checked(float x, float y, float *out) const
|
||||
{
|
||||
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
}
|
||||
else {
|
||||
read_elem(x, y, out);
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_bilinear(float x, float y, float *out) const
|
||||
{
|
||||
/* Only clear past +/-1 borders to be able to smooth edges. */
|
||||
if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f ||
|
||||
y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_a_single_elem) {
|
||||
if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin &&
|
||||
y < m_rect.ymax - 1.0f) {
|
||||
memcpy(out, m_buffer, get_elem_bytes_len());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do sampling at borders to smooth edges. */
|
||||
const float last_x = getWidth() - 1.0f;
|
||||
const float rel_x = get_relative_x(x);
|
||||
float single_x = 0.0f;
|
||||
if (rel_x < 0.0f) {
|
||||
single_x = rel_x;
|
||||
}
|
||||
else if (rel_x > last_x) {
|
||||
single_x = rel_x - last_x;
|
||||
}
|
||||
|
||||
const float last_y = getHeight() - 1.0f;
|
||||
const float rel_y = get_relative_y(y);
|
||||
float single_y = 0.0f;
|
||||
if (rel_y < 0.0f) {
|
||||
single_y = rel_y;
|
||||
}
|
||||
else if (rel_y > last_y) {
|
||||
single_y = rel_y - last_y;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer,
|
||||
out,
|
||||
getWidth(),
|
||||
getHeight(),
|
||||
m_num_channels,
|
||||
get_relative_x(x),
|
||||
get_relative_y(y));
|
||||
}
|
||||
|
||||
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
|
||||
{
|
||||
switch (sampler) {
|
||||
case PixelSampler::Nearest:
|
||||
this->read_elem(x, y, out);
|
||||
read_elem_checked(x, y, out);
|
||||
break;
|
||||
case PixelSampler::Bilinear:
|
||||
case PixelSampler::Bicubic:
|
||||
/* No bicubic. Current implementation produces fuzzy results. */
|
||||
this->readBilinear(out, x, y);
|
||||
read_elem_bilinear(x, y, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const;
|
||||
|
||||
/**
|
||||
* Get channel value at given coordinates.
|
||||
*/
|
||||
@@ -403,6 +476,8 @@ class MemoryBuffer {
|
||||
y = y + m_rect.ymin;
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend
|
||||
* use #wrap_pixel. */
|
||||
inline void read(float *result,
|
||||
int x,
|
||||
int y,
|
||||
@@ -425,6 +500,7 @@ class MemoryBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
inline void readNoCheck(float *result,
|
||||
int x,
|
||||
int y,
|
||||
@@ -507,12 +583,14 @@ class MemoryBuffer {
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
int elem_stride,
|
||||
int row_stride,
|
||||
int to_channel_offset);
|
||||
void copy_from(const uchar *src,
|
||||
const rcti &area,
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
int elem_stride,
|
||||
int row_stride,
|
||||
int to_x,
|
||||
int to_y,
|
||||
int to_channel_offset);
|
||||
@@ -582,6 +660,21 @@ class MemoryBuffer {
|
||||
return get_memory_width() * get_memory_height();
|
||||
}
|
||||
|
||||
void clear_elem(float *out) const
|
||||
{
|
||||
memset(out, 0, this->m_num_channels * sizeof(float));
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_x(T x) const
|
||||
{
|
||||
return x - m_rect.xmin;
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_y(T y) const
|
||||
{
|
||||
return y - m_rect.ymin;
|
||||
}
|
||||
|
||||
void copy_single_elem_from(const MemoryBuffer *src,
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
|
@@ -248,7 +248,9 @@ void NodeGraph::add_proxies_group_inputs(bNode *b_node, bNode *b_node_io)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer)
|
||||
void NodeGraph::add_proxies_group_outputs(const CompositorContext &context,
|
||||
bNode *b_node,
|
||||
bNode *b_node_io)
|
||||
{
|
||||
bNodeTree *b_group_tree = (bNodeTree *)b_node->id;
|
||||
BLI_assert(b_group_tree); /* should have been checked in advance */
|
||||
@@ -261,7 +263,8 @@ void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool
|
||||
b_sock_io = b_sock_io->next) {
|
||||
bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier);
|
||||
if (b_sock_group) {
|
||||
if (use_buffer) {
|
||||
if (context.isGroupnodeBufferEnabled() &&
|
||||
context.get_execution_model() == eExecutionModel::Tiled) {
|
||||
SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group);
|
||||
add_node(buffer, b_group_tree, key, is_active_group);
|
||||
}
|
||||
@@ -297,7 +300,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context,
|
||||
}
|
||||
|
||||
if (b_node_io->type == NODE_GROUP_OUTPUT && (b_node_io->flag & NODE_DO_OUTPUT)) {
|
||||
add_proxies_group_outputs(b_node, b_node_io, context.isGroupnodeBufferEnabled());
|
||||
add_proxies_group_outputs(context, b_node, b_node_io);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -107,7 +107,9 @@ class NodeGraph {
|
||||
bool is_active_group);
|
||||
|
||||
void add_proxies_group_inputs(bNode *b_node, bNode *b_node_io);
|
||||
void add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer);
|
||||
void add_proxies_group_outputs(const CompositorContext &context,
|
||||
bNode *b_node,
|
||||
bNode *b_node_io);
|
||||
void add_proxies_group(const CompositorContext &context, bNode *b_node, bNodeInstanceKey key);
|
||||
|
||||
void add_proxies_reroute(bNodeTree *b_ntree,
|
||||
|
@@ -82,8 +82,12 @@ void NodeOperation::determineResolution(unsigned int resolution[2],
|
||||
input.determineResolution(resolution, preferredResolution);
|
||||
used_resolution_index = m_resolutionInputSocketIndex;
|
||||
}
|
||||
unsigned int temp2[2] = {resolution[0], resolution[1]};
|
||||
|
||||
if (modify_determined_resolution_fn_) {
|
||||
modify_determined_resolution_fn_(resolution);
|
||||
}
|
||||
|
||||
unsigned int temp2[2] = {resolution[0], resolution[1]};
|
||||
unsigned int temp[2];
|
||||
for (unsigned int index = 0; index < m_inputs.size(); index++) {
|
||||
if (index == used_resolution_index) {
|
||||
|
@@ -287,6 +287,8 @@ class NodeOperation {
|
||||
*/
|
||||
unsigned int m_resolutionInputSocketIndex;
|
||||
|
||||
std::function<void(unsigned int resolution[2])> modify_determined_resolution_fn_;
|
||||
|
||||
/**
|
||||
* \brief mutex reference for very special node initializations
|
||||
* \note only use when you really know what you are doing.
|
||||
@@ -517,6 +519,15 @@ class NodeOperation {
|
||||
*/
|
||||
void setResolutionInputSocketIndex(unsigned int index);
|
||||
|
||||
/**
|
||||
* Set a custom function to modify determined resolution from main input just before setting it
|
||||
* as preferred resolution for the other inputs.
|
||||
*/
|
||||
void set_determined_resolution_modifier(std::function<void(unsigned int resolution[2])> fn)
|
||||
{
|
||||
modify_determined_resolution_fn_ = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief get the render priority of this node.
|
||||
* \note only applicable for output operations like ViewerOperation
|
||||
|
@@ -124,6 +124,10 @@ void CryptomatteNode::input_operations_from_render_source(
|
||||
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
|
||||
if (render_layer) {
|
||||
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
|
||||
if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
|
||||
if (blender::StringRef(combined_name).startswith(prefix)) {
|
||||
RenderLayersProg *op = new RenderLayersProg(
|
||||
|
@@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
|
||||
}
|
||||
|
||||
void RotateNode::convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext & /*context*/) const
|
||||
const CompositorContext &context) const
|
||||
{
|
||||
NodeInput *inputSocket = this->getInputSocket(0);
|
||||
NodeInput *inputDegreeSocket = this->getInputSocket(1);
|
||||
NodeOutput *outputSocket = this->getOutputSocket(0);
|
||||
RotateOperation *operation = new RotateOperation();
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
|
||||
converter.addOperation(sampler);
|
||||
converter.addOperation(operation);
|
||||
|
||||
converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0));
|
||||
converter.mapInputSocket(inputSocket, sampler->getInputSocket(0));
|
||||
PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
SetSamplerOperation *sampler_op = new SetSamplerOperation();
|
||||
sampler_op->setSampler(sampler);
|
||||
converter.addOperation(sampler_op);
|
||||
converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0));
|
||||
converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0));
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
operation->set_sampler(sampler);
|
||||
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
|
||||
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "COM_RotateOperation.h"
|
||||
#include "COM_ScaleOperation.h"
|
||||
#include "COM_SetSamplerOperation.h"
|
||||
#include "COM_TransformOperation.h"
|
||||
#include "COM_TranslateOperation.h"
|
||||
|
||||
#include "BKE_tracking.h"
|
||||
@@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
|
||||
NodeInput *imageInput = this->getInputSocket(0);
|
||||
MovieClip *clip = (MovieClip *)editorNode->id;
|
||||
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
|
||||
const PixelSampler sampler = (PixelSampler)editorNode->custom1;
|
||||
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
scaleOperation->setSampler((PixelSampler)editorNode->custom1);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation();
|
||||
MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation();
|
||||
SetSamplerOperation *psoperation = new SetSamplerOperation();
|
||||
psoperation->setSampler((PixelSampler)editorNode->custom1);
|
||||
|
||||
scaleAttribute->setAttribute(MCA_SCALE);
|
||||
scaleAttribute->setFramenumber(context.getFramenumber());
|
||||
@@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
|
||||
converter.addOperation(angleAttribute);
|
||||
converter.addOperation(xAttribute);
|
||||
converter.addOperation(yAttribute);
|
||||
converter.addOperation(scaleOperation);
|
||||
converter.addOperation(translateOperation);
|
||||
converter.addOperation(rotateOperation);
|
||||
converter.addOperation(psoperation);
|
||||
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
scaleOperation->setSampler(sampler);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
SetSamplerOperation *psoperation = new SetSamplerOperation();
|
||||
psoperation->setSampler(sampler);
|
||||
|
||||
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
converter.addOperation(scaleOperation);
|
||||
converter.addOperation(translateOperation);
|
||||
converter.addOperation(rotateOperation);
|
||||
converter.addOperation(psoperation);
|
||||
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
|
||||
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
|
||||
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
else {
|
||||
// Scale -> Rotate -> Translate.
|
||||
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
|
||||
if (invert) {
|
||||
// Translate -> Rotate -> Scale.
|
||||
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.addLink(translateOperation->getOutputSocket(),
|
||||
rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
else {
|
||||
// Scale -> Rotate -> Translate.
|
||||
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.addLink(rotateOperation->getOutputSocket(),
|
||||
translateOperation->getInputSocket(0));
|
||||
|
||||
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
TransformOperation *transform_op = new TransformOperation();
|
||||
transform_op->set_sampler(sampler);
|
||||
transform_op->set_convert_rotate_degree_to_rad(false);
|
||||
transform_op->set_invert(invert);
|
||||
converter.addOperation(transform_op);
|
||||
converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
|
||||
converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
|
||||
converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
|
||||
converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
|
||||
converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
|
||||
converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "COM_ScaleOperation.h"
|
||||
#include "COM_SetSamplerOperation.h"
|
||||
#include "COM_SetValueOperation.h"
|
||||
#include "COM_TransformOperation.h"
|
||||
#include "COM_TranslateOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
@@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
|
||||
}
|
||||
|
||||
void TransformNode::convertToOperations(NodeConverter &converter,
|
||||
const CompositorContext & /*context*/) const
|
||||
const CompositorContext &context) const
|
||||
{
|
||||
NodeInput *imageInput = this->getInputSocket(0);
|
||||
NodeInput *xInput = this->getInputSocket(1);
|
||||
@@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter,
|
||||
NodeInput *angleInput = this->getInputSocket(3);
|
||||
NodeInput *scaleInput = this->getInputSocket(4);
|
||||
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
converter.addOperation(scaleOperation);
|
||||
switch (context.get_execution_model()) {
|
||||
case eExecutionModel::Tiled: {
|
||||
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
|
||||
converter.addOperation(scaleOperation);
|
||||
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
converter.addOperation(rotateOperation);
|
||||
RotateOperation *rotateOperation = new RotateOperation();
|
||||
rotateOperation->setDoDegree2RadConversion(false);
|
||||
converter.addOperation(rotateOperation);
|
||||
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
converter.addOperation(translateOperation);
|
||||
TranslateOperation *translateOperation = new TranslateOperation();
|
||||
converter.addOperation(translateOperation);
|
||||
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(sampler);
|
||||
SetSamplerOperation *sampler = new SetSamplerOperation();
|
||||
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(sampler);
|
||||
|
||||
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
|
||||
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
|
||||
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
|
||||
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
|
||||
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
|
||||
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
|
||||
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
|
||||
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
|
||||
|
||||
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
|
||||
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
TransformOperation *op = new TransformOperation();
|
||||
op->set_sampler((PixelSampler)this->getbNode()->custom1);
|
||||
converter.addOperation(op);
|
||||
|
||||
converter.mapInputSocket(imageInput, op->getInputSocket(0));
|
||||
converter.mapInputSocket(xInput, op->getInputSocket(1));
|
||||
converter.mapInputSocket(yInput, op->getInputSocket(2));
|
||||
converter.mapInputSocket(angleInput, op->getInputSocket(3));
|
||||
converter.mapInputSocket(scaleInput, op->getInputSocket(4));
|
||||
converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "COM_BlurBaseOperation.h"
|
||||
#include "COM_ConstantOperation.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
@@ -36,11 +38,15 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type)
|
||||
this->m_size = 1.0f;
|
||||
this->m_sizeavailable = false;
|
||||
this->m_extend_bounds = false;
|
||||
use_variable_size_ = false;
|
||||
}
|
||||
void BlurBaseOperation::initExecution()
|
||||
|
||||
void BlurBaseOperation::init_data()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
this->m_inputSize = this->getInputSocketReader(1);
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
updateSize();
|
||||
}
|
||||
|
||||
this->m_data.image_in_width = this->getWidth();
|
||||
this->m_data.image_in_height = this->getHeight();
|
||||
if (this->m_data.relative) {
|
||||
@@ -61,6 +67,12 @@ void BlurBaseOperation::initExecution()
|
||||
this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex);
|
||||
this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey);
|
||||
}
|
||||
}
|
||||
|
||||
void BlurBaseOperation::initExecution()
|
||||
{
|
||||
this->m_inputProgram = this->getInputSocketReader(0);
|
||||
this->m_inputSize = this->getInputSocketReader(1);
|
||||
|
||||
QualityStepHelper::initExecution(COM_QH_MULTIPLY);
|
||||
}
|
||||
@@ -165,23 +177,82 @@ void BlurBaseOperation::setData(const NodeBlurData *data)
|
||||
memcpy(&m_data, data, sizeof(NodeBlurData));
|
||||
}
|
||||
|
||||
int BlurBaseOperation::get_blur_size(eDimension dim) const
|
||||
{
|
||||
switch (dim) {
|
||||
case eDimension::X:
|
||||
return m_data.sizex;
|
||||
case eDimension::Y:
|
||||
return m_data.sizey;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void BlurBaseOperation::updateSize()
|
||||
{
|
||||
if (!this->m_sizeavailable) {
|
||||
float result[4];
|
||||
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
this->m_sizeavailable = true;
|
||||
if (this->m_sizeavailable || use_variable_size_) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
float result[4];
|
||||
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
|
||||
if (size_input->get_flags().is_constant_operation) {
|
||||
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
|
||||
} /* Else use default. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->m_sizeavailable = true;
|
||||
}
|
||||
|
||||
void BlurBaseOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
{
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
if (this->m_extend_bounds) {
|
||||
resolution[0] += 2 * this->m_size * m_data.sizex;
|
||||
resolution[1] += 2 * this->m_size * m_data.sizey;
|
||||
if (!m_extend_bounds) {
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
resolution[0] += 2 * m_size * m_data.sizex;
|
||||
resolution[1] += 2 * m_size * m_data.sizey;
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
/* Setting a modifier ensures all non main inputs have extended bounds as preferred
|
||||
* resolution, avoiding unnecessary resolution convertions that would hide constant
|
||||
* operations. */
|
||||
set_determined_resolution_modifier([=](unsigned int res[2]) {
|
||||
/* Rounding to even prevents jiggling in backdrop while switching size values. */
|
||||
res[0] += round_to_even(2 * m_size * m_data.sizex);
|
||||
res[1] += round_to_even(2 * m_size * m_data.sizey);
|
||||
});
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurBaseOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case 0:
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
case 1:
|
||||
r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_QualityStepHelper.h"
|
||||
|
||||
#define MAX_GAUSSTAB_RADIUS 30000
|
||||
@@ -27,10 +27,16 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper {
|
||||
private:
|
||||
bool m_extend_bounds;
|
||||
|
||||
protected:
|
||||
BlurBaseOperation(DataType data_type);
|
||||
static constexpr int IMAGE_INPUT_INDEX = 0;
|
||||
static constexpr int SIZE_INPUT_INDEX = 1;
|
||||
|
||||
protected:
|
||||
BlurBaseOperation(DataType data_type8);
|
||||
float *make_gausstab(float rad, int size);
|
||||
#ifdef BLI_HAVE_SSE2
|
||||
__m128 *convert_gausstab_sse(const float *gausstab, int size);
|
||||
@@ -49,9 +55,11 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
float m_size;
|
||||
bool m_sizeavailable;
|
||||
|
||||
bool m_extend_bounds;
|
||||
/* Flags for inheriting classes. */
|
||||
bool use_variable_size_;
|
||||
|
||||
public:
|
||||
virtual void init_data() override;
|
||||
/**
|
||||
* Initialize the execution
|
||||
*/
|
||||
@@ -75,8 +83,14 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
|
||||
this->m_extend_bounds = extend_bounds;
|
||||
}
|
||||
|
||||
int get_blur_size(eDimension dim) const;
|
||||
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
|
||||
virtual void get_area_of_interest(int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "COM_BokehBlurOperation.h"
|
||||
#include "COM_ConstantOperation.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "COM_OpenCLDevice.h"
|
||||
|
||||
@@ -24,6 +26,11 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
constexpr int IMAGE_INPUT_INDEX = 0;
|
||||
constexpr int BOKEH_INPUT_INDEX = 1;
|
||||
constexpr int BOUNDING_BOX_INPUT_INDEX = 2;
|
||||
constexpr int SIZE_INPUT_INDEX = 3;
|
||||
|
||||
BokehBlurOperation::BokehBlurOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Color);
|
||||
@@ -44,6 +51,23 @@ BokehBlurOperation::BokehBlurOperation()
|
||||
this->m_extend_bounds = false;
|
||||
}
|
||||
|
||||
void BokehBlurOperation::init_data()
|
||||
{
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
updateSize();
|
||||
}
|
||||
|
||||
NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX);
|
||||
const int width = bokeh->getWidth();
|
||||
const int height = bokeh->getHeight();
|
||||
|
||||
const float dimension = MIN2(width, height);
|
||||
|
||||
m_bokehMidX = width / 2.0f;
|
||||
m_bokehMidY = height / 2.0f;
|
||||
m_bokehDimension = dimension / 2.0f;
|
||||
}
|
||||
|
||||
void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
{
|
||||
lockMutex();
|
||||
@@ -58,18 +82,11 @@ void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
void BokehBlurOperation::initExecution()
|
||||
{
|
||||
initMutex();
|
||||
|
||||
this->m_inputProgram = getInputSocketReader(0);
|
||||
this->m_inputBokehProgram = getInputSocketReader(1);
|
||||
this->m_inputBoundingBoxReader = getInputSocketReader(2);
|
||||
|
||||
int width = this->m_inputBokehProgram->getWidth();
|
||||
int height = this->m_inputBokehProgram->getHeight();
|
||||
|
||||
float dimension = MIN2(width, height);
|
||||
|
||||
this->m_bokehMidX = width / 2.0f;
|
||||
this->m_bokehMidY = height / 2.0f;
|
||||
this->m_bokehDimension = dimension / 2.0f;
|
||||
QualityStepHelper::initExecution(COM_QH_INCREASE);
|
||||
}
|
||||
|
||||
@@ -225,23 +242,146 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device,
|
||||
|
||||
void BokehBlurOperation::updateSize()
|
||||
{
|
||||
if (!this->m_sizeavailable) {
|
||||
float result[4];
|
||||
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
CLAMP(this->m_size, 0.0f, 10.0f);
|
||||
this->m_sizeavailable = true;
|
||||
if (this->m_sizeavailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
float result[4];
|
||||
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
|
||||
this->m_size = result[0];
|
||||
CLAMP(this->m_size, 0.0f, 10.0f);
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
|
||||
if (size_input->get_flags().is_constant_operation) {
|
||||
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
|
||||
CLAMP(m_size, 0.0f, 10.0f);
|
||||
} /* Else use default. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->m_sizeavailable = true;
|
||||
}
|
||||
|
||||
void BokehBlurOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
{
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
if (this->m_extend_bounds) {
|
||||
const float max_dim = MAX2(resolution[0], resolution[1]);
|
||||
resolution[0] += 2 * this->m_size * max_dim / 100.0f;
|
||||
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
|
||||
if (!m_extend_bounds) {
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (execution_model_) {
|
||||
case eExecutionModel::Tiled: {
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
const float max_dim = MAX2(resolution[0], resolution[1]);
|
||||
resolution[0] += 2 * this->m_size * max_dim / 100.0f;
|
||||
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
|
||||
break;
|
||||
}
|
||||
case eExecutionModel::FullFrame: {
|
||||
set_determined_resolution_modifier([=](unsigned int res[2]) {
|
||||
const float max_dim = MAX2(res[0], res[1]);
|
||||
/* Rounding to even prevents image jiggling in backdrop while switching size values. */
|
||||
float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f);
|
||||
res[0] += add_size;
|
||||
res[1] += add_size;
|
||||
});
|
||||
NodeOperation::determineResolution(resolution, preferredResolution);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BokehBlurOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX: {
|
||||
const float max_dim = MAX2(this->getWidth(), this->getHeight());
|
||||
const float add_size = m_size * max_dim / 100.0f;
|
||||
r_input_area.xmin = output_area.xmin - add_size;
|
||||
r_input_area.xmax = output_area.xmax + add_size;
|
||||
r_input_area.ymin = output_area.ymin - add_size;
|
||||
r_input_area.ymax = output_area.ymax + add_size;
|
||||
break;
|
||||
}
|
||||
case BOKEH_INPUT_INDEX: {
|
||||
NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX);
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.xmax = bokeh_input->getWidth();
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.ymax = bokeh_input->getHeight();
|
||||
break;
|
||||
}
|
||||
case BOUNDING_BOX_INPUT_INDEX:
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
case SIZE_INPUT_INDEX: {
|
||||
r_input_area = COM_SINGLE_ELEM_AREA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float max_dim = MAX2(this->getWidth(), this->getHeight());
|
||||
const int pixel_size = m_size * max_dim / 100.0f;
|
||||
const float m = m_bokehDimension / pixel_size;
|
||||
|
||||
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
|
||||
const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX];
|
||||
MemoryBuffer *bounding_input = inputs[BOUNDING_BOX_INPUT_INDEX];
|
||||
BuffersIterator<float> it = output->iterate_with({bounding_input}, area);
|
||||
const rcti &image_rect = image_input->get_rect();
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int x = it.x;
|
||||
const int y = it.y;
|
||||
const float bounding_box = *it.in(0);
|
||||
if (bounding_box <= 0.0f) {
|
||||
image_input->read_elem(x, y, it.out);
|
||||
continue;
|
||||
}
|
||||
|
||||
float color_accum[4] = {0};
|
||||
float multiplier_accum[4] = {0};
|
||||
if (pixel_size < 2) {
|
||||
image_input->read_elem(x, y, color_accum);
|
||||
multiplier_accum[0] = 1.0f;
|
||||
multiplier_accum[1] = 1.0f;
|
||||
multiplier_accum[2] = 1.0f;
|
||||
multiplier_accum[3] = 1.0f;
|
||||
}
|
||||
const int miny = MAX2(y - pixel_size, image_rect.ymin);
|
||||
const int maxy = MIN2(y + pixel_size, image_rect.ymax);
|
||||
const int minx = MAX2(x - pixel_size, image_rect.xmin);
|
||||
const int maxx = MIN2(x + pixel_size, image_rect.xmax);
|
||||
const int step = getStep();
|
||||
const int elem_stride = image_input->elem_stride * step;
|
||||
const int row_stride = image_input->row_stride * step;
|
||||
const float *row_color = image_input->get_elem(minx, miny);
|
||||
for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) {
|
||||
const float *color = row_color;
|
||||
const float v = m_bokehMidY - (ny - y) * m;
|
||||
for (int nx = minx; nx < maxx; nx += step, color += elem_stride) {
|
||||
const float u = m_bokehMidX - (nx - x) * m;
|
||||
float bokeh[4];
|
||||
bokeh_input->read_elem_checked(u, v, bokeh);
|
||||
madd_v4_v4v4(color_accum, bokeh, color);
|
||||
add_v4_v4(multiplier_accum, bokeh);
|
||||
}
|
||||
}
|
||||
it.out[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
|
||||
it.out[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
|
||||
it.out[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
|
||||
it.out[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,12 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_QualityStepHelper.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
|
||||
private:
|
||||
SocketReader *m_inputProgram;
|
||||
SocketReader *m_inputBokehProgram;
|
||||
@@ -31,6 +31,7 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
void updateSize();
|
||||
float m_size;
|
||||
bool m_sizeavailable;
|
||||
|
||||
float m_bokehMidX;
|
||||
float m_bokehMidY;
|
||||
float m_bokehDimension;
|
||||
@@ -39,6 +40,8 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
public:
|
||||
BokehBlurOperation();
|
||||
|
||||
void init_data() override;
|
||||
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
/**
|
||||
* The inner loop of this operation.
|
||||
@@ -79,6 +82,11 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
|
||||
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -27,6 +27,7 @@ ChannelMatteOperation::ChannelMatteOperation()
|
||||
addOutputSocket(DataType::Value);
|
||||
|
||||
this->m_inputImageProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void ChannelMatteOperation::initExecution()
|
||||
@@ -121,4 +122,37 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
|
||||
output[0] = MIN2(alpha, inColor[3]);
|
||||
}
|
||||
|
||||
void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *color = it.in(0);
|
||||
|
||||
/* Matte operation. */
|
||||
float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]);
|
||||
|
||||
/* Flip because 0.0 is transparent, not 1.0. */
|
||||
alpha = 1.0f - alpha;
|
||||
|
||||
/* Test range. */
|
||||
if (alpha > m_limit_max) {
|
||||
alpha = color[3]; /* Whatever it was prior. */
|
||||
}
|
||||
else if (alpha < m_limit_min) {
|
||||
alpha = 0.0f;
|
||||
}
|
||||
else { /* Blend. */
|
||||
alpha = (alpha - m_limit_min) / m_limit_range;
|
||||
}
|
||||
|
||||
/* Store matte(alpha) value in [0] to go with
|
||||
* COM_SetAlphaMultiplyOperation and the Value output.
|
||||
*/
|
||||
|
||||
/* Don't make something that was more transparent less transparent. */
|
||||
*it.out = MIN2(alpha, color[3]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MixOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class ChannelMatteOperation : public NodeOperation {
|
||||
class ChannelMatteOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputImageProgram;
|
||||
|
||||
@@ -71,6 +71,10 @@ class ChannelMatteOperation : public NodeOperation {
|
||||
this->m_limit_channel = nodeChroma->channel;
|
||||
this->m_matte_channel = custom2;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,6 +29,7 @@ ChromaMatteOperation::ChromaMatteOperation()
|
||||
|
||||
this->m_inputImageProgram = nullptr;
|
||||
this->m_inputKeyProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void ChromaMatteOperation::initExecution()
|
||||
@@ -110,4 +111,58 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float acceptance = this->m_settings->t1; /* In radians. */
|
||||
const float cutoff = this->m_settings->t2; /* In radians. */
|
||||
const float gain = this->m_settings->fstrength;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *in_image = it.in(0);
|
||||
const float *in_key = it.in(1);
|
||||
|
||||
/* Store matte(alpha) value in [0] to go with
|
||||
* #COM_SetAlphaMultiplyOperation and the Value output. */
|
||||
|
||||
/* Algorithm from book "Video Demystified", does not include the spill reduction part. */
|
||||
/* Find theta, the angle that the color space should be rotated based on key. */
|
||||
|
||||
/* Rescale to `-1.0..1.0`. */
|
||||
// const float image_Y = (in_image[0] * 2.0f) - 1.0f; // UNUSED
|
||||
const float image_cb = (in_image[1] * 2.0f) - 1.0f;
|
||||
const float image_cr = (in_image[2] * 2.0f) - 1.0f;
|
||||
|
||||
// const float key_Y = (in_key[0] * 2.0f) - 1.0f; // UNUSED
|
||||
const float key_cb = (in_key[1] * 2.0f) - 1.0f;
|
||||
const float key_cr = (in_key[2] * 2.0f) - 1.0f;
|
||||
|
||||
const float theta = atan2(key_cr, key_cb);
|
||||
|
||||
/* Rotate the cb and cr into x/z space. */
|
||||
const float x_angle = image_cb * cosf(theta) + image_cr * sinf(theta);
|
||||
const float z_angle = image_cr * cosf(theta) - image_cb * sinf(theta);
|
||||
|
||||
/* If within the acceptance angle. */
|
||||
/* If kfg is <0 then the pixel is outside of the key color. */
|
||||
const float kfg = x_angle - (fabsf(z_angle) / tanf(acceptance / 2.0f));
|
||||
|
||||
if (kfg > 0.0f) { /* Found a pixel that is within key color. */
|
||||
const float beta = atan2(z_angle, x_angle);
|
||||
float alpha = 1.0f - (kfg / gain);
|
||||
|
||||
/* Ff beta is within the cutoff angle. */
|
||||
if (fabsf(beta) < (cutoff / 2.0f)) {
|
||||
alpha = 0.0f;
|
||||
}
|
||||
|
||||
/* Don't make something that was more transparent less transparent. */
|
||||
it.out[0] = alpha < in_image[3] ? alpha : in_image[3];
|
||||
}
|
||||
else { /* Pixel is outside key color. */
|
||||
it.out[0] = in_image[3]; /* Make pixel just as transparent as it was before. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MixOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class ChromaMatteOperation : public NodeOperation {
|
||||
class ChromaMatteOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
NodeChroma *m_settings;
|
||||
SocketReader *m_inputImageProgram;
|
||||
@@ -50,6 +50,10 @@ class ChromaMatteOperation : public NodeOperation {
|
||||
{
|
||||
this->m_settings = nodeChroma;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,6 +29,7 @@ ColorMatteOperation::ColorMatteOperation()
|
||||
|
||||
this->m_inputImageProgram = nullptr;
|
||||
this->m_inputKeyProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void ColorMatteOperation::initExecution()
|
||||
@@ -82,4 +83,40 @@ void ColorMatteOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float hue = m_settings->t1;
|
||||
const float sat = m_settings->t2;
|
||||
const float val = m_settings->t3;
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *in_color = it.in(0);
|
||||
const float *in_key = it.in(1);
|
||||
|
||||
/* Store matte(alpha) value in [0] to go with
|
||||
* COM_SetAlphaMultiplyOperation and the Value output.
|
||||
*/
|
||||
|
||||
float h_wrap;
|
||||
if (
|
||||
/* Do hue last because it needs to wrap, and does some more checks. */
|
||||
|
||||
/* #sat */ (fabsf(in_color[1] - in_key[1]) < sat) &&
|
||||
/* #val */ (fabsf(in_color[2] - in_key[2]) < val) &&
|
||||
|
||||
/* Multiply by 2 because it wraps on both sides of the hue,
|
||||
* otherwise 0.5 would key all hue's. */
|
||||
|
||||
/* #hue */
|
||||
((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) {
|
||||
it.out[0] = 0.0f; /* Make transparent. */
|
||||
}
|
||||
|
||||
else { /* Pixel is outside key color. */
|
||||
it.out[0] = in_color[3]; /* Make pixel just as transparent as it was before. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MixOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class ColorMatteOperation : public NodeOperation {
|
||||
class ColorMatteOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
NodeChroma *m_settings;
|
||||
SocketReader *m_inputImageProgram;
|
||||
@@ -50,6 +50,10 @@ class ColorMatteOperation : public NodeOperation {
|
||||
{
|
||||
this->m_settings = nodeChroma;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,6 +29,7 @@ ColorRampOperation::ColorRampOperation()
|
||||
|
||||
this->m_inputProgram = nullptr;
|
||||
this->m_colorBand = nullptr;
|
||||
this->flags.can_be_constant = true;
|
||||
}
|
||||
void ColorRampOperation::initExecution()
|
||||
{
|
||||
@@ -51,4 +52,13 @@ void ColorRampOperation::deinitExecution()
|
||||
this->m_inputProgram = nullptr;
|
||||
}
|
||||
|
||||
void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,12 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class ColorRampOperation : public NodeOperation {
|
||||
class ColorRampOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -53,6 +53,10 @@ class ColorRampOperation : public NodeOperation {
|
||||
{
|
||||
this->m_colorBand = colorBand;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -32,6 +32,7 @@ ColorSpillOperation::ColorSpillOperation()
|
||||
this->m_inputFacReader = nullptr;
|
||||
this->m_spillChannel = 1; // GREEN
|
||||
this->m_spillMethod = 0;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void ColorSpillOperation::initExecution()
|
||||
@@ -118,4 +119,36 @@ void ColorSpillOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *color = it.in(0);
|
||||
const float factor = MIN2(1.0f, *it.in(1));
|
||||
|
||||
float map;
|
||||
switch (m_spillMethod) {
|
||||
case 0: /* simple */
|
||||
map = factor *
|
||||
(color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan]));
|
||||
break;
|
||||
default: /* average */
|
||||
map = factor * (color[m_spillChannel] -
|
||||
(m_settings->limscale * AVG(color[m_channel2], color[m_channel3])));
|
||||
break;
|
||||
}
|
||||
|
||||
if (map > 0.0f) {
|
||||
it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map);
|
||||
it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map);
|
||||
it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map);
|
||||
it.out[3] = color[3];
|
||||
}
|
||||
else {
|
||||
copy_v4_v4(it.out, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class ColorSpillOperation : public NodeOperation {
|
||||
class ColorSpillOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
NodeColorspill *m_settings;
|
||||
SocketReader *m_inputImageReader;
|
||||
@@ -65,6 +65,10 @@ class ColorSpillOperation : public NodeOperation {
|
||||
}
|
||||
|
||||
float calculateMapValue(float fac, float *input);
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -27,6 +27,7 @@ namespace blender::compositor {
|
||||
ConvertBaseOperation::ConvertBaseOperation()
|
||||
{
|
||||
this->m_inputOperation = nullptr;
|
||||
this->flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void ConvertBaseOperation::initExecution()
|
||||
@@ -39,6 +40,14 @@ void ConvertBaseOperation::deinitExecution()
|
||||
this->m_inputOperation = nullptr;
|
||||
}
|
||||
|
||||
void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
BuffersIterator<float> it = output->iterate_with(inputs, area);
|
||||
update_memory_buffer_partial(it);
|
||||
}
|
||||
|
||||
/* ******** Value to Color ******** */
|
||||
|
||||
ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation()
|
||||
@@ -58,6 +67,14 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4],
|
||||
output[3] = 1.0f;
|
||||
}
|
||||
|
||||
void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
|
||||
it.out[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to Value ******** */
|
||||
|
||||
ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation()
|
||||
@@ -76,6 +93,14 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4],
|
||||
output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f;
|
||||
}
|
||||
|
||||
void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to BW ******** */
|
||||
|
||||
ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation()
|
||||
@@ -94,6 +119,13 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4],
|
||||
output[0] = IMB_colormanagement_get_luminance(inputColor);
|
||||
}
|
||||
|
||||
void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = IMB_colormanagement_get_luminance(it.in(0));
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Color to Vector ******** */
|
||||
|
||||
ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOperation()
|
||||
@@ -112,6 +144,13 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4],
|
||||
copy_v3_v3(output, color);
|
||||
}
|
||||
|
||||
void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v3_v3(it.out, it.in(0));
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Value to Vector ******** */
|
||||
|
||||
ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation()
|
||||
@@ -130,6 +169,13 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4],
|
||||
output[0] = output[1] = output[2] = value;
|
||||
}
|
||||
|
||||
void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Vector to Color ******** */
|
||||
|
||||
ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation()
|
||||
@@ -147,6 +193,14 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4],
|
||||
output[3] = 1.0f;
|
||||
}
|
||||
|
||||
void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v3_v3(it.out, it.in(0));
|
||||
it.out[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Vector to Value ******** */
|
||||
|
||||
ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation()
|
||||
@@ -165,6 +219,14 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4],
|
||||
output[0] = (input[0] + input[1] + input[2]) / 3.0f;
|
||||
}
|
||||
|
||||
void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to YCC ******** */
|
||||
|
||||
ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation()
|
||||
@@ -207,6 +269,18 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode);
|
||||
|
||||
/* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */
|
||||
mul_v3_fl(it.out, 1.0f / 255.0f);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** YCC to RGB ******** */
|
||||
|
||||
ConvertYCCToRGBOperation::ConvertYCCToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -253,6 +327,22 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
/* Multiply by 255 to un-normalize (#ycc_to_rgb needs input values in 0-255 range). */
|
||||
ycc_to_rgb(in[0] * 255.0f,
|
||||
in[1] * 255.0f,
|
||||
in[2] * 255.0f,
|
||||
&it.out[0],
|
||||
&it.out[1],
|
||||
&it.out[2],
|
||||
this->m_mode);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to YUV ******** */
|
||||
|
||||
ConvertRGBToYUVOperation::ConvertRGBToYUVOperation() : ConvertBaseOperation()
|
||||
@@ -278,6 +368,15 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_yuv(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** YUV to RGB ******** */
|
||||
|
||||
ConvertYUVToRGBOperation::ConvertYUVToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -303,6 +402,15 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
yuv_to_rgb(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** RGB to HSV ******** */
|
||||
|
||||
ConvertRGBToHSVOperation::ConvertRGBToHSVOperation() : ConvertBaseOperation()
|
||||
@@ -322,6 +430,15 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
rgb_to_hsv_v(in, it.out);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** HSV to RGB ******** */
|
||||
|
||||
ConvertHSVToRGBOperation::ConvertHSVToRGBOperation() : ConvertBaseOperation()
|
||||
@@ -344,6 +461,18 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4],
|
||||
output[3] = inputColor[3];
|
||||
}
|
||||
|
||||
void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float *in = it.in(0);
|
||||
hsv_to_rgb_v(in, it.out);
|
||||
it.out[0] = max_ff(it.out[0], 0.0f);
|
||||
it.out[1] = max_ff(it.out[1], 0.0f);
|
||||
it.out[2] = max_ff(it.out[2], 0.0f);
|
||||
it.out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Premul to Straight ******** */
|
||||
|
||||
ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation()
|
||||
@@ -363,6 +492,13 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
|
||||
copy_v4_v4(output, converted);
|
||||
}
|
||||
|
||||
void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Premultiplied>(it.in(0)).unpremultiply_alpha());
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Straight to Premul ******** */
|
||||
|
||||
ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation()
|
||||
@@ -382,6 +518,13 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
|
||||
copy_v4_v4(output, converted);
|
||||
}
|
||||
|
||||
void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Straight>(it.in(0)).premultiply_alpha());
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Separate Channels ******** */
|
||||
|
||||
SeparateChannelOperation::SeparateChannelOperation()
|
||||
@@ -410,6 +553,15 @@ void SeparateChannelOperation::executePixelSampled(float output[4],
|
||||
output[0] = input[this->m_channel];
|
||||
}
|
||||
|
||||
void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
it.out[0] = it.in(0)[this->m_channel];
|
||||
}
|
||||
}
|
||||
|
||||
/* ******** Combine Channels ******** */
|
||||
|
||||
CombineChannelsOperation::CombineChannelsOperation()
|
||||
@@ -466,4 +618,16 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void CombineChannelsOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
it.out[0] = *it.in(0);
|
||||
it.out[1] = *it.in(1);
|
||||
it.out[2] = *it.in(2);
|
||||
it.out[3] = *it.in(3);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class ConvertBaseOperation : public NodeOperation {
|
||||
class ConvertBaseOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
SocketReader *m_inputOperation;
|
||||
|
||||
@@ -31,6 +31,13 @@ class ConvertBaseOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) final;
|
||||
|
||||
protected:
|
||||
virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0;
|
||||
};
|
||||
|
||||
class ConvertValueToColorOperation : public ConvertBaseOperation {
|
||||
@@ -38,6 +45,9 @@ class ConvertValueToColorOperation : public ConvertBaseOperation {
|
||||
ConvertValueToColorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToValueOperation : public ConvertBaseOperation {
|
||||
@@ -45,6 +55,9 @@ class ConvertColorToValueOperation : public ConvertBaseOperation {
|
||||
ConvertColorToValueOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToBWOperation : public ConvertBaseOperation {
|
||||
@@ -52,6 +65,9 @@ class ConvertColorToBWOperation : public ConvertBaseOperation {
|
||||
ConvertColorToBWOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertColorToVectorOperation : public ConvertBaseOperation {
|
||||
@@ -59,6 +75,9 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation {
|
||||
ConvertColorToVectorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertValueToVectorOperation : public ConvertBaseOperation {
|
||||
@@ -66,6 +85,9 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation {
|
||||
ConvertValueToVectorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertVectorToColorOperation : public ConvertBaseOperation {
|
||||
@@ -73,6 +95,9 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation {
|
||||
ConvertVectorToColorOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertVectorToValueOperation : public ConvertBaseOperation {
|
||||
@@ -80,6 +105,9 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation {
|
||||
ConvertVectorToValueOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToYCCOperation : public ConvertBaseOperation {
|
||||
@@ -94,6 +122,9 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation {
|
||||
|
||||
/** Set the YCC mode */
|
||||
void setMode(int mode);
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertYCCToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -108,6 +139,9 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation {
|
||||
|
||||
/** Set the YCC mode */
|
||||
void setMode(int mode);
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToYUVOperation : public ConvertBaseOperation {
|
||||
@@ -115,6 +149,9 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation {
|
||||
ConvertRGBToYUVOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertYUVToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -122,6 +159,9 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation {
|
||||
ConvertYUVToRGBOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertRGBToHSVOperation : public ConvertBaseOperation {
|
||||
@@ -129,6 +169,9 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation {
|
||||
ConvertRGBToHSVOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertHSVToRGBOperation : public ConvertBaseOperation {
|
||||
@@ -136,6 +179,9 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation {
|
||||
ConvertHSVToRGBOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertPremulToStraightOperation : public ConvertBaseOperation {
|
||||
@@ -143,6 +189,9 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation {
|
||||
ConvertPremulToStraightOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class ConvertStraightToPremulOperation : public ConvertBaseOperation {
|
||||
@@ -150,9 +199,12 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation {
|
||||
ConvertStraightToPremulOperation();
|
||||
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class SeparateChannelOperation : public NodeOperation {
|
||||
class SeparateChannelOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputOperation;
|
||||
int m_channel;
|
||||
@@ -168,9 +220,13 @@ class SeparateChannelOperation : public NodeOperation {
|
||||
{
|
||||
this->m_channel = channel;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class CombineChannelsOperation : public NodeOperation {
|
||||
class CombineChannelsOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputChannel1Operation;
|
||||
SocketReader *m_inputChannel2Operation;
|
||||
@@ -183,6 +239,10 @@ class CombineChannelsOperation : public NodeOperation {
|
||||
|
||||
void initExecution() override;
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel
|
||||
}
|
||||
}
|
||||
|
||||
void CropOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
rcti crop_area;
|
||||
BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax);
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) {
|
||||
copy_v4_v4(it.out, it.in(0));
|
||||
}
|
||||
else {
|
||||
zero_v4(it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CropImageOperation::CropImageOperation() : CropBaseOperation()
|
||||
{
|
||||
/* pass */
|
||||
@@ -114,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void CropImageOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
BLI_assert(input_idx == 0);
|
||||
UNUSED_VARS_NDEBUG(input_idx);
|
||||
r_input_area.xmax = output_area.xmax + this->m_xmin;
|
||||
r_input_area.xmin = output_area.xmin + this->m_xmin;
|
||||
r_input_area.ymax = output_area.ymax + this->m_ymin;
|
||||
r_input_area.ymin = output_area.ymin + this->m_ymin;
|
||||
}
|
||||
|
||||
void CropImageOperation::determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2])
|
||||
{
|
||||
@@ -136,4 +164,21 @@ void CropImageOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
rcti op_area;
|
||||
BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight());
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) {
|
||||
input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out);
|
||||
}
|
||||
else {
|
||||
zero_v4(it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class CropBaseOperation : public NodeOperation {
|
||||
class CropBaseOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
SocketReader *m_inputOperation;
|
||||
NodeTwoXYs *m_settings;
|
||||
@@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation {
|
||||
public:
|
||||
CropOperation();
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class CropImageOperation : public CropBaseOperation {
|
||||
@@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation {
|
||||
void determineResolution(unsigned int resolution[2],
|
||||
unsigned int preferredResolution[2]) override;
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -71,4 +71,34 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
|
||||
}
|
||||
}
|
||||
|
||||
void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
zero_v4(it.out);
|
||||
for (int i = 0; i < it.get_num_inputs(); i++) {
|
||||
const float *input = it.in(i);
|
||||
if (i == 0) {
|
||||
/* Write the front-most object as false color for picking. */
|
||||
it.out[0] = input[0];
|
||||
uint32_t m3hash;
|
||||
::memcpy(&m3hash, &input[0], sizeof(uint32_t));
|
||||
/* Since the red channel is likely to be out of display range,
|
||||
* setting green and blue gives more meaningful images. */
|
||||
it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX);
|
||||
it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX);
|
||||
}
|
||||
for (const float hash : m_objectIndex) {
|
||||
if (input[0] == hash) {
|
||||
it.out[3] += input[1];
|
||||
}
|
||||
if (input[2] == hash) {
|
||||
it.out[3] += input[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class CryptomatteOperation : public NodeOperation {
|
||||
class CryptomatteOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
Vector<float> m_objectIndex;
|
||||
|
||||
@@ -35,6 +35,10 @@ class CryptomatteOperation : public NodeOperation {
|
||||
void executePixel(float output[4], int x, int y, void *data) override;
|
||||
|
||||
void addObjectIndex(float objectIndex);
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,6 +29,7 @@ DifferenceMatteOperation::DifferenceMatteOperation()
|
||||
|
||||
this->m_inputImage1Program = nullptr;
|
||||
this->m_inputImage2Program = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void DifferenceMatteOperation::initExecution()
|
||||
@@ -86,4 +87,44 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *color1 = it.in(0);
|
||||
const float *color2 = it.in(1);
|
||||
|
||||
float difference = (fabsf(color2[0] - color1[0]) + fabsf(color2[1] - color1[1]) +
|
||||
fabsf(color2[2] - color1[2]));
|
||||
|
||||
/* Average together the distances. */
|
||||
difference = difference / 3.0f;
|
||||
|
||||
const float tolerance = m_settings->t1;
|
||||
const float falloff = m_settings->t2;
|
||||
|
||||
/* Make 100% transparent. */
|
||||
if (difference <= tolerance) {
|
||||
it.out[0] = 0.0f;
|
||||
}
|
||||
/* In the falloff region, make partially transparent. */
|
||||
else if (difference <= falloff + tolerance) {
|
||||
difference = difference - tolerance;
|
||||
const float alpha = difference / falloff;
|
||||
/* Only change if more transparent than before. */
|
||||
if (alpha < color1[3]) {
|
||||
it.out[0] = alpha;
|
||||
}
|
||||
else { /* Leave as before. */
|
||||
it.out[0] = color1[3];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Foreground object. */
|
||||
it.out[0] = color1[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MixOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class DifferenceMatteOperation : public NodeOperation {
|
||||
class DifferenceMatteOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
NodeChroma *m_settings;
|
||||
SocketReader *m_inputImage1Program;
|
||||
@@ -50,6 +50,10 @@ class DifferenceMatteOperation : public NodeOperation {
|
||||
{
|
||||
this->m_settings = nodeChroma;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation()
|
||||
this->flags.complex = true;
|
||||
|
||||
this->m_inputColorProgram = nullptr;
|
||||
this->m_inputVectorProgram = nullptr;
|
||||
this->m_inputScaleXProgram = nullptr;
|
||||
this->m_inputScaleYProgram = nullptr;
|
||||
}
|
||||
|
||||
void DisplaceOperation::initExecution()
|
||||
{
|
||||
this->m_inputColorProgram = this->getInputSocketReader(0);
|
||||
this->m_inputVectorProgram = this->getInputSocketReader(1);
|
||||
this->m_inputScaleXProgram = this->getInputSocketReader(2);
|
||||
this->m_inputScaleYProgram = this->getInputSocketReader(3);
|
||||
NodeOperation *vector = this->getInputSocketReader(1);
|
||||
NodeOperation *scale_x = this->getInputSocketReader(2);
|
||||
NodeOperation *scale_y = this->getInputSocketReader(3);
|
||||
if (execution_model_ == eExecutionModel::Tiled) {
|
||||
vector_read_fn_ = [=](float x, float y, float *out) {
|
||||
vector->readSampled(out, x, y, PixelSampler::Bilinear);
|
||||
};
|
||||
scale_x_read_fn_ = [=](float x, float y, float *out) {
|
||||
scale_x->readSampled(out, x, y, PixelSampler::Nearest);
|
||||
};
|
||||
scale_y_read_fn_ = [=](float x, float y, float *out) {
|
||||
scale_y->readSampled(out, x, y, PixelSampler::Nearest);
|
||||
};
|
||||
}
|
||||
|
||||
this->m_width_x4 = this->getWidth() * 4;
|
||||
this->m_height_x4 = this->getHeight() * 4;
|
||||
input_vector_width_ = vector->getWidth();
|
||||
input_vector_height_ = vector->getHeight();
|
||||
}
|
||||
|
||||
void DisplaceOperation::executePixelSampled(float output[4],
|
||||
@@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4],
|
||||
bool DisplaceOperation::read_displacement(
|
||||
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v)
|
||||
{
|
||||
float width = m_inputVectorProgram->getWidth();
|
||||
float height = m_inputVectorProgram->getHeight();
|
||||
float width = input_vector_width_;
|
||||
float height = input_vector_height_;
|
||||
if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
|
||||
r_u = 0.0f;
|
||||
r_v = 0.0f;
|
||||
@@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement(
|
||||
}
|
||||
|
||||
float col[4];
|
||||
m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
|
||||
vector_read_fn_(x, y, col);
|
||||
r_u = origin[0] - col[0] * xscale;
|
||||
r_v = origin[1] - col[1] * yscale;
|
||||
return true;
|
||||
@@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
|
||||
float uv[2]; /* temporary variables for derivative estimation */
|
||||
int num;
|
||||
|
||||
m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
|
||||
scale_x_read_fn_(xy[0], xy[1], col);
|
||||
float xs = col[0];
|
||||
m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
|
||||
scale_y_read_fn_(xy[0], xy[1], col);
|
||||
float ys = col[0];
|
||||
/* clamp x and y displacement to triple image resolution -
|
||||
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
|
||||
@@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
|
||||
void DisplaceOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputColorProgram = nullptr;
|
||||
this->m_inputVectorProgram = nullptr;
|
||||
this->m_inputScaleXProgram = nullptr;
|
||||
this->m_inputScaleYProgram = nullptr;
|
||||
vector_read_fn_ = nullptr;
|
||||
scale_x_read_fn_ = nullptr;
|
||||
scale_y_read_fn_ = nullptr;
|
||||
}
|
||||
|
||||
bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
@@ -195,4 +205,61 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplaceOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case 0: {
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.xmax = getInputOperation(input_idx)->getWidth();
|
||||
r_input_area.ymax = getInputOperation(input_idx)->getHeight();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
r_input_area = output_area;
|
||||
expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *vector = inputs[1];
|
||||
MemoryBuffer *scale_x = inputs[2];
|
||||
MemoryBuffer *scale_y = inputs[3];
|
||||
vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); };
|
||||
scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); };
|
||||
scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); };
|
||||
}
|
||||
|
||||
void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input_color = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
const float xy[2] = {(float)it.x, (float)it.y};
|
||||
float uv[2];
|
||||
float deriv[2][2];
|
||||
|
||||
pixelTransform(xy, uv, deriv);
|
||||
if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
|
||||
input_color->read_elem_bilinear(uv[0], uv[1], it.out);
|
||||
}
|
||||
else {
|
||||
/* EWA filtering (without nearest it gets blurry with NO distortion). */
|
||||
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,23 +18,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DisplaceOperation : public NodeOperation {
|
||||
class DisplaceOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
*/
|
||||
SocketReader *m_inputColorProgram;
|
||||
SocketReader *m_inputVectorProgram;
|
||||
SocketReader *m_inputScaleXProgram;
|
||||
SocketReader *m_inputScaleYProgram;
|
||||
|
||||
float m_width_x4;
|
||||
float m_height_x4;
|
||||
|
||||
int input_vector_width_;
|
||||
int input_vector_height_;
|
||||
|
||||
std::function<void(float x, float y, float *out)> vector_read_fn_;
|
||||
std::function<void(float x, float y, float *out)> scale_x_read_fn_;
|
||||
std::function<void(float x, float y, float *out)> scale_y_read_fn_;
|
||||
|
||||
public:
|
||||
DisplaceOperation();
|
||||
|
||||
@@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation {
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
|
||||
private:
|
||||
bool read_displacement(
|
||||
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
|
||||
|
@@ -132,4 +132,56 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case 0: {
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.xmax = getInputOperation(input_idx)->getWidth();
|
||||
r_input_area.ymax = getInputOperation(input_idx)->getHeight();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
r_input_area = output_area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const float width = this->getWidth();
|
||||
const float height = this->getHeight();
|
||||
const MemoryBuffer *input_color = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end();
|
||||
++it) {
|
||||
float scale_x = *it.in(1);
|
||||
float scale_y = *it.in(2);
|
||||
|
||||
/* Clamp x and y displacement to triple image resolution -
|
||||
* to prevent hangs from huge values mistakenly plugged in eg. z buffers. */
|
||||
CLAMP(scale_x, -m_width_x4, m_width_x4);
|
||||
CLAMP(scale_y, -m_height_x4, m_height_x4);
|
||||
|
||||
/* Main displacement in pixel space. */
|
||||
const float *vector = it.in(0);
|
||||
const float p_dx = vector[0] * scale_x;
|
||||
const float p_dy = vector[1] * scale_y;
|
||||
|
||||
/* Displaced pixel in uv coords, for image sampling. */
|
||||
/* Clamp nodes to avoid glitches. */
|
||||
float u = it.x - p_dx + 0.5f;
|
||||
float v = it.y - p_dy + 0.5f;
|
||||
CLAMP(u, 0.0f, width - 1.0f);
|
||||
CLAMP(v, 0.0f, height - 1.0f);
|
||||
|
||||
input_color->read_elem_checked(u, v, it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class DisplaceSimpleOperation : public NodeOperation {
|
||||
class DisplaceSimpleOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -29,6 +29,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation()
|
||||
|
||||
this->m_inputImageProgram = nullptr;
|
||||
this->m_inputKeyProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
|
||||
void DistanceRGBMatteOperation::initExecution()
|
||||
@@ -43,7 +44,7 @@ void DistanceRGBMatteOperation::deinitExecution()
|
||||
this->m_inputKeyProgram = nullptr;
|
||||
}
|
||||
|
||||
float DistanceRGBMatteOperation::calculateDistance(float key[4], float image[4])
|
||||
float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4])
|
||||
{
|
||||
return len_v3v3(key, image);
|
||||
}
|
||||
@@ -93,4 +94,43 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *in_image = it.in(0);
|
||||
const float *in_key = it.in(1);
|
||||
|
||||
float distance = this->calculateDistance(in_key, in_image);
|
||||
const float tolerance = this->m_settings->t1;
|
||||
const float falloff = this->m_settings->t2;
|
||||
|
||||
/* Store matte(alpha) value in [0] to go with
|
||||
* COM_SetAlphaMultiplyOperation and the Value output.
|
||||
*/
|
||||
|
||||
/* Make 100% transparent. */
|
||||
if (distance < tolerance) {
|
||||
it.out[0] = 0.0f;
|
||||
}
|
||||
/* In the falloff region, make partially transparent. */
|
||||
else if (distance < falloff + tolerance) {
|
||||
distance = distance - tolerance;
|
||||
const float alpha = distance / falloff;
|
||||
/* Only change if more transparent than before. */
|
||||
if (alpha < in_image[3]) {
|
||||
it.out[0] = alpha;
|
||||
}
|
||||
else { /* Leave as before. */
|
||||
it.out[0] = in_image[3];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Leave as before. */
|
||||
it.out[0] = in_image[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MixOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace blender::compositor {
|
||||
* this program converts an input color to an output value.
|
||||
* it assumes we are in sRGB color space.
|
||||
*/
|
||||
class DistanceRGBMatteOperation : public NodeOperation {
|
||||
class DistanceRGBMatteOperation : public MultiThreadedOperation {
|
||||
protected:
|
||||
NodeChroma *m_settings;
|
||||
SocketReader *m_inputImageProgram;
|
||||
SocketReader *m_inputKeyProgram;
|
||||
|
||||
virtual float calculateDistance(float key[4], float image[4]);
|
||||
virtual float calculateDistance(const float key[4], const float image[4]);
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -52,6 +52,10 @@ class DistanceRGBMatteOperation : public NodeOperation {
|
||||
{
|
||||
this->m_settings = nodeChroma;
|
||||
}
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -21,7 +21,7 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
|
||||
float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4])
|
||||
{
|
||||
/* only measure the second 2 values */
|
||||
return len_v2v2(key + 1, image + 1);
|
||||
|
@@ -29,7 +29,7 @@ namespace blender::compositor {
|
||||
*/
|
||||
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
|
||||
protected:
|
||||
float calculateDistance(float key[4], float image[4]) override;
|
||||
float calculateDistance(const float key[4], const float image[4]) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -62,6 +62,13 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest(
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::init_data()
|
||||
{
|
||||
BlurBaseOperation::init_data();
|
||||
this->m_sx = this->m_data.sizex * this->m_size / 2.0f;
|
||||
this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
@@ -117,6 +124,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
|
||||
unsigned int chan,
|
||||
unsigned int xy)
|
||||
{
|
||||
BLI_assert(!src->is_a_single_elem());
|
||||
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
|
||||
double *X, *Y, *W;
|
||||
const unsigned int src_width = src->getWidth();
|
||||
@@ -257,6 +265,64 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
|
||||
#undef YVV
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
switch (input_idx) {
|
||||
case IMAGE_INPUT_INDEX:
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.xmax = getWidth();
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.ymax = getHeight();
|
||||
break;
|
||||
default:
|
||||
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
/* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for
|
||||
* an output buffer. */
|
||||
const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
MemoryBuffer *image = nullptr;
|
||||
const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area);
|
||||
if (is_full_output) {
|
||||
image = output;
|
||||
}
|
||||
else {
|
||||
image = new MemoryBuffer(getOutputSocket()->getDataType(), area);
|
||||
}
|
||||
image->copy_from(input, area);
|
||||
|
||||
if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sx, c, 3);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this->m_sx > 0.0f) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sx, c, 1);
|
||||
}
|
||||
}
|
||||
if (this->m_sy > 0.0f) {
|
||||
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
|
||||
IIR_gauss(image, this->m_sy, c, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_full_output) {
|
||||
output->copy_from(image, area);
|
||||
delete image;
|
||||
}
|
||||
}
|
||||
|
||||
FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
|
||||
{
|
||||
this->addInputSocket(DataType::Value);
|
||||
@@ -341,4 +407,44 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
|
||||
return this->m_iirgaus;
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx),
|
||||
const rcti &UNUSED(output_area),
|
||||
rcti &r_input_area)
|
||||
{
|
||||
r_input_area.xmin = 0;
|
||||
r_input_area.xmax = getWidth();
|
||||
r_input_area.ymin = 0;
|
||||
r_input_area.ymax = getHeight();
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
if (m_iirgaus == nullptr) {
|
||||
const MemoryBuffer *image = inputs[0];
|
||||
MemoryBuffer *gauss = new MemoryBuffer(*image);
|
||||
FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3);
|
||||
m_iirgaus = gauss;
|
||||
}
|
||||
}
|
||||
|
||||
void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *image = inputs[0];
|
||||
BuffersIterator<float> it = output->iterate_with({image, m_iirgaus}, area);
|
||||
if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) {
|
||||
for (; !it.is_end(); ++it) {
|
||||
*it.out = MIN2(*it.in(0), *it.in(1));
|
||||
}
|
||||
}
|
||||
else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) {
|
||||
for (; !it.is_end(); ++it) {
|
||||
*it.out = MAX2(*it.in(0), *it.in(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -38,8 +38,19 @@ class FastGaussianBlurOperation : public BlurBaseOperation {
|
||||
|
||||
static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy);
|
||||
void *initializeTileData(rcti *rect) override;
|
||||
void init_data() override;
|
||||
void deinitExecution() override;
|
||||
void initExecution() override;
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
|
||||
const rcti &UNUSED(area),
|
||||
Span<MemoryBuffer *> UNUSED(inputs)) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -48,7 +59,7 @@ enum {
|
||||
FAST_GAUSS_OVERLAY_MAX = 1,
|
||||
};
|
||||
|
||||
class FastGaussianBlurValueOperation : public NodeOperation {
|
||||
class FastGaussianBlurValueOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
float m_sigma;
|
||||
MemoryBuffer *m_iirgaus;
|
||||
@@ -80,6 +91,14 @@ class FastGaussianBlurValueOperation : public NodeOperation {
|
||||
{
|
||||
this->m_overlay = overlay;
|
||||
}
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_started(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
|
||||
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
|
||||
}
|
||||
|
||||
void FlipOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
BLI_assert(input_idx == 0);
|
||||
UNUSED_VARS_NDEBUG(input_idx);
|
||||
if (this->m_flipX) {
|
||||
const int w = (int)this->getWidth() - 1;
|
||||
r_input_area.xmax = (w - output_area.xmin) + 1;
|
||||
r_input_area.xmin = (w - output_area.xmax) - 1;
|
||||
}
|
||||
else {
|
||||
r_input_area.xmin = output_area.xmin;
|
||||
r_input_area.xmax = output_area.xmax;
|
||||
}
|
||||
if (this->m_flipY) {
|
||||
const int h = (int)this->getHeight() - 1;
|
||||
r_input_area.ymax = (h - output_area.ymin) + 1;
|
||||
r_input_area.ymin = (h - output_area.ymax) - 1;
|
||||
}
|
||||
else {
|
||||
r_input_area.ymin = output_area.ymin;
|
||||
r_input_area.ymax = output_area.ymax;
|
||||
}
|
||||
}
|
||||
|
||||
void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input_img = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
|
||||
const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
|
||||
input_img->read_elem(nx, ny, it.out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class FlipOperation : public NodeOperation {
|
||||
class FlipOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
SocketReader *m_inputOperation;
|
||||
bool m_flipX;
|
||||
@@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation {
|
||||
{
|
||||
this->m_flipY = flipY;
|
||||
}
|
||||
|
||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -26,6 +26,7 @@ GammaCorrectOperation::GammaCorrectOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
void GammaCorrectOperation::initExecution()
|
||||
{
|
||||
@@ -58,6 +59,34 @@ void GammaCorrectOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
float color[4];
|
||||
input->read_elem(it.x, it.y, color);
|
||||
if (color[3] > 0.0f) {
|
||||
color[0] /= color[3];
|
||||
color[1] /= color[3];
|
||||
color[2] /= color[3];
|
||||
}
|
||||
|
||||
/* Check for negative to avoid nan's. */
|
||||
it.out[0] = color[0] > 0.0f ? color[0] * color[0] : 0.0f;
|
||||
it.out[1] = color[1] > 0.0f ? color[1] * color[1] : 0.0f;
|
||||
it.out[2] = color[2] > 0.0f ? color[2] * color[2] : 0.0f;
|
||||
it.out[3] = color[3];
|
||||
|
||||
if (color[3] > 0.0f) {
|
||||
it.out[0] *= color[3];
|
||||
it.out[1] *= color[3];
|
||||
it.out[2] *= color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GammaCorrectOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputProgram = nullptr;
|
||||
@@ -68,6 +97,7 @@ GammaUncorrectOperation::GammaUncorrectOperation()
|
||||
this->addInputSocket(DataType::Color);
|
||||
this->addOutputSocket(DataType::Color);
|
||||
this->m_inputProgram = nullptr;
|
||||
flags.can_be_constant = true;
|
||||
}
|
||||
void GammaUncorrectOperation::initExecution()
|
||||
{
|
||||
@@ -100,6 +130,33 @@ void GammaUncorrectOperation::executePixelSampled(float output[4],
|
||||
}
|
||||
}
|
||||
|
||||
void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const MemoryBuffer *input = inputs[0];
|
||||
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
|
||||
float color[4];
|
||||
input->read_elem(it.x, it.y, color);
|
||||
if (color[3] > 0.0f) {
|
||||
color[0] /= color[3];
|
||||
color[1] /= color[3];
|
||||
color[2] /= color[3];
|
||||
}
|
||||
|
||||
it.out[0] = color[0] > 0.0f ? sqrtf(color[0]) : 0.0f;
|
||||
it.out[1] = color[1] > 0.0f ? sqrtf(color[1]) : 0.0f;
|
||||
it.out[2] = color[2] > 0.0f ? sqrtf(color[2]) : 0.0f;
|
||||
it.out[3] = color[3];
|
||||
|
||||
if (color[3] > 0.0f) {
|
||||
it.out[0] *= color[3];
|
||||
it.out[1] *= color[3];
|
||||
it.out[2] *= color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GammaUncorrectOperation::deinitExecution()
|
||||
{
|
||||
this->m_inputProgram = nullptr;
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GammaCorrectOperation : public NodeOperation {
|
||||
class GammaCorrectOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -46,9 +46,13 @@ class GammaCorrectOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
class GammaUncorrectOperation : public NodeOperation {
|
||||
class GammaUncorrectOperation : public MultiThreadedOperation {
|
||||
private:
|
||||
/**
|
||||
* Cached reference to the inputProgram
|
||||
@@ -72,6 +76,10 @@ class GammaUncorrectOperation : public NodeOperation {
|
||||
* Deinitialize the execution
|
||||
*/
|
||||
void deinitExecution() override;
|
||||
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "COM_GaussianAlphaBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim)
|
||||
: BlurBaseOperation(DataType::Value)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
this->m_filtersize = 0;
|
||||
this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */
|
||||
dimension_ = dim;
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::init_data()
|
||||
{
|
||||
BlurBaseOperation::init_data();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f);
|
||||
rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS);
|
||||
m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::initExecution()
|
||||
{
|
||||
BlurBaseOperation::initExecution();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
|
||||
m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff);
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
|
||||
if (this->m_gausstab) {
|
||||
MEM_freeN(this->m_gausstab);
|
||||
this->m_gausstab = nullptr;
|
||||
}
|
||||
|
||||
if (this->m_distbuf_inv) {
|
||||
MEM_freeN(this->m_distbuf_inv);
|
||||
this->m_distbuf_inv = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area)
|
||||
{
|
||||
if (input_idx != IMAGE_INPUT_INDEX) {
|
||||
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
|
||||
return;
|
||||
}
|
||||
|
||||
r_input_area = output_area;
|
||||
switch (dimension_) {
|
||||
case eDimension::X:
|
||||
r_input_area.xmin = output_area.xmin - m_filtersize - 1;
|
||||
r_input_area.xmax = output_area.xmax + m_filtersize + 1;
|
||||
break;
|
||||
case eDimension::Y:
|
||||
r_input_area.ymin = output_area.ymin - m_filtersize - 1;
|
||||
r_input_area.ymax = output_area.ymax + m_filtersize + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE float finv_test(const float f, const bool test)
|
||||
{
|
||||
return (LIKELY(test == false)) ? f : 1.0f - f;
|
||||
}
|
||||
|
||||
void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
|
||||
const rcti &input_rect = input->get_rect();
|
||||
BuffersIterator<float> it = output->iterate_with({input}, area);
|
||||
|
||||
int min_input_coord = -1;
|
||||
int max_input_coord = -1;
|
||||
int elem_stride = -1;
|
||||
std::function<int()> get_current_coord;
|
||||
switch (dimension_) {
|
||||
case eDimension::X:
|
||||
min_input_coord = input_rect.xmin;
|
||||
max_input_coord = input_rect.xmax;
|
||||
get_current_coord = [&] { return it.x; };
|
||||
elem_stride = input->elem_stride;
|
||||
break;
|
||||
case eDimension::Y:
|
||||
min_input_coord = input_rect.ymin;
|
||||
max_input_coord = input_rect.ymax;
|
||||
get_current_coord = [&] { return it.y; };
|
||||
elem_stride = input->row_stride;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; !it.is_end(); ++it) {
|
||||
const int coord = get_current_coord();
|
||||
const int coord_min = max_ii(coord - m_filtersize, min_input_coord);
|
||||
const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord);
|
||||
|
||||
/* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */
|
||||
/* Gauss. */
|
||||
float alpha_accum = 0.0f;
|
||||
float multiplier_accum = 0.0f;
|
||||
|
||||
/* Dilate. */
|
||||
const bool do_invert = m_do_subtract;
|
||||
/* Init with the current color to avoid unneeded lookups. */
|
||||
float value_max = finv_test(*it.in(0), do_invert);
|
||||
float distfacinv_max = 1.0f; /* 0 to 1 */
|
||||
|
||||
const int step = QualityStepHelper::getStep();
|
||||
const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride;
|
||||
const int in_stride = elem_stride * step;
|
||||
int index = (coord_min - coord) + m_filtersize;
|
||||
const int index_end = index + (coord_max - coord_min);
|
||||
for (; index < index_end; in += in_stride, index += step) {
|
||||
float value = finv_test(*in, do_invert);
|
||||
|
||||
/* Gauss. */
|
||||
float multiplier = m_gausstab[index];
|
||||
alpha_accum += value * multiplier;
|
||||
multiplier_accum += multiplier;
|
||||
|
||||
/* Dilate - find most extreme color. */
|
||||
if (value > value_max) {
|
||||
multiplier = m_distbuf_inv[index];
|
||||
value *= multiplier;
|
||||
if (value > value_max) {
|
||||
value_max = value;
|
||||
distfacinv_max = multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Blend between the max value and gauss blue - gives nice feather. */
|
||||
const float value_blur = alpha_accum / multiplier_accum;
|
||||
const float value_final = (value_max * distfacinv_max) +
|
||||
(value_blur * (1.0f - distfacinv_max));
|
||||
*it.out = finv_test(value_final, do_invert);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_BlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GaussianAlphaBlurBaseOperation : public BlurBaseOperation {
|
||||
protected:
|
||||
float *m_gausstab;
|
||||
float *m_distbuf_inv;
|
||||
int m_falloff; /* Falloff for #distbuf_inv. */
|
||||
bool m_do_subtract;
|
||||
int m_filtersize;
|
||||
float rad_;
|
||||
eDimension dimension_;
|
||||
|
||||
public:
|
||||
GaussianAlphaBlurBaseOperation(eDimension dim);
|
||||
|
||||
virtual void init_data() override;
|
||||
virtual void initExecution() override;
|
||||
virtual void deinitExecution() override;
|
||||
|
||||
void get_area_of_interest(const int input_idx,
|
||||
const rcti &output_area,
|
||||
rcti &r_input_area) final;
|
||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) final;
|
||||
|
||||
/**
|
||||
* Set subtract for Dilate/Erode functionality
|
||||
*/
|
||||
void setSubtract(bool subtract)
|
||||
{
|
||||
this->m_do_subtract = subtract;
|
||||
}
|
||||
void setFalloff(int falloff)
|
||||
{
|
||||
this->m_falloff = falloff;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
@@ -24,11 +24,9 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value)
|
||||
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation()
|
||||
: GaussianAlphaBlurBaseOperation(eDimension::X)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
this->m_filtersize = 0;
|
||||
this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
|
||||
}
|
||||
|
||||
void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
@@ -44,12 +42,11 @@ void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
|
||||
void GaussianAlphaXBlurOperation::initExecution()
|
||||
{
|
||||
/* Until we support size input - comment this. */
|
||||
// BlurBaseOperation::initExecution();
|
||||
GaussianAlphaBlurBaseOperation::initExecution();
|
||||
|
||||
initMutex();
|
||||
|
||||
if (this->m_sizeavailable) {
|
||||
if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
|
||||
float rad = max_ff(m_size * m_data.sizex, 0.0f);
|
||||
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
|
||||
|
||||
@@ -144,7 +141,7 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo
|
||||
|
||||
void GaussianAlphaXBlurOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
GaussianAlphaBlurBaseOperation::deinitExecution();
|
||||
|
||||
if (this->m_gausstab) {
|
||||
MEM_freeN(this->m_gausstab);
|
||||
|
@@ -18,18 +18,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_BlurBaseOperation.h"
|
||||
#include "COM_NodeOperation.h"
|
||||
#include "COM_GaussianAlphaBlurBaseOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class GaussianAlphaXBlurOperation : public BlurBaseOperation {
|
||||
/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
|
||||
class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation {
|
||||
private:
|
||||
float *m_gausstab;
|
||||
float *m_distbuf_inv;
|
||||
int m_falloff; /* falloff for distbuf_inv */
|
||||
bool m_do_subtract;
|
||||
int m_filtersize;
|
||||
void updateGauss();
|
||||
|
||||
public:
|
||||
@@ -54,18 +49,6 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation {
|
||||
bool determineDependingAreaOfInterest(rcti *input,
|
||||
ReadBufferOperation *readOperation,
|
||||
rcti *output) override;
|
||||
|
||||
/**
|
||||
* Set subtract for Dilate/Erode functionality
|
||||
*/
|
||||
void setSubtract(bool subtract)
|
||||
{
|
||||
this->m_do_subtract = subtract;
|
||||
}
|
||||
void setFalloff(int falloff)
|
||||
{
|
||||
this->m_falloff = falloff;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@@ -24,11 +24,9 @@
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value)
|
||||
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation()
|
||||
: GaussianAlphaBlurBaseOperation(eDimension::Y)
|
||||
{
|
||||
this->m_gausstab = nullptr;
|
||||
this->m_filtersize = 0;
|
||||
this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
|
||||
}
|
||||
|
||||
void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
@@ -42,14 +40,14 @@ void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void GaussianAlphaYBlurOperation::initExecution()
|
||||
{
|
||||
/* Until we support size input - comment this. */
|
||||
// BlurBaseOperation::initExecution();
|
||||
GaussianAlphaBlurBaseOperation::initExecution();
|
||||
|
||||
initMutex();
|
||||
|
||||
if (this->m_sizeavailable) {
|
||||
if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
|
||||
float rad = max_ff(m_size * m_data.sizey, 0.0f);
|
||||
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
|
||||
|
||||
@@ -58,6 +56,7 @@ void GaussianAlphaYBlurOperation::initExecution()
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void GaussianAlphaYBlurOperation::updateGauss()
|
||||
{
|
||||
if (this->m_gausstab == nullptr) {
|
||||
@@ -143,7 +142,7 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo
|
||||
|
||||
void GaussianAlphaYBlurOperation::deinitExecution()
|
||||
{
|
||||
BlurBaseOperation::deinitExecution();
|
||||
GaussianAlphaBlurBaseOperation::deinitExecution();
|
||||
|
||||
if (this->m_gausstab) {
|
||||
MEM_freeN(this->m_gausstab);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user